From 2e0dd61b4ef8744f621a621678cbc9f2e0b36bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A1=82=E9=A9=AC?= Date: Wed, 2 Sep 2026 14:14:24 +0800 Subject: [PATCH] fix(agui): keep interrupts resumable without expiry --- .../run_agui_generation_fence.py | 2 - src/iac_code/agui/adapter.py | 100 +----------------- src/iac_code/agui/app.py | 2 - src/iac_code/agui/errors.py | 1 - src/iac_code/agui/events.py | 6 +- src/iac_code/agui/server.py | 2 - src/iac_code/cli/main.py | 11 -- .../i18n/locales/de/LC_MESSAGES/messages.po | 29 +++-- .../i18n/locales/es/LC_MESSAGES/messages.po | 29 +++-- .../i18n/locales/fr/LC_MESSAGES/messages.po | 31 +++--- .../i18n/locales/ja/LC_MESSAGES/messages.po | 29 +++-- .../i18n/locales/pt/LC_MESSAGES/messages.po | 29 +++-- .../i18n/locales/zh/LC_MESSAGES/messages.po | 29 +++-- tests/agui/test_events.py | 3 - tests/agui/test_http_sse_integration.py | 4 +- tests/agui/test_i18n.py | 1 - tests/agui/test_persistence.py | 46 ++++---- tests/cli/test_a2a_command.py | 2 - website/docs/agui/getting-started.md | 3 - website/docs/agui/protocol-reference.md | 6 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 6 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 8 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 6 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 8 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 6 +- .../current/agui/getting-started.md | 3 - .../current/agui/protocol-reference.md | 6 +- 32 files changed, 127 insertions(+), 296 deletions(-) diff --git a/scripts/a2a/e2e/permission_wait/run_agui_generation_fence.py b/scripts/a2a/e2e/permission_wait/run_agui_generation_fence.py index 41047a5f..58759569 100644 --- a/scripts/a2a/e2e/permission_wait/run_agui_generation_fence.py +++ b/scripts/a2a/e2e/permission_wait/run_agui_generation_fence.py @@ -176,8 +176,6 @@ def start(self, *, generation: int, a2a_url: str) -> None: a2a_url, "--state-dir", str(self.state_dir), - "--interrupt-ttl", - "120", ], cwd=self.repo_root, env=env, diff --git a/src/iac_code/agui/adapter.py b/src/iac_code/agui/adapter.py index d7ade19d..eb0684d1 100644 --- a/src/iac_code/agui/adapter.py +++ b/src/iac_code/agui/adapter.py @@ -11,7 +11,6 @@ import uuid from collections.abc import AsyncIterator, Mapping from dataclasses import dataclass, field -from datetime import datetime, timezone from pathlib import Path from typing import Any @@ -90,7 +89,6 @@ class ThreadBinding: applied_resume_digests: dict[tuple[str, str], str] = field(default_factory=dict) terminal_execution_ids: set[str] = field(default_factory=set) active_run_id: str | None = None - expiry_task: asyncio.Task[None] | None = None @dataclass @@ -133,7 +131,6 @@ def __init__( *, a2a_url: str, client: Any | None = None, - interrupt_ttl: int = 540, state_store: AguiStateStore | None = None, state_dir: str | Path | None = None, ) -> None: @@ -141,7 +138,6 @@ def __init__( raise ValueError("state_store and state_dir are mutually exclusive") self.a2a_url = a2a_url self.client = client or A2AClient() - self.interrupt_ttl = max(1, interrupt_ttl) self._state_store = state_store or FileAguiThreadStateStore(state_dir) self._lock = asyncio.Lock() self._threads: dict[str, ThreadBinding] = {} @@ -574,10 +570,6 @@ async def _apply_resume( if binding.task_id is None or not binding.pending: raise AguiError("EXECUTION_LOST", "The A2A task to resume is unavailable.") resolutions = self._validate_resume(ticket) - if binding.expiry_task is not None: - binding.expiry_task.cancel() - binding.expiry_task = None - prompt: str | None = None resolved_tools: list[tuple[str, str]] = [] permission_responses: list[tuple[PendingInput, dict[str, Any], str]] = [] @@ -615,8 +607,6 @@ async def _apply_resume( resolved_tools.append((tool_use_id, prompt)) prompt_responses.append((pending, digest)) except BaseException: - if binding.pending: - self._schedule_expiry(binding) raise acceptance = ResumeAcceptance() @@ -679,16 +669,12 @@ async def _stream_prompt_response( accepted = True yield event except BaseException: - if binding.pending: - self._schedule_expiry(binding) raise finally: close_stream = getattr(stream, "aclose", None) if close_stream is not None: await close_stream() if not accepted: - if binding.pending: - self._schedule_expiry(binding) raise AguiError("A2A_UNAVAILABLE", "The A2A interrupt response was not accepted.") async def _commit_accepted_inputs( @@ -761,8 +747,6 @@ async def _send_pipeline_permission_responses( _raise_for_a2a_error(response) await self._commit_accepted_inputs(binding, [(pending, digest)]) except BaseException: - if binding.pending: - self._schedule_expiry(binding) raise async def _stream_permission_responses( @@ -814,8 +798,6 @@ async def _stream_permission_responses( if close_stream is not None: await close_stream() except BaseException: - if binding.pending: - self._schedule_expiry(binding) raise async def _stream_after_sideband(self, binding: ThreadBinding) -> AsyncIterator[dict[str, Any]]: @@ -955,9 +937,6 @@ async def _prepare_resume( # A successful A2A SendMessage is authoritative for adapter # idempotency even if GetTask briefly returns a stale permission. binding.pending.pop(interrupt_id, None) - if binding.pending and self._pending_expired(binding): - await self._expire(binding, binding.execution_id) - raise AguiError("EXECUTION_EXPIRED", "The interrupted execution has expired.") try: self._persist_thread(binding) except AguiStateStoreError as exc: @@ -1034,7 +1013,6 @@ async def _commit_interrupt(self, ticket: RunTicket, mapper: A2AEventMapper) -> "STATE_PERSISTENCE_FAILED", "The interrupted execution state could not be committed.", ) from exc - self._schedule_expiry(binding) # This durable/paused marker must precede yielding any terminal event. The # ASGI response may be closed immediately after the client reads it. ticket.paused = True @@ -1074,7 +1052,7 @@ def _merge_pending( continue pending = PendingInput( value=_persistent_input(value), - interrupt=interrupt_from_a2a(_persistent_input(value), ttl_seconds=self.interrupt_ttl), + interrupt=interrupt_from_a2a(_persistent_input(value)), sideband=input_id in sideband_ids, ) merged[pending.interrupt.id] = pending @@ -1136,18 +1114,11 @@ async def cancel(self, execution_id: str, *, thread_id: str, ros_invocation_id: if binding.task_id: await self.client.cancel_task(self.a2a_url, binding.task_id) binding.pending.clear() - if binding.expiry_task is not None: - binding.expiry_task.cancel() - binding.expiry_task = None self._mark_execution_terminal(binding) self._persist_thread(binding) return "cancelled" async def aclose(self) -> None: - for binding in self._threads.values(): - if binding.expiry_task is not None: - binding.expiry_task.cancel() - binding.expiry_task = None close = getattr(self.client, "aclose", None) if close is not None: await close() @@ -1177,37 +1148,6 @@ def _mark_execution_terminal(self, binding: ThreadBinding) -> None: binding.terminal_execution_ids.add(binding.execution_id) binding.task_id = None - def _schedule_expiry(self, binding: ThreadBinding) -> None: - if not binding.pending: - return - if binding.expiry_task is not None: - binding.expiry_task.cancel() - binding.expiry_task = asyncio.create_task( - self._expire(binding, binding.execution_id), - name=f"agui-a2a-expiry-{binding.execution_id}", - ) - - async def _expire(self, binding: ThreadBinding, execution_id: str) -> None: - try: - expires_at = _pending_expires_at(binding) - if expires_at is None: - return - delay = max(0.0, (expires_at - datetime.now(timezone.utc)).total_seconds()) - await asyncio.sleep(delay) - if binding.execution_id != execution_id or not binding.pending: - return - if binding.task_id: - with contextlib.suppress(Exception): - await self.client.cancel_task(self.a2a_url, binding.task_id) - binding.pending.clear() - self._mark_execution_terminal(binding) - self._persist_thread_best_effort(binding) - except asyncio.CancelledError: - return - finally: - if binding.expiry_task is asyncio.current_task(): - binding.expiry_task = None - async def _cancel_unrecoverable(self, ticket: RunTicket) -> None: binding = ticket.binding if binding.pending: @@ -1223,16 +1163,9 @@ async def _cancel_for_state_failure(self, binding: ThreadBinding) -> None: with contextlib.suppress(Exception): await self.client.cancel_task(self.a2a_url, binding.task_id) binding.pending.clear() - if binding.expiry_task is not None: - binding.expiry_task.cancel() - binding.expiry_task = None self._mark_execution_terminal(binding) self._persist_thread_best_effort(binding) - def _pending_expired(self, binding: ThreadBinding) -> bool: - expires_at = _pending_expires_at(binding) - return expires_at is not None and expires_at <= datetime.now(timezone.utc) - def _persist_thread(self, binding: ThreadBinding) -> None: self._state_store.save_thread(binding.thread_id, self._thread_state_document(binding)) @@ -1246,7 +1179,6 @@ def _thread_state_document(self, binding: ThreadBinding) -> dict[str, Any]: pending = { input_id: { "value": _persistent_input(item.value), - "expiresAt": item.interrupt.expires_at, "sideband": item.sideband, } for input_id, item in binding.pending.items() @@ -1286,8 +1218,6 @@ def _load_thread(self, thread_id: str) -> ThreadBinding | None: self._threads[thread_id] = binding if binding.execution_id not in binding.terminal_execution_ids and binding.task_id: self._executions[binding.execution_id] = binding - if binding.pending: - self._schedule_expiry(binding) return binding def _restore_thread_state( @@ -1344,21 +1274,13 @@ def _restore_thread_state( if not isinstance(input_id, str) or not isinstance(raw_pending, Mapping): raise ValueError("pending entry is invalid") value = raw_pending.get("value") - expires_at = raw_pending.get("expiresAt") sideband = raw_pending.get("sideband", False) - if ( - not isinstance(value, Mapping) - or not isinstance(expires_at, str) - or not isinstance(sideband, bool) - ): + if not isinstance(value, Mapping) or not isinstance(sideband, bool): raise ValueError("pending entry is invalid") safe_value = _persistent_input(value) if safe_value.get("inputId") != input_id: raise ValueError("pending input identity mismatch") - _parse_expires_at(expires_at) - interrupt = interrupt_from_a2a(safe_value, ttl_seconds=1).model_copy( - update={"expires_at": expires_at} - ) + interrupt = interrupt_from_a2a(safe_value) pending[input_id] = PendingInput(value=safe_value, interrupt=interrupt, sideband=sideband) restored_run_digests = { key: value @@ -1437,22 +1359,6 @@ def _required_state_string(value: Mapping[str, Any], key: str) -> str: return result -def _parse_expires_at(value: str) -> datetime: - parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) - if parsed.tzinfo is None: - parsed = parsed.replace(tzinfo=timezone.utc) - return parsed.astimezone(timezone.utc) - - -def _pending_expires_at(binding: ThreadBinding) -> datetime | None: - values = [ - _parse_expires_at(item.interrupt.expires_at) - for item in binding.pending.values() - if isinstance(item.interrupt.expires_at, str) and item.interrupt.expires_at - ] - return min(values) if values else None - - def _raise_for_a2a_error(response: Any) -> None: payload = getattr(response, "payload", response) error = payload.get("error") if isinstance(payload, Mapping) else None diff --git a/src/iac_code/agui/app.py b/src/iac_code/agui/app.py index 5620c273..7f404d5e 100644 --- a/src/iac_code/agui/app.py +++ b/src/iac_code/agui/app.py @@ -33,7 +33,6 @@ def create_app( a2a_url: str = "http://127.0.0.1:41242/", a2a_client: Any | None = None, auth_token: str | None = None, - interrupt_ttl: int = 540, state_dir: str | Path | None = None, max_request_bytes: int = MAX_REQUEST_BYTES, idle_shutdown: float = 0, @@ -44,7 +43,6 @@ def create_app( run_adapter = adapter or AguiA2AAdapter( a2a_url=a2a_url, client=a2a_client, - interrupt_ttl=interrupt_ttl, state_dir=state_dir, ) owns_adapter = adapter is None diff --git a/src/iac_code/agui/errors.py b/src/iac_code/agui/errors.py index c82c6c1a..19684064 100644 --- a/src/iac_code/agui/errors.py +++ b/src/iac_code/agui/errors.py @@ -58,7 +58,6 @@ translate_message("The interrupt response does not contain an answer.", language="en"), translate_message("The interrupt response has already been applied.", language="en"), translate_message("The interrupt response payload is invalid.", language="en"), - translate_message("The interrupted execution has expired.", language="en"), translate_message("The interrupted execution state could not be committed.", language="en"), translate_message("The local A2A execution service is unavailable.", language="en"), translate_message( diff --git a/src/iac_code/agui/events.py b/src/iac_code/agui/events.py index abf85268..de7e5663 100644 --- a/src/iac_code/agui/events.py +++ b/src/iac_code/agui/events.py @@ -8,7 +8,6 @@ import time import uuid from collections.abc import Iterable, Mapping -from datetime import datetime, timedelta, timezone from typing import Any, cast from ag_ui.core import ( @@ -197,7 +196,7 @@ def a2a_sideband_input_ids(payload: Any) -> set[str]: return output -def interrupt_from_a2a(value: Mapping[str, Any], *, ttl_seconds: int) -> Interrupt: +def interrupt_from_a2a(value: Mapping[str, Any]) -> Interrupt: kind = str(value.get("kind") or "input_required") language = normalize_agui_language(value.get("language")) input_id = str(value.get("inputId") or f"input-{uuid.uuid4().hex}") @@ -232,9 +231,6 @@ def interrupt_from_a2a(value: Mapping[str, Any], *, ttl_seconds: int) -> Interru message=message, tool_call_id=tool_use_id, response_schema=schema, - expires_at=(datetime.now(timezone.utc) + timedelta(seconds=max(1, ttl_seconds))) - .isoformat() - .replace("+00:00", "Z"), metadata={"schemaVersion": 1, **dict(value), "standardOptions": options}, ) diff --git a/src/iac_code/agui/server.py b/src/iac_code/agui/server.py index b74d7c83..16d56187 100644 --- a/src/iac_code/agui/server.py +++ b/src/iac_code/agui/server.py @@ -21,7 +21,6 @@ def run_server( port: int = 8000, a2a_url: str | None = None, a2a_token: str | None = None, - interrupt_ttl: int = 540, state_dir: str | Path | None = None, debug: bool = False, auth_token: str | None = None, @@ -45,7 +44,6 @@ def request_shutdown() -> None: app = create_app( a2a_url=endpoint[0], a2a_client=client, - interrupt_ttl=interrupt_ttl, state_dir=state_dir, auth_token=auth_token, idle_shutdown=idle_shutdown, diff --git a/src/iac_code/cli/main.py b/src/iac_code/cli/main.py index 1cad51e1..b17ee8b3 100644 --- a/src/iac_code/cli/main.py +++ b/src/iac_code/cli/main.py @@ -770,12 +770,6 @@ def agui( "--log-stdout/--no-log-stdout", help=_("Mirror adapter logs to stdout"), ), - interrupt_ttl: int = typer.Option( - 540, - "--interrupt-ttl", - envvar="IAC_CODE_AGUI_INTERRUPT_TTL", - help=_("Seconds an AG-UI interrupt remains resumable"), - ), state_dir: str = typer.Option( "", "--state-dir", @@ -799,15 +793,11 @@ def agui( a2a_url = _a2a_config_value(ctx, config, "a2a_url", a2a_url) debug = _a2a_config_value(ctx, config, "debug", debug) log_stdout = _a2a_config_value(ctx, config, "log_stdout", log_stdout) - interrupt_ttl = _a2a_config_value(ctx, config, "interrupt_ttl", interrupt_ttl) state_dir = _a2a_config_value(ctx, config, "state_dir", state_dir) idle_shutdown = _a2a_config_value(ctx, config, "idle_shutdown", idle_shutdown) if not isinstance(port, int) or isinstance(port, bool) or not 1 <= port <= 65535: typer.echo(_("--port must be between 1 and 65535."), err=True) raise typer.Exit(1) - if not isinstance(interrupt_ttl, int) or isinstance(interrupt_ttl, bool) or interrupt_ttl <= 0: - typer.echo(_("--interrupt-ttl must be a positive integer."), err=True) - raise typer.Exit(1) if state_dir and not isinstance(state_dir, str): typer.echo(_("--state-dir must be a string."), err=True) raise typer.Exit(1) @@ -837,7 +827,6 @@ def agui( port=port, a2a_url=a2a_url or None, a2a_token=a2a_token, - interrupt_ttl=interrupt_ttl, state_dir=state_dir or None, debug=bool(debug), auth_token=auth_token, diff --git a/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po index 0e3752a3..554b9098 100644 --- a/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po @@ -481,6 +481,10 @@ msgstr "Stacktrace im öffentlichen Ereignis ausgelassen; siehe error_id." msgid "Unsupported run mode." msgstr "Nicht unterstützter Ausführungsmodus." +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "A2A-Aufgabe nicht gefunden" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "Die A2A-Aufgabe ist abgelaufen" @@ -505,10 +509,6 @@ msgstr "Der A2A-Kontext gehört zu einem anderen Arbeitsbereich" msgid "A2A context not found" msgstr "A2A-Kontext nicht gefunden" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "A2A-Aufgabe nicht gefunden" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -1084,10 +1084,6 @@ msgstr "Die Unterbrechungsantwort wurde bereits angewendet." msgid "The interrupt response payload is invalid." msgstr "Die Antwortdaten der Unterbrechung sind ungültig." -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "Die unterbrochene Ausführung ist abgelaufen." - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "Der Zustand der unterbrochenen Ausführung konnte nicht gespeichert werden." @@ -1488,10 +1484,6 @@ msgstr "" msgid "Mirror adapter logs to stdout" msgstr "Adapterprotokolle nach stdout spiegeln" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "Sekunden, in denen eine AG-UI-Unterbrechung fortgesetzt werden kann" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "Verzeichnis für den persistenten Zustand des AG-UI-Adapters" @@ -1506,10 +1498,6 @@ msgstr "" msgid "--port must be between 1 and 65535." msgstr "--port muss zwischen 1 und 65535 liegen." -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl muss eine positive Ganzzahl sein." - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir muss eine Zeichenfolge sein." @@ -14840,3 +14828,12 @@ msgstr "Bash zulassen?" #~ msgid "架构图优化中..." #~ msgstr "Architekturdiagramm wird optimiert..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "Die unterbrochene Ausführung ist abgelaufen." + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "Sekunden, in denen eine AG-UI-Unterbrechung fortgesetzt werden kann" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl muss eine positive Ganzzahl sein." + diff --git a/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po index 5c74d113..0bab7998 100644 --- a/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po @@ -474,6 +474,10 @@ msgstr "La traza de pila se omitió del evento público; consulta error_id." msgid "Unsupported run mode." msgstr "Modo de ejecución no compatible." +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "No se encontró la tarea A2A" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "La tarea A2A ha caducado" @@ -498,10 +502,6 @@ msgstr "El contexto A2A pertenece a otro espacio de trabajo" msgid "A2A context not found" msgstr "No se encontró el contexto A2A" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "No se encontró la tarea A2A" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -1072,10 +1072,6 @@ msgstr "La respuesta a la interrupción ya se ha aplicado." msgid "The interrupt response payload is invalid." msgstr "Los datos de la respuesta a la interrupción no son válidos." -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "La ejecución interrumpida ha caducado." - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "No se pudo guardar el estado de la ejecución interrumpida." @@ -1466,10 +1462,6 @@ msgstr "" msgid "Mirror adapter logs to stdout" msgstr "Duplicar los registros del adaptador en stdout" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "Segundos durante los que una interrupción AG-UI puede reanudarse" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "Directorio de estado persistente del adaptador AG-UI" @@ -1484,10 +1476,6 @@ msgstr "" msgid "--port must be between 1 and 65535." msgstr "--port debe estar entre 1 y 65535." -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl debe ser un entero positivo." - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir debe ser una cadena." @@ -14729,3 +14717,12 @@ msgstr "¿Permitir Bash?" #~ msgid "架构图优化中..." #~ msgstr "Optimizando diagrama de arquitectura..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "La ejecución interrumpida ha caducado." + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "Segundos durante los que una interrupción AG-UI puede reanudarse" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl debe ser un entero positivo." + diff --git a/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po index 77306af3..6a711de0 100644 --- a/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po @@ -468,6 +468,10 @@ msgstr "Trace de pile omise de l’événement public ; consultez error_id." msgid "Unsupported run mode." msgstr "Mode d’exécution non pris en charge." +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "Tâche A2A introuvable" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "La tâche A2A a expiré" @@ -492,10 +496,6 @@ msgstr "Le contexte A2A appartient à un autre espace de travail" msgid "A2A context not found" msgstr "Contexte A2A introuvable" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "Tâche A2A introuvable" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -1062,10 +1062,6 @@ msgstr "La réponse à l’interruption a déjà été appliquée." msgid "The interrupt response payload is invalid." msgstr "Les données de réponse à l’interruption ne sont pas valides." -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "L’exécution interrompue a expiré." - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "L’état de l’exécution interrompue n’a pas pu être enregistré." @@ -1459,10 +1455,6 @@ msgstr "" msgid "Mirror adapter logs to stdout" msgstr "Dupliquer les journaux de l’adaptateur vers stdout" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "Durée en secondes pendant laquelle une interruption AG-UI reste reprenable" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "Répertoire d’état persistant de l’adaptateur AG-UI" @@ -1477,10 +1469,6 @@ msgstr "" msgid "--port must be between 1 and 65535." msgstr "--port doit être compris entre 1 et 65535." -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl doit être un entier positif." - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir doit être une chaîne." @@ -14789,3 +14777,14 @@ msgstr "Autoriser Bash ?" #~ msgid "架构图优化中..." #~ msgstr "Optimisation du diagramme d’architecture..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "L’exécution interrompue a expiré." + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "" +#~ "Durée en secondes pendant laquelle une" +#~ " interruption AG-UI reste reprenable" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl doit être un entier positif." + diff --git a/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po index f341935a..236cab00 100644 --- a/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po @@ -423,6 +423,10 @@ msgstr "公開イベントではスタックトレースを省略しました。 msgid "Unsupported run mode." msgstr "サポートされていない実行モードです。" +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "A2A タスクが見つかりません" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "A2A タスクの有効期限が切れました" @@ -447,10 +451,6 @@ msgstr "A2A コンテキストは別のワークスペースに属していま msgid "A2A context not found" msgstr "A2A コンテキストが見つかりません" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "A2A タスクが見つかりません" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -1002,10 +1002,6 @@ msgstr "割り込み応答はすでに適用されています。" msgid "The interrupt response payload is invalid." msgstr "割り込み応答のペイロードが無効です。" -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "中断された実行は期限切れです。" - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "中断された実行状態を保存できませんでした。" @@ -1372,10 +1368,6 @@ msgstr "既存のローカル A2A URL。省略すると管理対象の A2A 子 msgid "Mirror adapter logs to stdout" msgstr "アダプターのログを標準出力にも出力します" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "AG-UI 割り込みを再開可能な状態で保持する秒数" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "AG-UI アダプターの永続状態ディレクトリ" @@ -1388,10 +1380,6 @@ msgstr "指定秒数アイドル状態が続くと終了します。0 でアイ msgid "--port must be between 1 and 65535." msgstr "--port は 1 から 65535 の範囲で指定してください。" -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl は正の整数で指定してください。" - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir は文字列で指定してください。" @@ -13637,3 +13625,12 @@ msgstr "Bash を許可しますか?" #~ msgid "架构图优化中..." #~ msgstr "アーキテクチャ図を最適化中..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "中断された実行は期限切れです。" + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "AG-UI 割り込みを再開可能な状態で保持する秒数" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl は正の整数で指定してください。" + diff --git a/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po index 579d2763..d34508a5 100644 --- a/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po @@ -467,6 +467,10 @@ msgstr "Rastreamento de pilha omitido do evento público; veja error_id." msgid "Unsupported run mode." msgstr "Modo de execução não compatível." +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "Tarefa A2A não encontrada" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "A tarefa A2A expirou" @@ -491,10 +495,6 @@ msgstr "O contexto A2A pertence a outro workspace" msgid "A2A context not found" msgstr "Contexto A2A não encontrado" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "Tarefa A2A não encontrada" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -1062,10 +1062,6 @@ msgstr "A resposta à interrupção já foi aplicada." msgid "The interrupt response payload is invalid." msgstr "Os dados da resposta à interrupção são inválidos." -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "A execução interrompida expirou." - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "Não foi possível salvar o estado da execução interrompida." @@ -1455,10 +1451,6 @@ msgstr "" msgid "Mirror adapter logs to stdout" msgstr "Espelhar os logs do adaptador para stdout" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "Segundos durante os quais uma interrupção AG-UI pode ser retomada" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "Diretório de estado persistente do adaptador AG-UI" @@ -1473,10 +1465,6 @@ msgstr "" msgid "--port must be between 1 and 65535." msgstr "--port deve estar entre 1 e 65535." -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl deve ser um inteiro positivo." - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir deve ser uma string." @@ -14597,3 +14585,12 @@ msgstr "Permitir Bash?" #~ msgid "架构图优化中..." #~ msgstr "Otimizando diagrama de arquitetura..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "A execução interrompida expirou." + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "Segundos durante os quais uma interrupção AG-UI pode ser retomada" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl deve ser um inteiro positivo." + diff --git a/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po index 3d89a7f8..d658dfc5 100644 --- a/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po @@ -423,6 +423,10 @@ msgstr "公开事件中已省略堆栈跟踪;请查看 error_id。" msgid "Unsupported run mode." msgstr "不支持的运行模式。" +#: src/iac_code/a2a/task_store.py +msgid "A2A task not found" +msgstr "未找到 A2A 任务" + #: src/iac_code/a2a/task_store.py msgid "A2A task expired" msgstr "A2A 任务已过期" @@ -447,10 +451,6 @@ msgstr "A2A 上下文属于另一个工作区" msgid "A2A context not found" msgstr "未找到 A2A 上下文" -#: src/iac_code/a2a/task_store.py -msgid "A2A task not found" -msgstr "未找到 A2A 任务" - #: src/iac_code/a2a/transports/base.py msgid "" "Unix domain socket transport is not supported on Windows. Use --transport" @@ -996,10 +996,6 @@ msgstr "中断响应已被应用。" msgid "The interrupt response payload is invalid." msgstr "中断响应内容无效。" -#: src/iac_code/agui/errors.py -msgid "The interrupted execution has expired." -msgstr "被中断的执行已过期。" - #: src/iac_code/agui/errors.py msgid "The interrupted execution state could not be committed." msgstr "无法持久化被中断的执行状态。" @@ -1362,10 +1358,6 @@ msgstr "已有的本地 A2A URL;省略时启动受管的 A2A 子进程" msgid "Mirror adapter logs to stdout" msgstr "将适配器日志同步输出到 stdout" -#: src/iac_code/cli/main.py -msgid "Seconds an AG-UI interrupt remains resumable" -msgstr "AG-UI 中断可恢复的保留秒数" - #: src/iac_code/cli/main.py msgid "Durable AG-UI adapter state directory" msgstr "AG-UI 适配器持久化状态目录" @@ -1378,10 +1370,6 @@ msgstr "空闲达到该秒数后退出;设为零禁用空闲退出" msgid "--port must be between 1 and 65535." msgstr "--port 必须在 1 到 65535 之间。" -#: src/iac_code/cli/main.py -msgid "--interrupt-ttl must be a positive integer." -msgstr "--interrupt-ttl 必须是正整数。" - #: src/iac_code/cli/main.py msgid "--state-dir must be a string." msgstr "--state-dir 必须是字符串。" @@ -13385,3 +13373,12 @@ msgstr "允许 Bash?" #~ msgid "架构图优化中..." #~ msgstr "架构图优化中..." +#~ msgid "The interrupted execution has expired." +#~ msgstr "被中断的执行已过期。" + +#~ msgid "Seconds an AG-UI interrupt remains resumable" +#~ msgstr "AG-UI 中断可恢复的保留秒数" + +#~ msgid "--interrupt-ttl must be a positive integer." +#~ msgstr "--interrupt-ttl 必须是正整数。" + diff --git a/tests/agui/test_events.py b/tests/agui/test_events.py index 1a577062..5edaedb0 100644 --- a/tests/agui/test_events.py +++ b/tests/agui/test_events.py @@ -217,7 +217,6 @@ def test_permission_metadata_becomes_self_describing_standard_interrupt() -> Non "options": [{"id": "allow_once", "label": "Allow once"}, {"id": "deny", "label": "Deny"}], "required": True, }, - ttl_seconds=60, ) assert interrupt.id == "permission-1" @@ -234,11 +233,9 @@ def test_interrupt_fallback_messages_use_projection_language(monkeypatch) -> Non permission = interrupt_from_a2a( {"kind": "permission", "inputId": "permission-1", "language": "zh-CN"}, - ttl_seconds=60, ) question = interrupt_from_a2a( {"kind": "ask_user_question", "inputId": "question-1", "language": "ja-JP"}, - ttl_seconds=60, ) assert permission.message == "zh:Permission required" diff --git a/tests/agui/test_http_sse_integration.py b/tests/agui/test_http_sse_integration.py index 0211ca2e..8743b84a 100644 --- a/tests/agui/test_http_sse_integration.py +++ b/tests/agui/test_http_sse_integration.py @@ -201,7 +201,7 @@ async def record_cancel(self, context, event_queue): ) with _serve(a2a_app) as a2a_url: - first_agui = create_agui_app(a2a_url=a2a_url, state_dir=state_dir, interrupt_ttl=30) + first_agui = create_agui_app(a2a_url=a2a_url, state_dir=state_dir) with _serve(first_agui) as first_agui_url: first_events = _read_agui(first_agui_url, _run_payload(workspace, run_id="run-1")) @@ -225,7 +225,7 @@ async def record_cancel(self, context, event_queue): ) assert persisted["execution"]["pending"][interrupt_id]["sideband"] is True - second_agui = create_agui_app(a2a_url=a2a_url, state_dir=state_dir, interrupt_ttl=30) + second_agui = create_agui_app(a2a_url=a2a_url, state_dir=state_dir) with _serve(second_agui) as second_agui_url: second_events = _read_agui( second_agui_url, diff --git a/tests/agui/test_i18n.py b/tests/agui/test_i18n.py index 56420735..7497db1b 100644 --- a/tests/agui/test_i18n.py +++ b/tests/agui/test_i18n.py @@ -11,7 +11,6 @@ "Unable to load the AG-UI config file.", "AG-UI config file must contain a YAML mapping.", "--port must be between 1 and 65535.", - "--interrupt-ttl must be a positive integer.", "--state-dir must be a string.", "--idle-shutdown must be a non-negative number.", "--a2a-url must be a string.", diff --git a/tests/agui/test_persistence.py b/tests/agui/test_persistence.py index 0fb05c48..6dd3925f 100644 --- a/tests/agui/test_persistence.py +++ b/tests/agui/test_persistence.py @@ -4,7 +4,6 @@ import json import os import stat -from datetime import datetime, timezone from pathlib import Path from typing import Any @@ -780,7 +779,7 @@ async def send_message_parts(self, url, parts, **kwargs): @pytest.mark.asyncio -async def test_cancelled_permission_resume_reschedules_interrupt_expiry(tmp_path) -> None: +async def test_cancelled_permission_resume_keeps_interrupt_pending(tmp_path) -> None: class BlockingResumeClient(FakeA2AClient): def stream_message_parts(self, url, parts, **kwargs): if kwargs.get("task_id") is None: @@ -825,8 +824,9 @@ async def events(): await blocked_read assert resume_ticket.binding.pending - assert resume_ticket.binding.expiry_task is not None - assert not resume_ticket.binding.expiry_task.done() + assert fake.cancelled == [] + persisted = json.loads(_thread_state_path(tmp_path / "state").read_text(encoding="utf-8")) + assert set(persisted["execution"]["pending"]) == {"permission-1"} await stream.aclose() await adapter.aclose() @@ -971,24 +971,25 @@ async def test_state_write_failure_cancels_a2a_without_emitting_interrupt(tmp_pa @pytest.mark.asyncio -async def test_restart_keeps_original_absolute_interrupt_expiry(tmp_path) -> None: +async def test_restart_keeps_interrupt_resumable_without_adapter_expiry(tmp_path) -> None: state_dir = tmp_path / "state" first_fake = FakeA2AClient(interrupt=True) - first_adapter = AguiA2AAdapter(a2a_url="http://a2a/", client=first_fake, state_dir=state_dir, interrupt_ttl=1) + first_adapter = AguiA2AAdapter(a2a_url="http://a2a/", client=first_fake, state_dir=state_dir) async with httpx.AsyncClient( transport=httpx.ASGITransport(app=create_app(adapter=first_adapter)), base_url="http://test" ) as client: response = await client.post("/", json=_payload(tmp_path)) - expires_at = datetime.fromisoformat( - _events(response)[-1]["outcome"]["interrupts"][0]["expiresAt"].replace("Z", "+00:00") - ) - await asyncio.sleep(0.55) + interrupt = _events(response)[-1]["outcome"]["interrupts"][0] + assert "expiresAt" not in interrupt + state_path = _thread_state_path(state_dir) + persisted = json.loads(state_path.read_text(encoding="utf-8")) + assert "expiresAt" not in persisted["execution"]["pending"]["permission-1"] + persisted["execution"]["pending"]["permission-1"]["expiresAt"] = "1970-01-01T00:00:00Z" + state_path.write_text(json.dumps(persisted), encoding="utf-8") await first_adapter.aclose() second_fake = FakeA2AClient(interrupt=True) - second_adapter = AguiA2AAdapter(a2a_url="http://a2a/", client=second_fake, state_dir=state_dir, interrupt_ttl=20) - await second_adapter.start() - assert second_adapter._threads == {} + second_adapter = AguiA2AAdapter(a2a_url="http://a2a/", client=second_fake, state_dir=state_dir) resume_payload = _payload( tmp_path, run_id="run-2", @@ -1000,18 +1001,13 @@ async def test_restart_keeps_original_absolute_interrupt_expiry(tmp_path) -> Non } ], ) - await second_adapter.admit(parse_run_input(resume_payload), canonical_digest(resume_payload)) - remaining = max(0.0, (expires_at - datetime.now(timezone.utc)).total_seconds()) - - async def wait_until_cancelled() -> None: - while not second_fake.cancelled: - await asyncio.sleep(0.02) - - await asyncio.wait_for(wait_until_cancelled(), timeout=remaining + 3.0) - assert second_fake.cancelled == ["task-1"] - state = json.loads(_thread_state_path(state_dir).read_text(encoding="utf-8")) - assert state["execution"]["taskId"] is None - assert state["execution"]["pending"] == {} + async with httpx.AsyncClient( + transport=httpx.ASGITransport(app=create_app(adapter=second_adapter)), base_url="http://test" + ) as client: + resumed = await client.post("/", json=resume_payload) + + assert _events(resumed)[-1]["outcome"] == {"type": "success"} + assert second_fake.cancelled == [] await second_adapter.aclose() diff --git a/tests/cli/test_a2a_command.py b/tests/cli/test_a2a_command.py index 2e377255..d843b184 100644 --- a/tests/cli/test_a2a_command.py +++ b/tests/cli/test_a2a_command.py @@ -58,7 +58,6 @@ def test_agui_command_loads_online_adapter_config(monkeypatch, tmp_path) -> None "port: 41243", "a2a_url: http://127.0.0.1:41242", "state_dir: /home/iac_code_config/agui", - "interrupt_ttl: 540", "idle_shutdown: 0", "log_stdout: true", ] @@ -89,7 +88,6 @@ def test_agui_command_loads_online_adapter_config(monkeypatch, tmp_path) -> None "port": 41243, "a2a_url": "http://127.0.0.1:41242", "a2a_token": None, - "interrupt_ttl": 540, "state_dir": "/home/iac_code_config/agui", "debug": False, "auth_token": None, diff --git a/website/docs/agui/getting-started.md b/website/docs/agui/getting-started.md index 345b8f53..424cc973 100644 --- a/website/docs/agui/getting-started.md +++ b/website/docs/agui/getting-started.md @@ -80,7 +80,6 @@ Static startup settings can be stored in YAML: host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -102,7 +101,6 @@ Common settings: | `--host` / `host` | `127.0.0.1` | AG-UI HTTP bind address | | `--port` / `port` | `8000` | AG-UI HTTP port; deployment examples use `41243` | | `--a2a-url` / `a2a-url` | empty | Local A2A URL; empty starts a managed child | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Seconds an interrupt remains resumable | | `--state-dir` / `state-dir` | `/agui` | AG-UI thread-state directory | | `--idle-shutdown` / `idle-shutdown` | `0` | Idle shutdown delay; `0` disables it | | `--debug` / `debug` | `false` | Debug logging | @@ -117,7 +115,6 @@ Related environment variables: | `IAC_CODE_AGUI_A2A_URL` | Local A2A upstream URL | | `IAC_CODE_AGUI_A2A_TOKEN` | A2A upstream bearer token | | `IAC_CODE_AGUI_AUTH_TOKEN` | Bearer token protecting the AG-UI endpoint | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Interrupt lifetime | | `IAC_CODE_AGUI_STATE_DIR` | AG-UI thread-state directory | | `IAC_CODE_AGUI_ALLOWED_CWDS` | Allowed workspace roots, separated with the OS path separator | | `IAC_CODE_CONFIG_DIR` | iac-code configuration root and default AG-UI state parent | diff --git a/website/docs/agui/protocol-reference.md b/website/docs/agui/protocol-reference.md index ab8337b8..b73b2500 100644 --- a/website/docs/agui/protocol-reference.md +++ b/website/docs/agui/protocol-reference.md @@ -169,9 +169,10 @@ An input-required run ends with `RUN_FINISHED.outcome.type = "interrupt"`. Each - a user-facing `message`; - an optional `toolCallId`; - a JSON `responseSchema`; -- `expiresAt`; - metadata such as `title`, `purpose`, `safeSummary`, `options`, and `toolName`. +The adapter does not impose an Interrupt deadline. A pending Interrupt remains resumable until A2A resolves, cancels, or terminates the task; A2A alone owns execution and recovery lifecycle. + For a permission request, the response schema typically accepts: ```json @@ -251,8 +252,6 @@ Each file contains thread/context/workspace binding, session and task identity, It never stores LLM keys, AccessKey secrets, or STS tokens. This is an adapter mapping directory, not a store for conversation text or execution artifacts. A2A manages its own session and task persistence; see the [A2A documentation](../a2a/overview.md). -An expired interrupt is rejected on the next access, its pending state is cleared, and the adapter attempts to cancel the matching A2A task. - ## Disconnections - A run safely finished with an interrupt no longer depends on its SSE connection. @@ -276,7 +275,6 @@ Errors before SSE begins use an HTTP JSON envelope. Errors during execution use | `UNKNOWN_INTERRUPT` | Resume references an unknown interrupt | | `RESUME_PAYLOAD_INVALID` | Missing payload or schema mismatch | | `RESUME_ALREADY_APPLIED` | The response was already applied or conflicts with it | -| `EXECUTION_EXPIRED` | The interrupt expired | | `EXECUTION_LOST` | Adapter, A2A task, or iac-code session could not be recovered | | `STATE_PERSISTENCE_FAILED` | Recovery-critical state could not be committed | | `A2A_UNAVAILABLE` | The local A2A execution service is unavailable | diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/de/docusaurus-plugin-content-docs/current/agui/getting-started.md index 1ec38116..c0704592 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -80,7 +80,6 @@ Statische Starteinstellungen können in YAML gespeichert werden: host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -100,7 +99,6 @@ Explizite CLI-Argumente überschreiben YAML. Übergeben Sie sensible Werte wie T | `--host` / `host` | `127.0.0.1` | HTTP-Bindeadresse von AG-UI | | `--port` / `port` | `8000` | AG-UI-HTTP-Port; Bereitstellungsbeispiele verwenden `41243` | | `--a2a-url` / `a2a-url` | leer | Lokale A2A-URL; leer startet einen verwalteten Kindprozess | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Sekunden, in denen eine Unterbrechung wiederaufgenommen werden kann | | `--state-dir` / `state-dir` | `/agui` | Verzeichnis für AG-UI-Threadstatus | | `--idle-shutdown` / `idle-shutdown` | `0` | Leerlaufabschaltung; `0` deaktiviert sie | | `--debug` / `debug` | `false` | Debug-Protokollierung | @@ -115,7 +113,6 @@ Zugehörige Umgebungsvariablen: | `IAC_CODE_AGUI_A2A_URL` | Lokale A2A-Upstream-URL | | `IAC_CODE_AGUI_A2A_TOKEN` | Bearer-Token für den A2A-Upstream | | `IAC_CODE_AGUI_AUTH_TOKEN` | Bearer-Token zum Schutz des AG-UI-Endpunkts | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Lebensdauer einer Unterbrechung | | `IAC_CODE_AGUI_STATE_DIR` | Verzeichnis für AG-UI-Threadstatus | | `IAC_CODE_AGUI_ALLOWED_CWDS` | Erlaubte Arbeitsbereichswurzeln, getrennt mit dem Pfadtrenner des Betriebssystems | | `IAC_CODE_CONFIG_DIR` | iac-code-Konfigurationswurzel und übergeordnetes Standardverzeichnis für AG-UI-Status | diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/de/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index 7a9a01e1..2c10ac93 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -169,9 +169,10 @@ Ein Lauf mit erforderlicher Eingabe endet mit `RUN_FINISHED.outcome.type = "inte - eine benutzerorientierte `message`; - eine optionale `toolCallId`; - ein JSON-`responseSchema`; -- `expiresAt`; - Metadaten wie `title`, `purpose`, `safeSummary`, `options` und `toolName`. +Der Adapter legt keine Frist für Unterbrechungen fest. Eine ausstehende Unterbrechung bleibt wiederaufnehmbar, bis A2A den Task auflöst, abbricht oder beendet; nur A2A verwaltet den Ausführungs- und Wiederherstellungslebenszyklus. + Für eine Berechtigungsanfrage akzeptiert das Schema normalerweise: ```json @@ -251,8 +252,6 @@ Jede Datei enthält Thread-/Kontext-/Arbeitsbereichsbindung, Sitzungs-, Task- un LLM-Schlüssel, AccessKey-Secrets und STS-Tokens werden nie gespeichert. Das Verzeichnis enthält Adapterzuordnungen, keine Gespräche oder Ausführungsartefakte. A2A verwaltet seine eigene Sitzungs- und Taskpersistenz; siehe [A2A-Dokumentation](../a2a/overview.md). -Eine abgelaufene Unterbrechung wird beim nächsten Zugriff abgelehnt, ihr Pending-Status gelöscht und der passende A2A-Task nach Möglichkeit abgebrochen. - ## Verbindungsabbrüche - Ein Lauf, der sicher mit einer Unterbrechung endete, hängt nicht mehr von seiner SSE-Verbindung ab. @@ -276,7 +275,6 @@ Fehler vor Beginn von SSE verwenden einen HTTP-JSON-Umschlag. Fehler während de | `UNKNOWN_INTERRUPT` | Unbekannte Unterbrechung in der Wiederaufnahme | | `RESUME_PAYLOAD_INVALID` | Fehlender Payload oder Schemaverstoß | | `RESUME_ALREADY_APPLIED` | Antwort wurde bereits angewendet oder steht im Konflikt | -| `EXECUTION_EXPIRED` | Unterbrechung ist abgelaufen | | `EXECUTION_LOST` | Adapter, A2A-Task oder iac-code-Sitzung konnte nicht wiederhergestellt werden | | `STATE_PERSISTENCE_FAILED` | Wiederherstellungskritischer Status konnte nicht gespeichert werden | | `A2A_UNAVAILABLE` | Lokaler A2A-Ausführungsdienst ist nicht verfügbar | diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/es/docusaurus-plugin-content-docs/current/agui/getting-started.md index f5081091..bcd3b8e4 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -74,7 +74,6 @@ iac-code agui --port 41243 --a2a-url http://127.0.0.1:41242 host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -92,7 +91,6 @@ Los argumentos CLI explícitos prevalecen sobre YAML. Inyecte los tokens mediant | `--host` / `host` | `127.0.0.1` | Dirección HTTP | | `--port` / `port` | `8000` | Puerto AG-UI; los ejemplos usan `41243` | | `--a2a-url` / `a2a-url` | vacío | URL A2A local; vacío inicia un hijo | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Segundos durante los que puede reanudarse un Interrupt | | `--state-dir` / `state-dir` | `/agui` | Estado por thread | | `--idle-shutdown` / `idle-shutdown` | `0` | Cierre por inactividad; `0` lo desactiva | | `--debug` / `debug` | `false` | Logs de depuración | @@ -104,7 +102,6 @@ Los argumentos CLI explícitos prevalecen sobre YAML. Inyecte los tokens mediant | `IAC_CODE_AGUI_A2A_URL` | URL upstream A2A local | | `IAC_CODE_AGUI_A2A_TOKEN` | Bearer token de A2A | | `IAC_CODE_AGUI_AUTH_TOKEN` | Bearer token del endpoint AG-UI | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Vigencia de Interrupt | | `IAC_CODE_AGUI_STATE_DIR` | Directorio de estado | | `IAC_CODE_AGUI_ALLOWED_CWDS` | Raíces permitidas separadas con el separador de rutas del SO | | `IAC_CODE_CONFIG_DIR` | Raíz de configuración de iac-code | diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/es/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index 79510ccd..f3dfe4f8 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -121,7 +121,9 @@ Tipos de Pipeline admitidos: ## Interrupt y Resume -Cada Interrupt contiene `id`, `reason`, `message`, `responseSchema`, `expiresAt`, un `toolCallId` opcional y metadata descriptiva. La autorización suele aceptar `{"decision":"allow_once"}` o `{"decision":"deny"}`. La UI debe respetar el schema en vez de deducir la respuesta solo a partir de `reason`. +Cada Interrupt contiene `id`, `reason`, `message`, `responseSchema`, un `toolCallId` opcional y metadata descriptiva. La autorización suele aceptar `{"decision":"allow_once"}` o `{"decision":"deny"}`. La UI debe respetar el schema en vez de deducir la respuesta solo a partir de `reason`. + +El adaptador no impone un plazo al Interrupt. Un Interrupt pendiente puede reanudarse hasta que A2A resuelva, cancele o termine la tarea; A2A es el único responsable del ciclo de vida de ejecución y recuperación. Resume es otra solicitud con el mismo `threadId`, un `runId` nuevo y el mismo `rosInvocationId`: @@ -154,8 +156,8 @@ Responde `cancelled`, `already_terminal` o `EXECUTION_NOT_FOUND`. La cancelació El estado se guarda en `/agui/threads/.json`. Contiene relaciones, identidades, posiciones del Pipeline, Interrupt e idempotencia; carga solo el thread solicitado y sustituye atómicamente un archivo pequeño. No almacena claves LLM, secretos de AccessKey, STS token, texto de conversación ni artifacts. A2A gestiona su propia persistencia; consulte su [documentación](../a2a/overview.md). -Un Interrupt expirado se rechaza y se limpia. Un run terminado con Interrupt ya no depende de su SSE; desconectar un run normal activo cancela la tarea A2A. +Un run terminado con Interrupt ya no depende de su SSE; desconectar un run normal activo cancela la tarea A2A. -Antes de SSE, los errores usan JSON HTTP; durante la ejecución usan `RUN_ERROR`. Los códigos principales son `INVALID_INPUT`, `DUPLICATE_RUN_ID`, `RUN_ID_CONFLICT`, `THREAD_BUSY`, `THREAD_BINDING_CONFLICT`, `RESUME_REQUIRED`, `INCOMPLETE_RESUME`, `UNKNOWN_INTERRUPT`, `RESUME_PAYLOAD_INVALID`, `RESUME_ALREADY_APPLIED`, `EXECUTION_EXPIRED`, `EXECUTION_LOST`, `STATE_PERSISTENCE_FAILED`, `A2A_UNAVAILABLE`, `A2A_PROTOCOL_ERROR`, `A2A_EXECUTION_FAILED` y `CANCELLED`. +Antes de SSE, los errores usan JSON HTTP; durante la ejecución usan `RUN_ERROR`. Los códigos principales son `INVALID_INPUT`, `DUPLICATE_RUN_ID`, `RUN_ID_CONFLICT`, `THREAD_BUSY`, `THREAD_BINDING_CONFLICT`, `RESUME_REQUIRED`, `INCOMPLETE_RESUME`, `UNKNOWN_INTERRUPT`, `RESUME_PAYLOAD_INVALID`, `RESUME_ALREADY_APPLIED`, `EXECUTION_LOST`, `STATE_PERSISTENCE_FAILED`, `A2A_UNAVAILABLE`, `A2A_PROTOCOL_ERROR`, `A2A_EXECUTION_FAILED` y `CANCELLED`. Las escrituras necesarias para la recuperación fallan de forma cerrada: el adaptador no anuncia un estado recuperable antes de guardarlo y cancela la tarea A2A cuando sea necesario. diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/getting-started.md index 5f6cf658..93c0db06 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -80,7 +80,6 @@ Les paramètres statiques peuvent être enregistrés dans un fichier YAML : host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -100,7 +99,6 @@ Les arguments CLI explicites remplacent le YAML. Injectez les valeurs sensibles, | `--host` / `host` | `127.0.0.1` | Adresse d’écoute HTTP AG-UI | | `--port` / `port` | `8000` | Port HTTP AG-UI ; les exemples de déploiement utilisent `41243` | | `--a2a-url` / `a2a-url` | vide | URL A2A locale ; vide démarre un enfant géré | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Durée en secondes pendant laquelle une interruption peut être reprise | | `--state-dir` / `state-dir` | `/agui` | Répertoire d’état des threads AG-UI | | `--idle-shutdown` / `idle-shutdown` | `0` | Arrêt après inactivité ; `0` le désactive | | `--debug` / `debug` | `false` | Journalisation de débogage | @@ -115,7 +113,6 @@ Variables d’environnement associées : | `IAC_CODE_AGUI_A2A_URL` | URL locale du service A2A amont | | `IAC_CODE_AGUI_A2A_TOKEN` | Jeton bearer du service A2A amont | | `IAC_CODE_AGUI_AUTH_TOKEN` | Jeton bearer protégeant le point d’accès AG-UI | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Durée de vie des interruptions | | `IAC_CODE_AGUI_STATE_DIR` | Répertoire d’état des threads AG-UI | | `IAC_CODE_AGUI_ALLOWED_CWDS` | Racines d’espace de travail autorisées, séparées par le séparateur de chemins du système | | `IAC_CODE_CONFIG_DIR` | Racine de configuration d’iac-code et parent par défaut de l’état AG-UI | diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index d354862d..ec160307 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -169,9 +169,10 @@ Une exécution nécessitant une saisie se termine par `RUN_FINISHED.outcome.type - un `message` destiné à l’utilisateur ; - un `toolCallId` facultatif ; - un `responseSchema` JSON ; -- `expiresAt` ; - des métadonnées comme `title`, `purpose`, `safeSummary`, `options` et `toolName`. +L’adaptateur n’impose aucun délai à une interruption. Une interruption en attente reste reprenable jusqu’à ce qu’A2A résolve, annule ou termine la tâche ; A2A est seul responsable du cycle de vie de l’exécution et de la reprise. + Pour une demande d’autorisation, le schéma accepte généralement : ```json @@ -251,8 +252,6 @@ Chaque fichier contient la liaison thread/contexte/espace de travail, les identi Les clés LLM, secrets AccessKey et jetons STS n’y sont jamais enregistrés. Ce répertoire sert aux associations de l’adaptateur, pas aux conversations ni aux artefacts. A2A gère sa propre persistance de sessions et de tâches ; consultez la [documentation A2A](../a2a/overview.md). -Lors de l’accès suivant, une interruption expirée est refusée, son état en attente est supprimé et l’adaptateur tente d’annuler la tâche A2A correspondante. - ## Déconnexions - Une exécution terminée proprement par une interruption ne dépend plus de sa connexion SSE. @@ -276,7 +275,6 @@ Les erreurs antérieures au démarrage de SSE utilisent une enveloppe JSON HTTP. | `UNKNOWN_INTERRUPT` | Interruption inconnue dans la reprise | | `RESUME_PAYLOAD_INVALID` | Payload absent ou non conforme au schéma | | `RESUME_ALREADY_APPLIED` | Réponse déjà appliquée ou en conflit | -| `EXECUTION_EXPIRED` | Interruption expirée | | `EXECUTION_LOST` | Impossible de restaurer l’adaptateur, la tâche A2A ou la session iac-code | | `STATE_PERSISTENCE_FAILED` | Impossible de persister un état critique pour la reprise | | `A2A_UNAVAILABLE` | Service d’exécution A2A local indisponible | diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/getting-started.md index 4b5e9de8..b7dee9d4 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -78,7 +78,6 @@ iac-code agui --port 41243 --a2a-url http://127.0.0.1:41242 host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -96,7 +95,6 @@ iac-code agui --config agui-server.yml | `--host` / `host` | `127.0.0.1` | AG-UI HTTP バインド先 | | `--port` / `port` | `8000` | AG-UI ポート。例では `41243` | | `--a2a-url` / `a2a-url` | 空 | ローカル A2A URL。空なら子プロセスを起動 | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Interrupt を Resume できる秒数 | | `--state-dir` / `state-dir` | `/agui` | thread 状態ディレクトリ | | `--idle-shutdown` / `idle-shutdown` | `0` | アイドル終了秒数。`0` は無効 | | `--debug` / `debug` | `false` | デバッグログ | @@ -109,7 +107,6 @@ iac-code agui --config agui-server.yml | `IAC_CODE_AGUI_A2A_URL` | ローカル A2A upstream URL | | `IAC_CODE_AGUI_A2A_TOKEN` | A2A upstream token | | `IAC_CODE_AGUI_AUTH_TOKEN` | AG-UI endpoint を保護する token | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Interrupt 有効期間 | | `IAC_CODE_AGUI_STATE_DIR` | thread 状態ディレクトリ | | `IAC_CODE_AGUI_ALLOWED_CWDS` | OS のパス区切りで列挙した許可ワークスペースルート | | `IAC_CODE_CONFIG_DIR` | iac-code 設定ルートと既定状態ディレクトリの親 | diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index bdeb8cc5..cfd60d8c 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -144,7 +144,9 @@ AG-UI span の整合性を保つため、Interrupt 前に開いている message ## Interrupt と Resume -入力待ち run は `RUN_FINISHED.outcome.type = "interrupt"` で終了します。Interrupt には `id`、`reason`、ユーザー向け `message`、任意の `toolCallId`、JSON `responseSchema`、`expiresAt`、`title/purpose/safeSummary/options/toolName` などの metadata が含まれます。 +入力待ち run は `RUN_FINISHED.outcome.type = "interrupt"` で終了します。Interrupt には `id`、`reason`、ユーザー向け `message`、任意の `toolCallId`、JSON `responseSchema`、`title/purpose/safeSummary/options/toolName` などの metadata が含まれます。 + +adapter は Interrupt に期限を設定しません。pending Interrupt は A2A が task を解決、キャンセル、または終了するまで Resume 可能で、実行と復旧のライフサイクルは A2A だけが管理します。 権限確認の例: @@ -205,7 +207,7 @@ Content-Type: application/json LLM key、AccessKey Secret、STS token、会話本文、実行成果物は保存しません。A2A の session/task 永続化は A2A server が管理します。[A2A ドキュメント](../a2a/overview.md)を参照してください。 -期限切れ Interrupt は次回アクセス時に拒否・消去され、対応 A2A task のキャンセルを試みます。Interrupt で安全終了した run は古い SSE を必要としません。通常の実行中に切断すると A2A task をキャンセルします。 +Interrupt で安全終了した run は古い SSE を必要としません。通常の実行中に切断すると A2A task をキャンセルします。 ## エラー @@ -221,7 +223,7 @@ SSE 前のエラーは HTTP JSON、実行中のエラーは `RUN_ERROR` です | `INCOMPLETE_RESUME` / `UNKNOWN_INTERRUPT` | Resume の不足、重複、未知 ID | | `RESUME_PAYLOAD_INVALID` | payload が schema と不一致 | | `RESUME_ALREADY_APPLIED` | 回答を適用済み | -| `EXECUTION_EXPIRED` / `EXECUTION_LOST` | execution が期限切れまたは復旧不能 | +| `EXECUTION_LOST` | execution が復旧不能 | | `STATE_PERSISTENCE_FAILED` | 重要状態を書き込めない | | `A2A_UNAVAILABLE` / `A2A_PROTOCOL_ERROR` / `A2A_EXECUTION_FAILED` | A2A の接続、ID、実行エラー | | `CANCELLED` | execution がキャンセル済み | diff --git a/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/getting-started.md index b86aac69..1d7193a4 100644 --- a/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -80,7 +80,6 @@ Configurações estáticas de inicialização podem ser armazenadas em YAML: host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -100,7 +99,6 @@ Argumentos explícitos da CLI substituem o YAML. Injete valores confidenciais, c | `--host` / `host` | `127.0.0.1` | Endereço HTTP de escuta do AG-UI | | `--port` / `port` | `8000` | Porta HTTP do AG-UI; os exemplos de implantação usam `41243` | | `--a2a-url` / `a2a-url` | vazio | URL A2A local; vazio inicia um filho gerenciado | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Segundos durante os quais uma interrupção pode ser retomada | | `--state-dir` / `state-dir` | `/agui` | Diretório de estado dos threads AG-UI | | `--idle-shutdown` / `idle-shutdown` | `0` | Atraso para desligamento ocioso; `0` o desabilita | | `--debug` / `debug` | `false` | Logs de depuração | @@ -115,7 +113,6 @@ Variáveis de ambiente relacionadas: | `IAC_CODE_AGUI_A2A_URL` | URL do upstream A2A local | | `IAC_CODE_AGUI_A2A_TOKEN` | Token bearer do upstream A2A | | `IAC_CODE_AGUI_AUTH_TOKEN` | Token bearer que protege o endpoint AG-UI | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Vida útil da interrupção | | `IAC_CODE_AGUI_STATE_DIR` | Diretório de estado dos threads AG-UI | | `IAC_CODE_AGUI_ALLOWED_CWDS` | Raízes de workspace permitidas, separadas pelo separador de caminhos do sistema operacional | | `IAC_CODE_CONFIG_DIR` | Raiz de configuração do iac-code e diretório pai padrão do estado AG-UI | diff --git a/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index 468b9acc..e46dabcf 100644 --- a/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/pt/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -169,9 +169,10 @@ Uma execução que requer entrada termina com `RUN_FINISHED.outcome.type = "inte - uma `message` para o usuário; - um `toolCallId` opcional; - um `responseSchema` JSON; -- `expiresAt`; - metadados como `title`, `purpose`, `safeSummary`, `options` e `toolName`. +O adaptador não impõe prazo ao Interrupt. Um Interrupt pendente continua retomável até que o A2A resolva, cancele ou encerre a tarefa; somente o A2A controla o ciclo de vida da execução e da recuperação. + Para uma solicitação de permissão, o esquema normalmente aceita: ```json @@ -251,8 +252,6 @@ Cada arquivo contém o vínculo entre thread, contexto e workspace, identidades Chaves de LLM, segredos AccessKey e tokens STS nunca são armazenados. Esse é um diretório de mapeamentos do adaptador, não de conversas ou artefatos de execução. O A2A gerencia sua própria persistência de sessões e tarefas; consulte a [documentação do A2A](../a2a/overview.md). -Uma interrupção expirada é rejeitada no próximo acesso, seu estado pendente é removido e o adaptador tenta cancelar a tarefa A2A correspondente. - ## Desconexões - Uma execução concluída com segurança por uma interrupção deixa de depender da conexão SSE. @@ -276,7 +275,6 @@ Erros anteriores ao início do SSE usam um envelope JSON HTTP. Erros durante a e | `UNKNOWN_INTERRUPT` | A retomada referencia uma interrupção desconhecida | | `RESUME_PAYLOAD_INVALID` | Payload ausente ou incompatível com o esquema | | `RESUME_ALREADY_APPLIED` | A resposta já foi aplicada ou conflita com ela | -| `EXECUTION_EXPIRED` | A interrupção expirou | | `EXECUTION_LOST` | Não foi possível recuperar o adaptador, a tarefa A2A ou a sessão do iac-code | | `STATE_PERSISTENCE_FAILED` | O estado crítico para recuperação não pôde ser persistido | | `A2A_UNAVAILABLE` | O serviço local de execução A2A está indisponível | diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/getting-started.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/getting-started.md index 57bb4362..672f34c0 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/getting-started.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/getting-started.md @@ -80,7 +80,6 @@ iac-code agui --port 41243 --a2a-url http://127.0.0.1:41242 host: 0.0.0.0 port: 41243 a2a-url: http://127.0.0.1:41242 -interrupt-ttl: 540 state-dir: /var/lib/iac-code/agui idle-shutdown: 0 debug: false @@ -102,7 +101,6 @@ iac-code agui --config agui-server.yml | `--host` / `host` | `127.0.0.1` | AG-UI HTTP 监听地址 | | `--port` / `port` | `8000` | AG-UI HTTP 端口;部署示例使用 `41243` | | `--a2a-url` / `a2a-url` | 空 | 本地 A2A URL;为空时启动受管子进程 | -| `--interrupt-ttl` / `interrupt-ttl` | `540` | Interrupt 可恢复秒数 | | `--state-dir` / `state-dir` | `/agui` | AG-UI thread 状态目录 | | `--idle-shutdown` / `idle-shutdown` | `0` | 空闲自动退出秒数;`0` 表示关闭 | | `--debug` / `debug` | `false` | 调试日志 | @@ -117,7 +115,6 @@ iac-code agui --config agui-server.yml | `IAC_CODE_AGUI_A2A_URL` | 本地 A2A upstream URL | | `IAC_CODE_AGUI_A2A_TOKEN` | A2A upstream Bearer token | | `IAC_CODE_AGUI_AUTH_TOKEN` | 保护 AG-UI endpoint 的 Bearer token | -| `IAC_CODE_AGUI_INTERRUPT_TTL` | Interrupt 有效期 | | `IAC_CODE_AGUI_STATE_DIR` | AG-UI thread 状态目录 | | `IAC_CODE_AGUI_ALLOWED_CWDS` | 允许的工作区根目录,使用操作系统路径分隔符分隔 | | `IAC_CODE_CONFIG_DIR` | iac-code 配置根目录,也决定默认 AG-UI 状态目录 | diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/protocol-reference.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/protocol-reference.md index a1c4ce9c..53bc2f11 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/protocol-reference.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agui/protocol-reference.md @@ -238,7 +238,6 @@ data: {"type":"TEXT_MESSAGE_CONTENT",...} "required": ["decision"], "additionalProperties": false }, - "expiresAt": "2026-08-27T03:00:00Z", "metadata": { "schemaVersion": 1, "kind": "permission", @@ -360,14 +359,14 @@ AG-UI 状态默认保存在: - iac-code session ID; - 当前 `executionId`、`rosInvocationId` 和 A2A `taskId`; - Pipeline sequence、打开的步骤和文本快照摘要; -- pending Interrupt 与有效期; +- pending Interrupt; - run、Resume 和终态幂等信息。 adapter 启动时不扫描全部目录。收到 thread 请求后才懒加载对应文件,每次只原子替换当前 thread 的小文件。 AG-UI 状态不保存 LLM key、AccessKey secret 或 STS token。该目录只属于协议 adapter,不是 iac-code 对话正文或执行产物的存储位置。A2A 的会话和任务持久化由 A2A server 自己管理,详见 [A2A 文档](../a2a/overview.md)。 -如果 Interrupt 超过 `expiresAt`,下一次访问会拒绝 Resume、清理 pending,并尽力取消对应 A2A task。 +adapter 不为 Interrupt 设置有效期。pending Interrupt 会一直保持可恢复,直到 A2A 完成、取消或终止对应 task;执行和恢复生命周期只由 A2A 管理。 ## 断开连接 @@ -403,7 +402,6 @@ AG-UI 状态不保存 LLM key、AccessKey secret 或 STS token。该目录只属 | `UNKNOWN_INTERRUPT` | Resume 引用了未知 Interrupt | | `RESUME_PAYLOAD_INVALID` | payload 缺失或不符合 response schema | | `RESUME_ALREADY_APPLIED` | 响应已应用,或重复请求与已应用内容冲突 | -| `EXECUTION_EXPIRED` | Interrupt 已过期 | | `EXECUTION_LOST` | adapter、A2A task 或 iac-code session 无法恢复 | | `STATE_PERSISTENCE_FAILED` | 恢复关键状态无法可靠写入 | | `A2A_UNAVAILABLE` | 本地 A2A execution service 不可用 |