sync: agent-ops from agentic-framework v1.1.200
This commit is contained in:
parent
89e5ac6d70
commit
0fefdb5e46
4 changed files with 52 additions and 6 deletions
|
|
@ -1 +1 @@
|
||||||
1.1.199
|
1.1.200
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,9 @@ Accept self-check completion only when `## Implementation Checklist` or its supp
|
||||||
- Record the target id, opaque agent/model identity, execution class, runtime contract, catalog evidence, process identity, workspace identity, timestamps, result, and exact failure evidence.
|
- Record the target id, opaque agent/model identity, execution class, runtime contract, catalog evidence, process identity, workspace identity, timestamps, result, and exact failure evidence.
|
||||||
- Treat stderr as terminal diagnostic evidence. For JSONL, recognize generic terminal event fields such as error/fatal type or severity, rejected/failed status with an error code, explicit error flags, and a non-retrying `agent_end` whose last assistant message ends with `error` or `aborted`.
|
- Treat stderr as terminal diagnostic evidence. For JSONL, recognize generic terminal event fields such as error/fatal type or severity, rejected/failed status with an error code, explicit error flags, and a non-retrying `agent_end` whose last assistant message ends with `error` or `aborted`.
|
||||||
- Determine liveness from PID/start-token/process-marker evidence and actual stream or native-session progress. Heartbeat mtime is never agent progress. For Codex JSONL, an unmatched `item.started` `command_execution` is an active tool interval: suspend the model-response silence timer until its matching `item.completed`, then restore normal stall detection.
|
- Determine liveness from PID/start-token/process-marker evidence and actual stream or native-session progress. Heartbeat mtime is never agent progress. For Codex JSONL, an unmatched `item.started` `command_execution` is an active tool interval: suspend the model-response silence timer until its matching `item.completed`, then restore normal stall detection.
|
||||||
|
- The dispatcher model-silence safety net is 70 seconds. Downstream provider runtimes should emit their bounded terminal before that deadline; do not extend the dispatcher budget per target to cover nested retries.
|
||||||
|
- Treat a confirmed provider transport terminal as the end of the current dispatch. Do not resume or automatically resend the same native session; an operator may start a fresh dispatch after the provider/runtime state is corrected.
|
||||||
|
- Retry `session-stall` only with a fresh native conversation. Preserve workspace changes and logical locator evidence, but do not carry the silent conversation context into the next attempt or a restarted dispatcher.
|
||||||
- Never start a duplicate attempt while owned live evidence remains.
|
- Never start a duplicate attempt while owned live evidence remains.
|
||||||
- Keep a 10-consecutive-failure budget per task stage. Reset only that stage's budget after success.
|
- Keep a 10-consecutive-failure budget per task stage. Reset only that stage's budget after success.
|
||||||
- Preserve failed attempt logs. Delete successful attempt logs only after verified archive completion and no live evidence.
|
- Preserve failed attempt logs. Delete successful attempt logs only after verified archive completion and no live evidence.
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ def validated_max_parallel(value: int) -> int:
|
||||||
|
|
||||||
|
|
||||||
STREAM_HEARTBEAT_SECONDS = 30
|
STREAM_HEARTBEAT_SECONDS = 30
|
||||||
MODEL_RESPONSE_STALL_SECONDS = 3 * 60
|
MODEL_RESPONSE_STALL_SECONDS = 70
|
||||||
RECOVERY_FAILURE_LIMIT = 10
|
RECOVERY_FAILURE_LIMIT = 10
|
||||||
GENERIC_FAILURE_LIMIT_PER_TARGET = 3
|
GENERIC_FAILURE_LIMIT_PER_TARGET = 3
|
||||||
SELF_CHECK_UNCHECKED_RETRY_LIMIT = 10
|
SELF_CHECK_UNCHECKED_RETRY_LIMIT = 10
|
||||||
|
|
@ -209,6 +209,7 @@ RUNTIME_FAILURE_PATTERNS = {
|
||||||
],
|
],
|
||||||
"provider-connection": [
|
"provider-connection": [
|
||||||
r"\bprovider[_ -]?tunnel[_ -]?error\b",
|
r"\bprovider[_ -]?tunnel[_ -]?error\b",
|
||||||
|
r"no provider supports the required output validation capability",
|
||||||
(
|
(
|
||||||
r"(?:provider|backend|inference (?:server|endpoint))"
|
r"(?:provider|backend|inference (?:server|endpoint))"
|
||||||
r".{0,160}(?:connection refused|dial tcp)"
|
r".{0,160}(?:connection refused|dial tcp)"
|
||||||
|
|
@ -3252,7 +3253,7 @@ def native_resume_locator(
|
||||||
if (
|
if (
|
||||||
not isinstance(record.get("runtime"), dict)
|
not isinstance(record.get("runtime"), dict)
|
||||||
or not record["runtime"].get("native_session_monitor")
|
or not record["runtime"].get("native_session_monitor")
|
||||||
or record.get("failure_class") not in {"context-limit", "session-stall"}
|
or record.get("failure_class") != "context-limit"
|
||||||
or record.get("status") != "failed"
|
or record.get("status") != "failed"
|
||||||
):
|
):
|
||||||
return None
|
return None
|
||||||
|
|
@ -3819,7 +3820,7 @@ async def invoke(
|
||||||
record["native_pending_tool_call_ids"] = list(
|
record["native_pending_tool_call_ids"] = list(
|
||||||
native_state.pending_tool_call_ids
|
native_state.pending_tool_call_ids
|
||||||
)
|
)
|
||||||
record["native_stall_timeout_seconds"] = None
|
record["native_stall_timeout_seconds"] = MODEL_RESPONSE_STALL_SECONDS
|
||||||
record.setdefault("native_activity_state", "starting")
|
record.setdefault("native_activity_state", "starting")
|
||||||
if (
|
if (
|
||||||
spec.native_resume
|
spec.native_resume
|
||||||
|
|
@ -4645,6 +4646,37 @@ async def run_escalating(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
return False, locator
|
return False, locator
|
||||||
|
if failure in PROVIDER_TRANSPORT_FAILURES:
|
||||||
|
reason = (
|
||||||
|
f"{role} provider transport failure requires a fresh dispatch"
|
||||||
|
)
|
||||||
|
selected = (
|
||||||
|
current_decision.get("selected")
|
||||||
|
if isinstance(current_decision, dict)
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
store.update_task(
|
||||||
|
task,
|
||||||
|
blocked=f"{reason} locator={locator}",
|
||||||
|
blocker_evidence={
|
||||||
|
"role": role,
|
||||||
|
"failure_class": failure,
|
||||||
|
"locator": str(locator) if locator else None,
|
||||||
|
"selected": selected,
|
||||||
|
"work_unit_id": current_decision.get("work_unit_id")
|
||||||
|
if isinstance(current_decision, dict)
|
||||||
|
else None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
banner(
|
||||||
|
"작업차단",
|
||||||
|
task.name,
|
||||||
|
[
|
||||||
|
"reason=provider-transport-terminal",
|
||||||
|
*failure_report_lines(failure, locator),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return False, locator
|
||||||
if spec.native_resume:
|
if spec.native_resume:
|
||||||
if failure in {"context-limit", "session-stall"}:
|
if failure in {"context-limit", "session-stall"}:
|
||||||
native_recovery_retries += 1
|
native_recovery_retries += 1
|
||||||
|
|
@ -4658,7 +4690,7 @@ async def run_escalating(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
previous_locator = locator
|
previous_locator = locator
|
||||||
native_resume_locator = locator
|
native_resume_locator = locator if failure == "context-limit" else None
|
||||||
await asyncio.sleep(min(30, 2 ** min(native_recovery_retries, 5)))
|
await asyncio.sleep(min(30, 2 ** min(native_recovery_retries, 5)))
|
||||||
continue
|
continue
|
||||||
native_recovery_retries += 1
|
native_recovery_retries += 1
|
||||||
|
|
@ -5309,7 +5341,11 @@ async def run_worker(
|
||||||
resume_locator: Path | None = None,
|
resume_locator: Path | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
retry_context = store.task_state(task).get("retry_failover_context")
|
retry_context = store.task_state(task).get("retry_failover_context")
|
||||||
if resume_locator is None and isinstance(retry_context, dict):
|
if (
|
||||||
|
resume_locator is None
|
||||||
|
and isinstance(retry_context, dict)
|
||||||
|
and retry_context.get("failure_class") not in PROVIDER_TRANSPORT_FAILURES
|
||||||
|
):
|
||||||
locator_value = retry_context.get("locator")
|
locator_value = retry_context.get("locator")
|
||||||
if isinstance(locator_value, str) and locator_value:
|
if isinstance(locator_value, str) and locator_value:
|
||||||
resume_locator = Path(locator_value)
|
resume_locator = Path(locator_value)
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,13 @@ class RuntimeCatalogDispatcherTests(unittest.TestCase):
|
||||||
self.assertEqual(failure, "provider-quota")
|
self.assertEqual(failure, "provider-quota")
|
||||||
self.assertIsNotNone(evidence)
|
self.assertIsNotNone(evidence)
|
||||||
|
|
||||||
|
def test_output_validation_capability_rejection_is_provider_terminal(self):
|
||||||
|
failure, evidence = dispatch.classify_failure_with_evidence(
|
||||||
|
"no provider supports the required output validation capability"
|
||||||
|
)
|
||||||
|
self.assertEqual(failure, "provider-connection")
|
||||||
|
self.assertIsNotNone(evidence)
|
||||||
|
|
||||||
def test_generic_json_terminal_diagnostic_has_no_agent_branch(self):
|
def test_generic_json_terminal_diagnostic_has_no_agent_branch(self):
|
||||||
diagnostic = dispatch.terminal_diagnostic(
|
diagnostic = dispatch.terminal_diagnostic(
|
||||||
"opaque-agent",
|
"opaque-agent",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue