agentengine-sdk-python 0.8.5__py3-none-any.whl → 0.8.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/METADATA +12 -8
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/RECORD +823 -388
- ksadk/_async_iterators.py +79 -0
- ksadk/_build_provenance.json +1 -1
- ksadk/_model_anthropic.py +49 -0
- ksadk/_model_input.py +86 -0
- ksadk/_model_json.py +40 -0
- ksadk/_model_reasoning.py +17 -0
- ksadk/_model_refusal.py +20 -0
- ksadk/_model_sse.py +163 -0
- ksadk/a2a/__init__.py +157 -167
- ksadk/a2a/_invocation_state.py +86 -0
- ksadk/a2a/_space_client_events.py +33 -22
- ksadk/a2a/adk.py +194 -0
- ksadk/a2a/adk_invocation_context.py +242 -0
- ksadk/a2a/adk_runtime.py +126 -0
- ksadk/a2a/adk_tools.py +319 -0
- ksadk/a2a/bootstrap.py +149 -23
- ksadk/a2a/build_validation.py +147 -0
- ksadk/a2a/card.py +23 -15
- ksadk/a2a/codex.py +160 -0
- ksadk/a2a/codex_checkpoint.py +111 -0
- ksadk/a2a/codex_recovery.py +96 -0
- ksadk/a2a/codex_recovery_events.py +123 -0
- ksadk/a2a/codex_runtime.py +198 -0
- ksadk/a2a/codex_session_checkpoint.py +162 -0
- ksadk/a2a/composition.py +88 -0
- ksadk/a2a/context_store.py +118 -19
- ksadk/a2a/control_plane.py +108 -106
- ksadk/a2a/event_adapter.py +92 -193
- ksadk/a2a/executor.py +257 -85
- ksadk/a2a/exposure.py +106 -0
- ksadk/a2a/external_transport.py +3 -4
- ksadk/a2a/identity.py +106 -12
- ksadk/a2a/ids.py +14 -3
- ksadk/a2a/inbound_kernel.py +133 -0
- ksadk/a2a/interaction_wire.py +76 -0
- ksadk/a2a/invocation_checkpoint.py +274 -0
- ksadk/a2a/invocation_policy.py +87 -0
- ksadk/a2a/invocation_publication.py +210 -0
- ksadk/a2a/invocation_service.py +972 -0
- ksadk/a2a/invocation_types.py +459 -0
- ksadk/a2a/kernel_task_adapter.py +259 -0
- ksadk/a2a/langgraph.py +20 -7
- ksadk/a2a/langgraph_invocation_context.py +136 -0
- ksadk/a2a/langgraph_managed.py +150 -0
- ksadk/a2a/langgraph_recovery.py +32 -0
- ksadk/a2a/langgraph_runtime.py +89 -0
- ksadk/a2a/langgraph_tool_runtime.py +132 -0
- ksadk/a2a/langgraph_tools.py +63 -0
- ksadk/a2a/message_digest.py +40 -0
- ksadk/a2a/platform.py +389 -0
- ksadk/a2a/policy_refs.py +13 -0
- ksadk/a2a/presentation.py +402 -0
- ksadk/a2a/product_kernel_facts.py +177 -0
- ksadk/a2a/product_key.py +99 -0
- ksadk/a2a/protocol_errors.py +116 -0
- ksadk/a2a/result_text.py +49 -0
- ksadk/a2a/resume_store.py +93 -3
- ksadk/a2a/routes.py +6 -1
- ksadk/a2a/runtime_approval.py +198 -0
- ksadk/a2a/runtime_input.py +151 -0
- ksadk/a2a/server.py +16 -7
- ksadk/a2a/space_client.py +101 -16
- ksadk/a2a/task_adapter.py +19 -5
- ksadk/a2a/task_event_dispatcher.py +39 -2
- ksadk/a2a/task_event_outbox.py +215 -0
- ksadk/a2a/task_event_outbox_owned.py +130 -0
- ksadk/a2a/task_store.py +37 -0
- ksadk/a2a/tool_activity.py +176 -0
- ksadk/a2a/workload_tokens.py +134 -0
- ksadk/api/client.py +32 -79
- ksadk/api/sse_stream.py +96 -0
- ksadk/builders/code_builder.py +83 -106
- ksadk/builders/container_builder.py +11 -29
- ksadk/builders/framework_requirements.py +2 -0
- ksadk/builders/managed_runtime_builder.py +12 -0
- ksadk/builders/source_provenance.py +87 -0
- ksadk/cli/cmd_a2a.py +43 -0
- ksadk/cli/cmd_build.py +9 -0
- ksadk/cli/cmd_deploy.py +27 -2
- ksadk/cli/cmd_invoke.py +45 -92
- ksadk/cli/cmd_studio.py +5 -1
- ksadk/cli/cmd_web.py +2 -0
- ksadk/cli/hosted_invoke.py +206 -0
- ksadk/cli/invoke_content.py +60 -0
- ksadk/cli/runtime_bootstrap.py +27 -5
- ksadk/codex/_expert_request_reader.py +48 -0
- ksadk/codex/a2a_tools.py +140 -0
- ksadk/codex/async_approval.py +169 -0
- ksadk/codex/async_client.py +938 -0
- ksadk/codex/async_proxy.py +216 -0
- ksadk/codex/base_runtime.py +25 -0
- ksadk/codex/client.py +3 -1048
- ksadk/codex/client_payloads.py +85 -0
- ksadk/codex/rollout_checkpoint.py +264 -0
- ksadk/codex/runtime.py +290 -54
- ksadk/common/auth.py +2 -2
- ksadk/common/cloud_headers.py +36 -0
- ksadk/common/diagnostics.py +34 -0
- ksadk/common/kop_client.py +2 -1
- ksadk/configs/env_registry.py +106 -127
- ksadk/configs/env_registry_a2a.py +173 -0
- ksadk/configs/env_registry_studio.py +53 -5
- ksadk/configs/global_config.py +1 -0
- ksadk/configs/settings.py +6 -0
- ksadk/context_engine/assembler.py +9 -0
- ksadk/context_engine/contributors.py +80 -17
- ksadk/context_engine/instruction_authority.py +18 -0
- ksadk/conversations/attachments.py +53 -15
- ksadk/conversations/contracts.py +28 -9
- ksadk/conversations/message_projection.py +30 -125
- ksadk/conversations/message_projection_blocks.py +137 -0
- ksadk/conversations/model_context.py +11 -3
- ksadk/conversations/projector.py +63 -0
- ksadk/conversations/runtime_compaction.py +7 -2
- ksadk/conversations/runtime_input.py +16 -4
- ksadk/conversations/runtime_payloads.py +4 -0
- ksadk/conversations/runtime_persistence.py +192 -24
- ksadk/conversations/runtime_preparation.py +85 -6
- ksadk/conversations/runtime_projection.py +217 -0
- ksadk/conversations/runtime_resume.py +141 -70
- ksadk/conversations/tool_receipt_resume.py +387 -0
- ksadk/conversations/tool_receipts.py +32 -0
- ksadk/deployment/env_forward.py +5 -0
- ksadk/deployment/manager.py +15 -6
- ksadk/deployment/providers/serverless.py +76 -136
- ksadk/deployment/providers/serverless_payloads.py +122 -0
- ksadk/evaluation/agent_eval_client.py +2 -1
- ksadk/evaluation/contracts.py +78 -3
- ksadk/evaluation/evaluators.py +166 -1
- ksadk/evaluation/evidence.py +271 -4
- ksadk/evaluation/local_adapter.py +76 -12
- ksadk/evaluation/studio_build_adapter.py +73 -9
- ksadk/events/adapters/_a2a_checkpoint.py +151 -0
- ksadk/events/adapters/_a2a_snapshot.py +18 -14
- ksadk/events/adapters/_a2a_support.py +15 -3
- ksadk/events/adapters/_a2a_tool_activity.py +123 -0
- ksadk/events/adapters/_codex_errors.py +49 -0
- ksadk/events/adapters/_langgraph_support.py +12 -1
- ksadk/events/adapters/a2a.py +54 -26
- ksadk/events/adapters/adk.py +188 -30
- ksadk/events/adapters/codex.py +33 -7
- ksadk/events/adapters/langgraph.py +16 -6
- ksadk/events/canonical.py +5 -0
- ksadk/events/canonical_store.py +200 -4
- ksadk/events/reducer.py +82 -0
- ksadk/events/run_authority.py +176 -0
- ksadk/harness/__init__.py +4 -0
- ksadk/harness/artifact_adapters/__init__.py +1 -0
- ksadk/harness/artifact_adapters/native_page_budget.py +172 -0
- ksadk/harness/artifact_adapters/native_search.py +249 -0
- ksadk/harness/artifact_adapters/native_search_budget.py +134 -0
- ksadk/harness/artifact_adapters/native_store.py +115 -0
- ksadk/harness/artifact_adapters/native_tool.py +222 -0
- ksadk/harness/artifact_store.py +26 -9
- ksadk/harness/browser_adapters/__init__.py +6 -0
- ksadk/harness/browser_adapters/native_browser_tool.py +92 -0
- ksadk/harness/browser_adapters/native_dom_snapshot.py +187 -0
- ksadk/harness/browser_adapters/native_playwright.py +953 -0
- ksadk/harness/browser_adapters/native_vision.py +295 -0
- ksadk/harness/capability_adapters/__init__.py +1 -0
- ksadk/harness/capability_adapters/native_dsh.py +289 -0
- ksadk/harness/capability_runtime.py +104 -9
- ksadk/harness/compaction_record.py +31 -0
- ksadk/harness/compiler.py +3 -3
- ksadk/harness/config.py +28 -5
- ksadk/harness/conformance/contract.py +9 -11
- ksadk/harness/context_engine.py +18 -33
- ksadk/harness/context_sections.py +397 -0
- ksadk/harness/diagnostic_adapters/__init__.py +1 -0
- ksadk/harness/diagnostic_adapters/contracts.py +70 -0
- ksadk/harness/diagnostic_adapters/lsp_rpc.py +210 -0
- ksadk/harness/diagnostic_adapters/native_lsp.py +286 -0
- ksadk/harness/engine/completion_guard.py +16 -1
- ksadk/harness/engine/context_pipeline.py +864 -92
- ksadk/harness/engine/graph_builder.py +123 -630
- ksadk/harness/engine/hooked_reasoner.py +380 -0
- ksadk/harness/engine/langgraph.py +126 -8
- ksadk/harness/engine/mcp_disclosure.py +65 -114
- ksadk/harness/engine/phases/__init__.py +6 -0
- ksadk/harness/engine/phases/context.py +48 -0
- ksadk/harness/engine/phases/interaction.py +31 -0
- ksadk/harness/engine/phases/reason.py +552 -0
- ksadk/harness/engine/phases/tools.py +638 -0
- ksadk/harness/engine/policy_runtime.py +21 -10
- ksadk/harness/engine/postgres_checkpointer.py +325 -2
- ksadk/harness/engine/run_control_bridge.py +21 -1
- ksadk/harness/engine/run_types.py +10 -0
- ksadk/harness/engine/skill_disclosure.py +98 -12
- ksadk/harness/engine/steering.py +177 -0
- ksadk/harness/engine/subagents.py +18 -1
- ksadk/harness/engine/tool_dispatch.py +112 -7
- ksadk/harness/events.py +39 -2
- ksadk/harness/execution_policy.py +10 -0
- ksadk/harness/hooks.py +259 -0
- ksadk/harness/loop/reason.py +281 -36
- ksadk/harness/loop/tools.py +157 -12
- ksadk/harness/managed_runtime.py +254 -94
- ksadk/harness/mcp_disclosure_contract.py +85 -0
- ksadk/harness/mcp_result_policy.py +44 -0
- ksadk/harness/mcp_runtime.py +33 -0
- ksadk/harness/micro_compact.py +69 -0
- ksadk/harness/model_adapters/__init__.py +1 -0
- ksadk/harness/model_adapters/native_mcp_media.py +100 -0
- ksadk/harness/model_adapters/native_mcp_result.py +36 -0
- ksadk/harness/model_adapters/native_openai.py +703 -0
- ksadk/harness/model_capability.py +223 -8
- ksadk/harness/model_provider.py +214 -4
- ksadk/harness/multimodal.py +57 -0
- ksadk/harness/native/__init__.py +1 -0
- ksadk/harness/native/activation_contract.py +25 -0
- ksadk/harness/native/activation_recovery.py +29 -0
- ksadk/harness/native/adapters.py +291 -0
- ksadk/harness/native/artifact_contract.py +14 -0
- ksadk/harness/native/artifact_partial_read_capacity.py +171 -0
- ksadk/harness/native/artifact_read_capacity.py +182 -0
- ksadk/harness/native/artifact_read_contract.py +27 -0
- ksadk/harness/native/artifact_runtime.py +408 -0
- ksadk/harness/native/artifact_search_capacity.py +206 -0
- ksadk/harness/native/artifact_search_contract.py +40 -0
- ksadk/harness/native/artifact_single_read_capacity.py +170 -0
- ksadk/harness/native/browser_contract.py +229 -0
- ksadk/harness/native/browser_recovery.py +183 -0
- ksadk/harness/native/capabilities.py +204 -0
- ksadk/harness/native/child_approval.py +60 -0
- ksadk/harness/native/child_budget.py +136 -0
- ksadk/harness/native/child_cancellation.py +80 -0
- ksadk/harness/native/child_input.py +98 -0
- ksadk/harness/native/child_input_runtime.py +59 -0
- ksadk/harness/native/child_interruption.py +81 -0
- ksadk/harness/native/child_lifecycle.py +246 -0
- ksadk/harness/native/child_lifecycle_contract.py +91 -0
- ksadk/harness/native/child_notifications.py +75 -0
- ksadk/harness/native/child_promotion.py +66 -0
- ksadk/harness/native/command_runtime.py +99 -0
- ksadk/harness/native/completion.py +302 -0
- ksadk/harness/native/completion_text.py +116 -0
- ksadk/harness/native/context.py +358 -0
- ksadk/harness/native/context_artifact_page.py +75 -0
- ksadk/harness/native/context_compaction_projection.py +131 -0
- ksadk/harness/native/context_contributions.py +320 -0
- ksadk/harness/native/context_contributor_contract.py +133 -0
- ksadk/harness/native/context_driver.py +820 -0
- ksadk/harness/native/context_fresh_tools.py +49 -0
- ksadk/harness/native/context_history.py +272 -0
- ksadk/harness/native/context_microcompact.py +61 -0
- ksadk/harness/native/context_partial_summary.py +58 -0
- ksadk/harness/native/context_partition.py +492 -0
- ksadk/harness/native/context_pruning.py +275 -0
- ksadk/harness/native/context_reduction.py +60 -0
- ksadk/harness/native/context_replay.py +148 -0
- ksadk/harness/native/context_request_projection.py +526 -0
- ksadk/harness/native/context_runtime.py +976 -0
- ksadk/harness/native/context_runtime_contracts.py +409 -0
- ksadk/harness/native/context_search.py +112 -0
- ksadk/harness/native/context_source_fragments.py +188 -0
- ksadk/harness/native/context_source_progress.py +119 -0
- ksadk/harness/native/context_summary_planning.py +788 -0
- ksadk/harness/native/context_summary_recovery.py +105 -0
- ksadk/harness/native/delivery_review.py +331 -0
- ksadk/harness/native/delivery_review_contract.py +54 -0
- ksadk/harness/native/effect_admission.py +82 -0
- ksadk/harness/native/effect_control.py +318 -0
- ksadk/harness/native/effect_receipts.py +729 -0
- ksadk/harness/native/effect_runtime.py +303 -0
- ksadk/harness/native/effective_configuration.py +28 -0
- ksadk/harness/native/engine.py +945 -0
- ksadk/harness/native/final_output.py +34 -0
- ksadk/harness/native/final_submission.py +58 -0
- ksadk/harness/native/finalization_workspace.py +96 -0
- ksadk/harness/native/fork.py +151 -0
- ksadk/harness/native/guidance.py +91 -0
- ksadk/harness/native/history.py +381 -0
- ksadk/harness/native/hook_executor.py +205 -0
- ksadk/harness/native/hook_runtime_contract.py +282 -0
- ksadk/harness/native/interaction_observation.py +117 -0
- ksadk/harness/native/lease.py +103 -0
- ksadk/harness/native/machine.py +974 -0
- ksadk/harness/native/mcp_result_governance.py +156 -0
- ksadk/harness/native/model.py +570 -0
- ksadk/harness/native/model_feedback.py +63 -0
- ksadk/harness/native/model_input.py +30 -0
- ksadk/harness/native/model_recovery_contract.py +35 -0
- ksadk/harness/native/model_replay.py +128 -0
- ksadk/harness/native/model_request.py +133 -0
- ksadk/harness/native/model_runtime.py +196 -0
- ksadk/harness/native/model_takeover.py +419 -0
- ksadk/harness/native/model_tool_batch.py +40 -0
- ksadk/harness/native/observed_progress.py +46 -0
- ksadk/harness/native/operator_guidance.py +201 -0
- ksadk/harness/native/output_contract.py +80 -0
- ksadk/harness/native/output_recovery.py +24 -0
- ksadk/harness/native/ports.py +749 -0
- ksadk/harness/native/precompact_hook_runtime.py +171 -0
- ksadk/harness/native/replay_context.py +149 -0
- ksadk/harness/native/request_media.py +112 -0
- ksadk/harness/native/restore_contract.py +16 -0
- ksadk/harness/native/run_clock.py +120 -0
- ksadk/harness/native/state.py +436 -0
- ksadk/harness/native/steering.py +170 -0
- ksadk/harness/native/stop_hook_runtime.py +151 -0
- ksadk/harness/native/studio_admission.py +29 -0
- ksadk/harness/native/subagents.py +86 -0
- ksadk/harness/native/task_policy.py +99 -0
- ksadk/harness/native/task_progress.py +86 -0
- ksadk/harness/native/terminal_verification.py +43 -0
- ksadk/harness/native/tool_argument_feedback.py +178 -0
- ksadk/harness/native/tool_budget.py +253 -0
- ksadk/harness/native/tool_error_streak.py +89 -0
- ksadk/harness/native/tool_hook_runtime.py +343 -0
- ksadk/harness/native/tool_input_validation.py +58 -0
- ksadk/harness/native/tool_interruption.py +96 -0
- ksadk/harness/native/tool_observation.py +259 -0
- ksadk/harness/native/tool_progress.py +223 -0
- ksadk/harness/native/tool_recovery.py +25 -0
- ksadk/harness/native/tool_scheduling.py +33 -0
- ksadk/harness/native/tool_schema.py +126 -0
- ksadk/harness/native/transitions.py +151 -0
- ksadk/harness/native/turn_control.py +98 -0
- ksadk/harness/native/user_document.py +98 -0
- ksadk/harness/native/user_document_capture.py +49 -0
- ksadk/harness/native/user_input.py +87 -0
- ksadk/harness/native/verification.py +432 -0
- ksadk/harness/native/verification_binding.py +40 -0
- ksadk/harness/native/verification_runtime.py +233 -0
- ksadk/harness/native/verification_workspace.py +67 -0
- ksadk/harness/native_driver_failure.py +123 -0
- ksadk/harness/native_facade.py +977 -0
- ksadk/harness/native_factory.py +244 -0
- ksadk/harness/native_projection.py +503 -0
- ksadk/harness/native_runtime.py +621 -0
- ksadk/harness/native_shadow.py +250 -0
- ksadk/harness/native_shadow_inputs.py +146 -0
- ksadk/harness/progress_adapters/__init__.py +5 -0
- ksadk/harness/progress_adapters/native_todo.py +75 -0
- ksadk/harness/prompt_cache.py +65 -3
- ksadk/harness/public_activity.py +59 -0
- ksadk/harness/public_output.py +30 -0
- ksadk/harness/public_summary.py +36 -0
- ksadk/harness/reasoner.py +331 -22
- ksadk/harness/rpc.py +200 -0
- ksadk/harness/run_control.py +107 -2
- ksadk/harness/runtime.py +11 -2
- ksadk/harness/runtime_capabilities.py +71 -1
- ksadk/harness/sandbox_backend.py +203 -0
- ksadk/harness/sdk.py +153 -0
- ksadk/harness/session_store.py +370 -24
- ksadk/harness/skill_adapters/__init__.py +1 -0
- ksadk/harness/skill_adapters/native_bundle.py +148 -0
- ksadk/harness/skill_adapters/native_disclosure.py +306 -0
- ksadk/harness/skill_intent.py +47 -0
- ksadk/harness/skill_runtime.py +74 -16
- ksadk/harness/spec.py +20 -10
- ksadk/harness/state.py +2 -0
- ksadk/harness/stores/__init__.py +14 -0
- ksadk/harness/stores/native_command_settlement.py +65 -0
- ksadk/harness/stores/native_contract.py +311 -0
- ksadk/harness/stores/native_memory.py +326 -0
- ksadk/harness/stores/native_memory_archive.py +70 -0
- ksadk/harness/stores/native_migration_cli.py +91 -0
- ksadk/harness/stores/native_outbox.py +56 -0
- ksadk/harness/stores/native_sqlite.py +782 -0
- ksadk/harness/stores/native_sqlite_audit.py +29 -0
- ksadk/harness/stores/native_sqlite_migration.py +252 -0
- ksadk/harness/subagent.py +12 -2
- ksadk/harness/subagent_adapters/checkpoint_recovery.py +120 -0
- ksadk/harness/subagent_adapters/native_background_child.py +803 -0
- ksadk/harness/subagent_adapters/native_child.py +819 -0
- ksadk/harness/subagent_adapters/native_child_approval.py +128 -0
- ksadk/harness/subagent_adapters/native_child_continuation.py +51 -0
- ksadk/harness/subagent_adapters/native_child_result.py +392 -0
- ksadk/harness/subagent_adapters/prepared_recovery.py +120 -0
- ksadk/harness/tool_display.py +75 -0
- ksadk/harness/tool_display_contract.py +134 -0
- ksadk/harness/tool_output.py +141 -0
- ksadk/harness/tool_policy.py +3 -0
- ksadk/harness/tool_result_policy.py +216 -0
- ksadk/harness/tools.py +52 -0
- ksadk/harness/workspace_adapters/__init__.py +6 -0
- ksadk/harness/workspace_adapters/native_directory.py +180 -0
- ksadk/harness/workspace_adapters/native_git.py +208 -0
- ksadk/harness/workspace_adapters/native_isolation.py +350 -0
- ksadk/interaction/providers/langgraph.py +8 -1
- ksadk/kernel/bootstrap.py +144 -160
- ksadk/kernel/contract_fingerprints.py +45 -1
- ksadk/kernel/contracts.py +25 -0
- ksadk/kernel/control.py +153 -33
- ksadk/kernel/execution_grants.py +209 -2
- ksadk/kernel/execution_grants_memory.py +127 -8
- ksadk/kernel/execution_grants_postgres.py +188 -10
- ksadk/kernel/execution_grants_sqlite.py +235 -9
- ksadk/kernel/execution_host_ingress.py +1071 -0
- ksadk/kernel/history_projection.py +120 -0
- ksadk/kernel/ingress.py +117 -204
- ksadk/kernel/ingress_bootstrap.py +268 -0
- ksadk/kernel/legacy_history.py +179 -0
- ksadk/kernel/memory_store.py +38 -48
- ksadk/kernel/memory_store_projection.py +63 -0
- ksadk/kernel/postgres_event_log.py +181 -0
- ksadk/kernel/postgres_store.py +167 -198
- ksadk/kernel/readiness.py +125 -0
- ksadk/kernel/recovery.py +89 -3
- ksadk/kernel/recovery_projection.py +124 -0
- ksadk/kernel/runtime_checkpoint.py +34 -0
- ksadk/kernel/sql/001_agent_kernel.sql +5 -0
- ksadk/kernel/sqlite_schema.py +100 -0
- ksadk/kernel/sqlite_store.py +147 -216
- ksadk/kernel/store.py +47 -15
- ksadk/kernel/teams_artifact_tools.py +392 -0
- ksadk/kernel/teams_effects.py +851 -0
- ksadk/kernel/teams_execution_context.py +408 -0
- ksadk/kernel/teams_host_http.py +237 -0
- ksadk/kernel/teams_material_transport.py +245 -0
- ksadk/kernel/teams_materials.py +318 -0
- ksadk/kernel/teams_runtime_bootstrap.py +604 -0
- ksadk/kernel/teams_tool_runtime.py +273 -0
- ksadk/kernel/teams_workspace.py +259 -0
- ksadk/kernel/worker.py +323 -332
- ksadk/kernel/worker_controls.py +454 -0
- ksadk/kernel/worker_identity.py +27 -0
- ksadk/kernel/worker_interactions.py +197 -0
- ksadk/knowledge_base/client.py +15 -1
- ksadk/managed_runtime.py +29 -2
- ksadk/memory/adk/backends/http_ltm_backend.py +2 -1
- ksadk/memory/adk/backends/sdk_ltm_backend.py +15 -1
- ksadk/memory/adk/bounded_database_session_service.py +232 -0
- ksadk/memory/adk/resilient_session_service.py +115 -15
- ksadk/memory/adk/short_term_memory.py +8 -9
- ksadk/memory/extraction.py +120 -7
- ksadk/memory/providers/local_candidates.py +183 -0
- ksadk/memory/providers/local_sqlite.py +252 -78
- ksadk/memory/resolved_policy.py +12 -7
- ksadk/model_gateway/__init__.py +29 -0
- ksadk/model_gateway/client.py +253 -0
- ksadk/model_gateway/events.py +113 -0
- ksadk/model_gateway/matrix.py +297 -0
- ksadk/model_proxy/compat.py +670 -0
- ksadk/model_proxy/server.py +10 -7
- ksadk/model_proxy/transform.py +37 -9
- ksadk/plugins/bridges/dsh.py +18 -20
- ksadk/plugins/bridges/dsh_profile_manifest.py +36 -0
- ksadk/plugins/builtin_resources.py +199 -0
- ksadk/plugins/builtins.py +380 -186
- ksadk/plugins/context_contributor.py +15 -4
- ksadk/plugins/dsh_dependency_policy.py +41 -0
- ksadk/plugins/dsh_toolchain.py +24 -35
- ksadk/plugins/execution_host.py +8 -0
- ksadk/plugins/host.py +73 -5
- ksadk/plugins/kernel_adapter.py +44 -1
- ksadk/plugins/memory_provider.py +180 -0
- ksadk/plugins/providers/_process_group.py +50 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +24 -2
- ksadk/plugins/providers/bundles/dsh-knowledge/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-memory/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-skill-center/package.json +1 -1
- ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +1 -1
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +1 -1
- ksadk/plugins/providers/codex.py +42 -38
- ksadk/plugins/providers/codex_native.py +20 -0
- ksadk/plugins/providers/codex_skills.py +46 -0
- ksadk/plugins/providers/dsh_capabilities.py +16 -171
- ksadk/plugins/providers/dsh_connector_lease.py +225 -0
- ksadk/plugins/providers/dsh_mcp.py +163 -1
- ksadk/plugins/providers/harness.py +247 -34
- ksadk/plugins/providers/harness_context.py +61 -0
- ksadk/plugins/providers/harness_contributions.py +241 -0
- ksadk/plugins/providers/harness_delegation.py +91 -14
- ksadk/plugins/providers/harness_hooks.py +121 -0
- ksadk/plugins/providers/harness_managed.py +269 -175
- ksadk/plugins/providers/harness_tools.py +165 -36
- ksadk/plugins/providers/legacy_catalog.py +1 -1
- ksadk/plugins/providers/mcp_projection.py +100 -3
- ksadk/plugins/providers/platform_resources.py +16 -6
- ksadk/plugins/teams/application.py +3 -143
- ksadk/plugins/teams/build_artifacts.py +460 -0
- ksadk/plugins/teams/cloud_contract_fingerprints.py +22 -0
- ksadk/plugins/teams/cloud_contracts.py +928 -0
- ksadk/plugins/teams/cloud_permits.py +250 -0
- ksadk/plugins/teams/domain.py +201 -148
- ksadk/plugins/teams/effect_contracts.py +255 -0
- ksadk/plugins/teams/execution_projection.py +255 -0
- ksadk/plugins/teams/leader_decisions.py +106 -0
- ksadk/plugins/teams/postgres_store.py +299 -85
- ksadk/plugins/teams/release_contracts.py +60 -0
- ksadk/plugins/teams/runtime.py +116 -40
- ksadk/plugins/teams/store.py +214 -6
- ksadk/plugins/teams/task_decisions.py +21 -8
- ksadk/plugins/teams/tool_service.py +152 -0
- ksadk/plugins/teams/transport.py +8 -3
- ksadk/resource_runtime/skill_runtime.py +3 -0
- ksadk/resource_runtime/socket_server.py +11 -0
- ksadk/resource_runtime/worker.py +28 -4
- ksadk/runners/_adk_a2a_stream.py +122 -0
- ksadk/runners/_adk_canonical_stream.py +474 -0
- ksadk/runners/_langgraph_canonical_streams.py +527 -0
- ksadk/runners/_langgraph_runner_streams.py +6 -400
- ksadk/runners/_langgraph_tool_projection.py +86 -0
- ksadk/runners/_langgraph_tools_transformer.py +27 -0
- ksadk/runners/adk_runner.py +195 -290
- ksadk/runners/base_runner.py +6 -5
- ksadk/runners/langgraph_runner.py +82 -86
- ksadk/runners/utils/loader.py +28 -14
- ksadk/runtime/_runner_adapter/a2ui_surface.py +145 -0
- ksadk/runtime/_runner_adapter/gateway_approvals.py +93 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +83 -128
- ksadk/runtime/adapter.py +40 -3
- ksadk/runtime/conversation_execution.py +4 -2
- ksadk/runtime/executor.py +97 -7
- ksadk/runtime/factory.py +13 -6
- ksadk/runtime/framework_adapters.py +184 -10
- ksadk/runtime/hosted_finalizer.py +4 -6
- ksadk/runtime/launch.py +2 -0
- ksadk/runtime/runner_adapter.py +123 -13
- ksadk/runtime_context.py +2 -0
- ksadk/sandbox/backends/e2b.py +30 -4
- ksadk/sandbox/backends/local_process.py +10 -0
- ksadk/sandbox/e2b_connection.py +9 -1
- ksadk/sandbox/local_controls.py +32 -2
- ksadk/sandbox/local_launcher.py +34 -0
- ksadk/server/factory.py +178 -25
- ksadk/server/history_deadline.py +158 -0
- ksadk/server/history_page_cache.py +129 -0
- ksadk/server/routes/checkpoint_audit.py +88 -0
- ksadk/server/routes/checkpoint_listing.py +294 -0
- ksadk/server/routes/checkpoint_resolution.py +73 -20
- ksadk/server/routes/control.py +51 -3
- ksadk/server/routes/display_projection.py +154 -0
- ksadk/server/routes/kernel_ingress.py +17 -2
- ksadk/server/routes/misc.py +102 -12
- ksadk/server/routes/models.py +25 -1
- ksadk/server/routes/openai_compat.py +6 -0
- ksadk/server/routes/projection.py +159 -216
- ksadk/server/routes/run.py +6 -2
- ksadk/server/routes/sessions.py +301 -342
- ksadk/server/routes/streaming.py +68 -15
- ksadk/server/routes/tool_receipt_claim_routes.py +107 -0
- ksadk/server/routes/workspace.py +5 -2
- ksadk/server/static/assets/{ArtifactsPanel-C1YoaKhx.js → ArtifactsPanel-CILaW-fC.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-DHuAWfr5.js → CodeBlock-BVrNVtJC.js} +1 -1
- ksadk/server/static/assets/{MathMessageMarkdown-cvdrBwYr.js → MathMessageMarkdown-Dp7c4wWD.js} +1 -1
- ksadk/server/static/assets/{MermaidBlock-DeBkbJvI.js → MermaidBlock-Dxop8Rd7.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-oJj44_sV.js → NativeTerminalPanel-Cjjz8B30.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-Dhfv25o2.js → abnfDiagram-VCTEODGH-ckWDmOzw.js} +1 -1
- ksadk/server/static/assets/{arc-C35w-pn1.js → arc-DqxJede-.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-pn_DuLy6.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-FoMY_u6D.js → architectureDiagram-5GKGNRK7-SEQA4bVi.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-zD4Rt7oE.js → blockDiagram-NRAW4CY4-pM0B-6NJ.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BCtGGkhN.js → c4Diagram-UCG6FXSJ-m2tuCNVq.js} +1 -1
- ksadk/server/static/assets/channel-fySsnkft.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-BheGxKRQ.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-C2Ms13_k.js → chunk-5VM5RSS4-GJHDr9Ww.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-BoTj-qgx.js → chunk-F27PBJKO-CbAN45AV.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-DOMyyjHH.js → chunk-G27WJ6UU-D5ngbbzF.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-DbJJqDli.js → chunk-JWPE2WC7-Cc5IwuYC.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-CLuRCt_W.js → chunk-LCL6LL3I-CprgDDdJ.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-D0Dz847e.js → chunk-POPQ4Y6H-CDZhSy82.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-BWSRzEcd.js → chunk-RHFEMEQ7-CZchUNro.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-CPoHqkCY.js → chunk-SVP7TREG-BVruqk6T.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-Bx1jS-X-.js → chunk-XXDRQBXY-4ld9SxPN.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DSfLtNYm.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DSfLtNYm.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-LzyWm4m0.js → cose-bilkent-JH36ORCC-Dmyyk_Vp.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-PFWIGtvA.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-BlW_bHED.js → cynefinDiagram-5FMLGOSQ-BIyq3EJb.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-Bu_S86_i.js → dagre-3AP2YEHR-eBw08Qdk.js} +1 -1
- ksadk/server/static/assets/{dagre-B7nCawed.js → dagre-DfvnEf45.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-W_Zow2Km.js → diagram-S7CK7UJ4-CUSrE3Mg.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-CLIKAHON.js → diagram-UQ7AKVKN-Dq5Zw2gy.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-Dv6_QAlN.js → diagram-VSXAHHWV-DyUHfNwT.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-BWAWRDtZ.js → diagram-VX7I27RA-CREmM89O.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-XA_rWMss.js → diagram-Z3DM3KII-Dw9zhHUK.js} +1 -1
- ksadk/server/static/assets/{dist-CGt_UGeO.js → dist-0uJ4tdx6.js} +1 -1
- ksadk/server/static/assets/{dist-C0J5_3qM.js → dist-AEv-vXf-.js} +1 -1
- ksadk/server/static/assets/{dist-CIpWk_5f.js → dist-B49GzZDb.js} +1 -1
- ksadk/server/static/assets/{dist-B3bA05-e.js → dist-B8Myiet9.js} +1 -1
- ksadk/server/static/assets/{dist-DWfdmz9s.js → dist-BBsYarrI.js} +1 -1
- ksadk/server/static/assets/{dist-BLpRy_Yx.js → dist-BQMrUwdI.js} +1 -1
- ksadk/server/static/assets/{dist-CIRMvUM4.js → dist-BX_-FENW.js} +1 -1
- ksadk/server/static/assets/{dist-BdNFAney.js → dist-CD4L6ymH.js} +1 -1
- ksadk/server/static/assets/{dist-n3n6t_FO.js → dist-CHpW1BHj.js} +1 -1
- ksadk/server/static/assets/{dist-DppE_r-D.js → dist-CVPau3tn.js} +1 -1
- ksadk/server/static/assets/{dist-qe8xjesy.js → dist-C_qdxcSH.js} +1 -1
- ksadk/server/static/assets/{dist-2vL2sAdF.js → dist-Co68Apq4.js} +1 -1
- ksadk/server/static/assets/{dist-1nXCdsej.js → dist-Cp2PaOMY.js} +1 -1
- ksadk/server/static/assets/{dist-DNKuMzWc.js → dist-CuR5Bgme.js} +1 -1
- ksadk/server/static/assets/{dist-t6MzE0jU.js → dist-DDSyLEOX.js} +1 -1
- ksadk/server/static/assets/{dist-CPyieXEf.js → dist-DUxO0R99.js} +1 -1
- ksadk/server/static/assets/{dist-DyG6Ya-z.js → dist-QzbGrED_.js} +1 -1
- ksadk/server/static/assets/{dist-0X_j2Rcc.js → dist-bupkyEBZ.js} +1 -1
- ksadk/server/static/assets/{dist-mqD5TZup.js → dist-cxTGfdqx.js} +1 -1
- ksadk/server/static/assets/{dist-M-SrdhhZ.js → dist-hL5p1til.js} +1 -1
- ksadk/server/static/assets/{dist-D-92S-Fd.js → dist-lOy23y43.js} +1 -1
- ksadk/server/static/assets/{dist-BO_UwatH.js → dist-pq-nD33R.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-BfCzF1Xi.js → ebnfDiagram-PWID7BFC-BS5LdNoR.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-Dpz9_0zn.js → erDiagram-SSCWMZ5O-Bv1QtiY-.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-DsM4upQX.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-D5NA16k9.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DDaG1f4_.js → ganttDiagram-EL5Y4UJY-musi-e0G.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-DUCVz-ih.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DcxzRwYC.js → gitGraphDiagram-WWUBYQGX-DH37EGkG.js} +1 -1
- ksadk/server/static/assets/index-CZdkAvsv.js +244 -0
- ksadk/server/static/assets/index-CvXdHgF3.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-rw0ZNHZp.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-CYMdn6lQ.js → infoDiagram-RXCK75RN-C1kEtv2y.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CLHFAD6c.js → ishikawaDiagram-5VMMS53U-BCNPyMka.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-BHSKQGAl.js → journeyDiagram-EYS64GPL--cDbKnjP.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-D5W3tjTT.js → kanban-definition-3QL26DDD-R1rAVw2f.js} +1 -1
- ksadk/server/static/assets/{katex-mtjOi4KM.js → katex-BT-X-V7U.js} +1 -1
- ksadk/server/static/assets/{linear-BETqsxE_.js → linear-DTR631iG.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DPWrvs90.js → mermaid-parser.core-DfFzJhX8.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-CJG2mfBP.js → mindmap-definition-FBJOCRG2-D4by_rmk.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-Ca4Hvki9.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-GjAPNaqn.js → pegDiagram-XKGWAZYB-xWXPEh0c.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-hwiz6fbw.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-ToIewrqr.js → pieDiagram-E7YTZNPT-BudBDlCI.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-4d78KiEY.js → quadrantDiagram-AXDQQJYC-DXZ0PCJF.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-BR1oZXLM.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-DfgEISfX.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-CWOC3rdk.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CiRvHCLY.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-DSDDyJ2f.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-DKzx4xfv.js → railroadDiagram-O6MQD6OU-BdAYRYB8.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-CJYljxQS.js → requirementDiagram-EFPCY7ZU-CBBmaL70.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-DLU2fV1w.js → sankeyDiagram-P5KCCOFB-Doc3rdwp.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CVqysrA9.js → sequenceDiagram-WJ2MYXX4-0J8R8hWW.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-BUxfmTHc.js → sizeCapture-X5ZJPWSS-CfFLlWsK.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-CNFgYI9E.js → stateDiagram-HBIQ2CUA-jBwkjtTy.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-Cw1fPb5w.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-B6qCMe8N.js → swimlanes-XN3QIQJK-B0WxTQf8.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-Cc3JMBAF.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-CMD3x_U_.js → timeline-definition-24CTP7MA-BfCT_75A.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-C05SJHxo.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-BqNLAhc3.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-BqtUqvXM.js → vennDiagram-4TSXK5OY-9gX1VMDC.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-DokNktqB.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-B5KfDjI-.js → wardleyDiagram-VM6X3IG4-sEs_BP6O.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BaUlSZrn.js → xychartDiagram-S5SC5T6Z-Ll4jvUN9.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +8 -1
- ksadk/sessions/_checkpoint_resume_audit.py +44 -0
- ksadk/sessions/_in_memory_query.py +112 -0
- ksadk/sessions/_local_service_sync.py +157 -156
- ksadk/sessions/_local_state_sync.py +152 -0
- ksadk/sessions/_local_tables.py +2 -0
- ksadk/sessions/_postgres_checkpoint_read.py +323 -0
- ksadk/sessions/_postgres_checkpoint_stats.py +330 -0
- ksadk/sessions/_postgres_history_read.py +678 -0
- ksadk/sessions/_postgres_schema.py +44 -2
- ksadk/sessions/_postgres_tables.py +2 -0
- ksadk/sessions/_postgres_tool_receipts.py +169 -0
- ksadk/sessions/_resilient_read.py +630 -0
- ksadk/sessions/_tool_receipt_sync.py +119 -0
- ksadk/sessions/agent_identity.py +16 -0
- ksadk/sessions/base.py +278 -0
- ksadk/sessions/db_budget.py +332 -0
- ksadk/sessions/errors.py +12 -0
- ksadk/sessions/history_budget.py +36 -0
- ksadk/sessions/history_identity.py +27 -0
- ksadk/sessions/in_memory.py +376 -113
- ksadk/sessions/local_service.py +345 -0
- ksadk/sessions/postgres_service.py +515 -549
- ksadk/sessions/resilience.py +21 -14
- ksadk/sessions/resilient.py +611 -504
- ksadk/skills/loader.py +44 -2
- ksadk/skills/runtime/backends/e2b.py +17 -14
- ksadk/skills/service_client.py +6 -1
- ksadk/studio/a2a_bindings.py +407 -0
- ksadk/studio/a2a_orchestration.py +178 -0
- ksadk/studio/active_run_output.py +92 -0
- ksadk/studio/activity_records.py +542 -0
- ksadk/studio/agent_configuration.py +173 -0
- ksadk/studio/api.py +219 -389
- ksadk/studio/api_a2a_routes.py +93 -0
- ksadk/studio/api_catalog_routes.py +13 -0
- ksadk/studio/api_cloud_chat_routes.py +382 -0
- ksadk/studio/api_contracts.py +23 -0
- ksadk/studio/attachment_store.py +14 -2
- ksadk/studio/build_resolution.py +117 -0
- ksadk/studio/build_singleflight.py +37 -0
- ksadk/studio/builder.py +20 -2
- ksadk/studio/capabilities.py +8 -3
- ksadk/studio/cloud.py +83 -377
- ksadk/studio/cloud_gateways.py +424 -0
- ksadk/studio/cloud_shared_web.py +7 -0
- ksadk/studio/codex_agent_service.py +17 -0
- ksadk/studio/codex_manifest.py +5 -1
- ksadk/studio/codex_provider_build.py +65 -0
- ksadk/studio/compiler.py +102 -6
- ksadk/studio/contract_base.py +19 -0
- ksadk/studio/contracts.py +231 -201
- ksadk/studio/dsh_application.py +58 -12
- ksadk/studio/dsh_capability_service.py +17 -171
- ksadk/studio/dsh_mcp_transport.py +191 -0
- ksadk/studio/event_store.py +17 -0
- ksadk/studio/execution_host.py +84 -11
- ksadk/studio/framework_run.py +18 -0
- ksadk/studio/interaction_submission.py +265 -0
- ksadk/studio/kernel_registry.py +12 -1
- ksadk/studio/lifecycle_status.py +43 -0
- ksadk/studio/model_buffered_wire.py +227 -0
- ksadk/studio/model_chat_tools.py +143 -0
- ksadk/studio/model_client.py +1241 -761
- ksadk/studio/model_content_stream.py +129 -0
- ksadk/studio/model_credentials.py +196 -0
- ksadk/studio/model_profile_service.py +65 -31
- ksadk/studio/model_request_projection.py +284 -0
- ksadk/studio/model_responses_content.py +296 -0
- ksadk/studio/model_usage.py +109 -0
- ksadk/studio/model_wire_failure.py +110 -0
- ksadk/studio/model_wire_identity.py +138 -0
- ksadk/studio/model_wire_snapshot.py +45 -0
- ksadk/studio/model_wire_tools.py +283 -0
- ksadk/studio/native_acceptance.py +68 -0
- ksadk/studio/native_approval.py +111 -0
- ksadk/studio/native_browser_port.py +861 -0
- ksadk/studio/native_child_mcp.py +87 -0
- ksadk/studio/native_child_publication.py +68 -0
- ksadk/studio/native_child_skills.py +72 -0
- ksadk/studio/native_child_workspace.py +176 -0
- ksadk/studio/native_command_policy.py +53 -0
- ksadk/studio/native_context_port.py +326 -0
- ksadk/studio/native_context_registration.py +54 -0
- ksadk/studio/native_document_input.py +88 -0
- ksadk/studio/native_final_shape.py +852 -0
- ksadk/studio/native_gateway_stream.py +69 -0
- ksadk/studio/native_hook_port.py +145 -0
- ksadk/studio/native_hook_registration.py +70 -0
- ksadk/studio/native_inventory_disclosure.py +223 -0
- ksadk/studio/native_kernel_admission.py +84 -0
- ksadk/studio/native_language_diagnostics.py +320 -0
- ksadk/studio/native_mcp_disclosure.py +198 -0
- ksadk/studio/native_mcp_tool_port.py +219 -0
- ksadk/studio/native_memory_acceptance.py +362 -0
- ksadk/studio/native_memory_context.py +266 -0
- ksadk/studio/native_memory_flush.py +174 -0
- ksadk/studio/native_memory_sources.py +80 -0
- ksadk/studio/native_memory_tool_port.py +360 -0
- ksadk/studio/native_memory_turn.py +183 -0
- ksadk/studio/native_model_port.py +765 -0
- ksadk/studio/native_model_recovery.py +126 -0
- ksadk/studio/native_options.py +75 -0
- ksadk/studio/native_output_fields.py +58 -0
- ksadk/studio/native_planning.py +156 -0
- ksadk/studio/native_product.py +988 -0
- ksadk/studio/native_read_budget.py +426 -0
- ksadk/studio/native_recovery.py +171 -0
- ksadk/studio/native_scope.py +35 -0
- ksadk/studio/native_session_context_port.py +141 -0
- ksadk/studio/native_session_engine.py +14 -0
- ksadk/studio/native_skill_source.py +74 -0
- ksadk/studio/native_subagent_port.py +700 -0
- ksadk/studio/native_terminal_recovery.py +95 -0
- ksadk/studio/native_test_action.py +610 -0
- ksadk/studio/native_tool_port.py +477 -0
- ksadk/studio/native_user_input.py +60 -0
- ksadk/studio/native_verification_presentation.py +65 -0
- ksadk/studio/native_workspace_diagnostics.py +183 -0
- ksadk/studio/native_workspace_port.py +43 -0
- ksadk/studio/operations.py +34 -9
- ksadk/studio/plugin_composition.py +26 -10
- ksadk/studio/plugin_kernel_adapter.py +75 -1
- ksadk/studio/plugin_native_activation.py +163 -0
- ksadk/studio/plugin_runtime.py +639 -119
- ksadk/studio/plugin_runtime_admission.py +57 -0
- ksadk/studio/presentation.py +92 -0
- ksadk/studio/public_output_store.py +69 -0
- ksadk/studio/resource_authority.py +11 -3
- ksadk/studio/resource_catalog.py +8 -12
- ksadk/studio/run_activity.py +338 -385
- ksadk/studio/run_controls.py +185 -0
- ksadk/studio/run_service.py +908 -319
- ksadk/studio/run_submission_receipts.py +68 -0
- ksadk/studio/runtime_catalog.py +23 -4
- ksadk/studio/runtime_prewarm.py +22 -0
- ksadk/studio/scheduler_assistant.py +4 -1
- ksadk/studio/service.py +356 -282
- ksadk/studio/service_settings.py +229 -0
- ksadk/studio/shared_web.py +393 -169
- ksadk/studio/shared_web_response_events.py +180 -0
- ksadk/studio/shared_web_run_state.py +133 -0
- ksadk/studio/skill_discovery.py +305 -3
- ksadk/studio/static/assets/ChannelsPage-xX5HlMId.js +8 -0
- ksadk/studio/static/assets/{CodeBlock-DMIf4h33-UIqo-BPW.js → CodeBlock-DMIf4h33-CkufcPYo.js} +3 -3
- ksadk/studio/static/assets/{MathMessageMarkdown-BesSeCYl-OhNwH4K9.js → MathMessageMarkdown-BesSeCYl-DYG1Wzbx.js} +1 -1
- ksadk/studio/static/assets/{MessageMarkdown-CwMTvgFj-g0I-I4dD.js → MessageMarkdown-CwMTvgFj-BeyLyQHu.js} +2 -2
- ksadk/studio/static/assets/TeamsPage-CCaaGaVp.js +3 -0
- ksadk/studio/static/assets/index-C7DNDik2.css +2 -0
- ksadk/studio/static/assets/index-Cy68gTlH.js +265 -0
- ksadk/studio/static/index.html +3 -3
- ksadk/studio/teams_catalog.py +33 -0
- ksadk/studio/teams_installation.py +4 -1
- ksadk/studio/teams_node.py +4 -0
- ksadk/studio/teams_node_factory.py +558 -0
- ksadk/studio/teams_node_runner.py +3 -0
- ksadk/studio/teams_node_v1.py +935 -0
- ksadk/studio/teams_remote.py +259 -37
- ksadk/studio/templates.py +1 -0
- ksadk/studio/workspace_plugins.py +3 -1
- ksadk/tools/a2a_policy.py +5 -0
- ksadk/tools/artifact_observation.py +53 -0
- ksadk/tools/captured_source_artifact.py +68 -0
- ksadk/tools/schema.py +34 -0
- ksadk/toolsets/__init__.py +82 -88
- ksadk/toolsets/a2a.py +89 -89
- ksadk/toolsets/sandbox.py +377 -6
- ksadk/toolsets/skill_management.py +315 -0
- ksadk/toolsets/tool_selection.py +94 -0
- ksadk/toolsets/workspace.py +342 -324
- ksadk/toolsets/workspace_editing.py +291 -0
- ksadk/toolsets/workspace_model_observation.py +104 -0
- ksadk/toolsets/workspace_mutation.py +75 -0
- ksadk/toolsets/workspace_read_budget.py +216 -0
- ksadk/toolsets/workspace_search.py +192 -0
- ksadk/toolsets/workspace_search_process.py +129 -0
- ksadk/toolsets/workspace_state.py +6 -0
- ksadk/version.py +1 -1
- ksadk_runtime_common/http_headers.py +18 -0
- ksadk_runtime_common/workspace_files/router.py +2 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +0 -1
- ksadk/server/static/assets/channel-_KDifNys.js +0 -1
- ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +0 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +0 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +0 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +0 -1
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +0 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +0 -1
- ksadk/server/static/assets/index-BMd2mucF.css +0 -2
- ksadk/server/static/assets/index-D5qyyhTH.js +0 -244
- ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +0 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +0 -1
- ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +0 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +0 -1
- ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +0 -1
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +0 -1
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +0 -1
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +0 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +0 -8
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +0 -1
- ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +0 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +0 -1
- ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +0 -8
- ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +0 -3
- ksadk/studio/static/assets/index-CfEnms6Y.css +0 -2
- ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
"""Managed A2A invocation lifecycle over the canonical store and Space wire port.
|
|
2
|
+
|
|
3
|
+
The canonical store allocates sequence numbers. The service keeps transient
|
|
4
|
+
protocol state only; it never creates another event log or stores DI objects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from contextlib import AsyncExitStack, asynccontextmanager
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
|
|
15
|
+
from a2a.types import Message, StreamResponse, Task, TaskState, TaskStatusUpdateEvent
|
|
16
|
+
|
|
17
|
+
from ksadk.a2a._invocation_state import (
|
|
18
|
+
A2AInvocationEventStore as A2AInvocationEventStore,
|
|
19
|
+
)
|
|
20
|
+
from ksadk.a2a._invocation_state import (
|
|
21
|
+
A2AInvocationTimeouts as A2AInvocationTimeouts,
|
|
22
|
+
)
|
|
23
|
+
from ksadk.a2a._invocation_state import (
|
|
24
|
+
_CancelDelivery,
|
|
25
|
+
_ServiceCheckpoint,
|
|
26
|
+
_State,
|
|
27
|
+
)
|
|
28
|
+
from ksadk.a2a.invocation_policy import A2AApprovalRequired, A2AInvocationPolicy
|
|
29
|
+
from ksadk.a2a.invocation_types import (
|
|
30
|
+
A2ACancelResult,
|
|
31
|
+
A2AContinuationRequest,
|
|
32
|
+
A2AInvocationError,
|
|
33
|
+
A2AInvocationOutputRef,
|
|
34
|
+
A2AInvocationRequest,
|
|
35
|
+
A2AInvocationSnapshot,
|
|
36
|
+
)
|
|
37
|
+
from ksadk.a2a.message_digest import canonical_message_sha256
|
|
38
|
+
from ksadk.a2a.presentation import (
|
|
39
|
+
ExecutionScopeDescriptor,
|
|
40
|
+
PublicAgentRef,
|
|
41
|
+
ScopeCancelState,
|
|
42
|
+
ScopeOutputRef,
|
|
43
|
+
ScopeTriggerRef,
|
|
44
|
+
validate_scope_transition,
|
|
45
|
+
)
|
|
46
|
+
from ksadk.events.adapters.a2a import A2AAdapterContext, A2AEventAdapter
|
|
47
|
+
from ksadk.events.canonical import (
|
|
48
|
+
ContinuationCreated,
|
|
49
|
+
ContinuationResumed,
|
|
50
|
+
ErrorInfo,
|
|
51
|
+
InteractionRequested,
|
|
52
|
+
InteractionResolved,
|
|
53
|
+
ItemCompleted,
|
|
54
|
+
ItemFailed,
|
|
55
|
+
ItemSnapshotReplaced,
|
|
56
|
+
ItemStarted,
|
|
57
|
+
SourceRef,
|
|
58
|
+
parse_runtime_event,
|
|
59
|
+
)
|
|
60
|
+
from ksadk.events.content import ContentSnapshot, DataContent
|
|
61
|
+
from ksadk.events.identity import stable_event_id, stable_item_id, stable_part_id
|
|
62
|
+
|
|
63
|
+
from ksadk.a2a.invocation_publication import _InvocationPublicationMixin
|
|
64
|
+
|
|
65
|
+
logger = logging.getLogger(__name__)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class A2AInvocationService(_InvocationPublicationMixin):
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
*,
|
|
72
|
+
space_client,
|
|
73
|
+
event_store: A2AInvocationEventStore,
|
|
74
|
+
policy: A2AInvocationPolicy,
|
|
75
|
+
timeouts=None,
|
|
76
|
+
clock=None,
|
|
77
|
+
event_dispatcher=None,
|
|
78
|
+
trigger_resolver=None,
|
|
79
|
+
checkpoint_store=None,
|
|
80
|
+
recover_checkpoint_leases=False,
|
|
81
|
+
):
|
|
82
|
+
self._space_client = space_client
|
|
83
|
+
self._control_plane = space_client.control_plane
|
|
84
|
+
self._event_store, self._policy = event_store, policy
|
|
85
|
+
self._timeouts = timeouts or A2AInvocationTimeouts()
|
|
86
|
+
self._clock = clock or time.time
|
|
87
|
+
self._dispatcher = event_dispatcher
|
|
88
|
+
self._trigger_resolver = trigger_resolver
|
|
89
|
+
self._states: dict[str, _State] = {}
|
|
90
|
+
self._recover_checkpoint_leases = recover_checkpoint_leases
|
|
91
|
+
self._checkpoint_store = checkpoint_store
|
|
92
|
+
self._checkpoint_leases = {}
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def durable_recovery_available(self) -> bool:
|
|
96
|
+
return self._checkpoint_store is not None
|
|
97
|
+
|
|
98
|
+
async def _save_state(self, state):
|
|
99
|
+
lease = self._checkpoint_leases.get(state.handle.scope_id)
|
|
100
|
+
if lease is None:
|
|
101
|
+
return
|
|
102
|
+
from ksadk.a2a.invocation_checkpoint import A2AInvocationCheckpoint
|
|
103
|
+
|
|
104
|
+
recovery = _ServiceCheckpoint(
|
|
105
|
+
descriptor=state.descriptor,
|
|
106
|
+
adapter=state.adapter.checkpoint(state.context) if state.context else None,
|
|
107
|
+
snapshot=state.snapshot,
|
|
108
|
+
tokens=state.tokens,
|
|
109
|
+
refs=list(state.refs.values()),
|
|
110
|
+
continuation_id=state.continuation_id,
|
|
111
|
+
pending_continuation=state.pending_continuation,
|
|
112
|
+
pending_input=state.pending_input,
|
|
113
|
+
source=state.source,
|
|
114
|
+
cursor=state.cursor,
|
|
115
|
+
last_event_id=state.last_event_id,
|
|
116
|
+
pending=[event.model_dump(mode="json") for event in state.pending],
|
|
117
|
+
)
|
|
118
|
+
await lease.save(
|
|
119
|
+
A2AInvocationCheckpoint(
|
|
120
|
+
request=state.request,
|
|
121
|
+
handle=state.handle,
|
|
122
|
+
send_entered=state.send_entered,
|
|
123
|
+
state=recovery.model_dump(mode="json", by_alias=True),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def _restore_state(self, checkpoint):
|
|
128
|
+
recovery = _ServiceCheckpoint.model_validate(checkpoint.state)
|
|
129
|
+
state = _State(
|
|
130
|
+
request=checkpoint.request,
|
|
131
|
+
handle=checkpoint.handle,
|
|
132
|
+
descriptor=recovery.descriptor,
|
|
133
|
+
snapshot=recovery.snapshot,
|
|
134
|
+
send_entered=checkpoint.send_entered,
|
|
135
|
+
tokens=recovery.tokens,
|
|
136
|
+
refs={(ref.item_id, ref.part_id): ref for ref in recovery.refs},
|
|
137
|
+
continuation_id=recovery.continuation_id,
|
|
138
|
+
pending_continuation=recovery.pending_continuation,
|
|
139
|
+
pending_input=recovery.pending_input,
|
|
140
|
+
source=recovery.source,
|
|
141
|
+
cursor=recovery.cursor,
|
|
142
|
+
last_event_id=recovery.last_event_id,
|
|
143
|
+
pending=[parse_runtime_event(event) for event in recovery.pending],
|
|
144
|
+
)
|
|
145
|
+
if recovery.adapter is not None:
|
|
146
|
+
state.adapter, state.context = A2AEventAdapter.restore_checkpoint(recovery.adapter)
|
|
147
|
+
if (
|
|
148
|
+
state.descriptor.scope_id != state.handle.scope_id
|
|
149
|
+
or state.descriptor.parent_scope_id != state.handle.parent_scope_id
|
|
150
|
+
or state.descriptor.binding_id != state.request.spec.binding_id
|
|
151
|
+
or state.descriptor.agent.id != state.request.spec.target_agent_id
|
|
152
|
+
or state.descriptor.agent.version_id != state.request.spec.expected_version_id
|
|
153
|
+
or state.descriptor.trigger_ref.call_id != state.handle.call_id
|
|
154
|
+
or any(ref.scope_id != state.handle.scope_id for ref in state.refs.values())
|
|
155
|
+
or (
|
|
156
|
+
state.context is not None
|
|
157
|
+
and (
|
|
158
|
+
state.context.scope_id != state.handle.scope_id
|
|
159
|
+
or state.context.run_id != state.handle.run_id
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
or any(
|
|
163
|
+
event.scope_id != state.handle.scope_id or event.run_id != state.handle.run_id
|
|
164
|
+
for event in state.pending
|
|
165
|
+
)
|
|
166
|
+
):
|
|
167
|
+
raise ValueError("A2A_CHECKPOINT_IDENTITY_CONFLICT")
|
|
168
|
+
if (
|
|
169
|
+
state.descriptor.status in {"completed", "failed", "cancelled"}
|
|
170
|
+
and state.snapshot is None
|
|
171
|
+
):
|
|
172
|
+
self._snapshot(state)
|
|
173
|
+
return state
|
|
174
|
+
|
|
175
|
+
@asynccontextmanager
|
|
176
|
+
async def _operation(self, handle):
|
|
177
|
+
if self._checkpoint_store is None:
|
|
178
|
+
yield
|
|
179
|
+
return
|
|
180
|
+
options = {"wait_for_recovery": True} if self._recover_checkpoint_leases else {}
|
|
181
|
+
async with self._checkpoint_store.lease(handle.scope_id, **options) as lease:
|
|
182
|
+
self._checkpoint_leases[handle.scope_id] = lease
|
|
183
|
+
ready = False
|
|
184
|
+
try:
|
|
185
|
+
saved = await lease.load()
|
|
186
|
+
if saved is not None:
|
|
187
|
+
self._states[handle.scope_id] = self._restore_state(saved)
|
|
188
|
+
state = self._state(handle)
|
|
189
|
+
await self._flush_pending(state)
|
|
190
|
+
else:
|
|
191
|
+
self._states.pop(handle.scope_id, None)
|
|
192
|
+
ready = True
|
|
193
|
+
yield
|
|
194
|
+
finally:
|
|
195
|
+
try:
|
|
196
|
+
state = self._states.get(handle.scope_id)
|
|
197
|
+
if ready and state is not None:
|
|
198
|
+
await self._save_state(state)
|
|
199
|
+
finally:
|
|
200
|
+
self._checkpoint_leases.pop(handle.scope_id, None)
|
|
201
|
+
|
|
202
|
+
@classmethod
|
|
203
|
+
def from_space_client(cls, space_client, **kwargs):
|
|
204
|
+
kwargs.setdefault("event_dispatcher", space_client.event_dispatcher)
|
|
205
|
+
return cls(space_client=space_client, **kwargs)
|
|
206
|
+
|
|
207
|
+
def handle(self, scope_id):
|
|
208
|
+
return self._states[scope_id].handle
|
|
209
|
+
|
|
210
|
+
async def _terminal_replay(self, state):
|
|
211
|
+
"""Restore the caller's result from existing facts, without another send."""
|
|
212
|
+
events = [
|
|
213
|
+
event
|
|
214
|
+
for event in await self._event_store.list(
|
|
215
|
+
state.handle.session_id, run_id=state.handle.run_id
|
|
216
|
+
)
|
|
217
|
+
if event.run_id == state.handle.run_id and event.scope_id == state.handle.scope_id
|
|
218
|
+
]
|
|
219
|
+
completed = {event.item_id: event for event in events if isinstance(event, ItemCompleted)}
|
|
220
|
+
descriptor_item = stable_item_id("a2a", state.handle.scope_id, "execution.scope")
|
|
221
|
+
descriptor = completed.get(descriptor_item)
|
|
222
|
+
if descriptor is None or not any(
|
|
223
|
+
isinstance(part, DataContent)
|
|
224
|
+
and isinstance(part.data, dict)
|
|
225
|
+
and ExecutionScopeDescriptor.model_validate(part.data) == state.descriptor
|
|
226
|
+
for part in descriptor.snapshot.parts
|
|
227
|
+
):
|
|
228
|
+
raise ValueError("A2A_TERMINAL_RECOVERY_FACT_REQUIRED")
|
|
229
|
+
for ref in state.refs.values():
|
|
230
|
+
item = completed.get(ref.item_id)
|
|
231
|
+
if item is None or (
|
|
232
|
+
ref.part_id is not None
|
|
233
|
+
and not any(part.part_id == ref.part_id for part in item.snapshot.parts)
|
|
234
|
+
):
|
|
235
|
+
raise ValueError("A2A_TERMINAL_RECOVERY_FACT_REQUIRED")
|
|
236
|
+
return events
|
|
237
|
+
|
|
238
|
+
async def original_request(self, handle):
|
|
239
|
+
"""Recover the exact initial request; never refresh its deadline or token.
|
|
240
|
+
|
|
241
|
+
The same lease and identity checks as invoke/get protect the lookup.
|
|
242
|
+
None means no durable/local invocation exists, not an unknown send.
|
|
243
|
+
"""
|
|
244
|
+
async with self._operation(handle):
|
|
245
|
+
if handle.scope_id not in self._states:
|
|
246
|
+
return None
|
|
247
|
+
return self._state(handle).request
|
|
248
|
+
|
|
249
|
+
async def pending_continuation(self, handle):
|
|
250
|
+
"""Read the immutable supplemental request persisted before approval."""
|
|
251
|
+
async with self._operation(handle):
|
|
252
|
+
if handle.scope_id not in self._states:
|
|
253
|
+
return None
|
|
254
|
+
state = self._state(handle)
|
|
255
|
+
if state.snapshot is not None and state.snapshot.terminal:
|
|
256
|
+
return None
|
|
257
|
+
return state.pending_continuation
|
|
258
|
+
|
|
259
|
+
async def pending_input_request(self, handle):
|
|
260
|
+
"""Recover the exact persisted prompt, without another remote send."""
|
|
261
|
+
async with self._operation(handle):
|
|
262
|
+
state = self._state(handle)
|
|
263
|
+
if state.snapshot is None or state.snapshot.status not in {
|
|
264
|
+
"input_required",
|
|
265
|
+
"auth_required",
|
|
266
|
+
}:
|
|
267
|
+
return None
|
|
268
|
+
event = state.pending_input
|
|
269
|
+
if event is None or event.run_id != handle.run_id or event.scope_id != handle.scope_id:
|
|
270
|
+
raise ValueError("A2A_INPUT_RECOVERY_FACT_REQUIRED")
|
|
271
|
+
return event
|
|
272
|
+
|
|
273
|
+
def snapshot(self, handle):
|
|
274
|
+
"""Return the latest local snapshot without performing network I/O."""
|
|
275
|
+
return self._state(handle).snapshot
|
|
276
|
+
|
|
277
|
+
async def replay(self, handle):
|
|
278
|
+
return tuple(
|
|
279
|
+
event
|
|
280
|
+
for event in await self._event_store.list(handle.session_id, run_id=handle.run_id)
|
|
281
|
+
if event.scope_id == handle.scope_id
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
async def _timed(self, awaitable, deadline, segment):
|
|
285
|
+
remaining = deadline - self._clock()
|
|
286
|
+
if remaining <= 0:
|
|
287
|
+
if hasattr(awaitable, "close"):
|
|
288
|
+
awaitable.close()
|
|
289
|
+
raise TimeoutError("A2A_OVERALL_TIMEOUT")
|
|
290
|
+
limit = getattr(self._timeouts, segment + "_seconds")
|
|
291
|
+
try:
|
|
292
|
+
return await asyncio.wait_for(awaitable, min(remaining, limit))
|
|
293
|
+
except asyncio.TimeoutError as exc:
|
|
294
|
+
code = "A2A_OVERALL_TIMEOUT" if remaining <= limit else f"A2A_{segment.upper()}_TIMEOUT"
|
|
295
|
+
raise TimeoutError(code) from exc
|
|
296
|
+
|
|
297
|
+
async def _authorize(self, request, route=None, *, deadline=None):
|
|
298
|
+
decision = await self._timed(
|
|
299
|
+
self._policy.evaluate(request, route_kind=route),
|
|
300
|
+
request.deadline_unix_seconds if deadline is None else deadline,
|
|
301
|
+
"prepare",
|
|
302
|
+
)
|
|
303
|
+
if decision.action == "approval_required":
|
|
304
|
+
raise A2AApprovalRequired(decision.reason_code or "A2A_APPROVAL_REQUIRED")
|
|
305
|
+
if decision.action != "allow":
|
|
306
|
+
raise PermissionError(decision.reason_code or "A2A_APPROVAL_REQUIRED")
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def _validate_prepared(request, prepared):
|
|
310
|
+
prepared.target.assert_matches(
|
|
311
|
+
target_agent_id=request.spec.target_agent_id,
|
|
312
|
+
expected_version_id=request.spec.expected_version_id,
|
|
313
|
+
)
|
|
314
|
+
if prepared.route.kind != "hosted_gateway":
|
|
315
|
+
raise PermissionError("A2A_HOSTED_ROUTE_REQUIRED")
|
|
316
|
+
|
|
317
|
+
async def invoke(
|
|
318
|
+
self, request: A2AInvocationRequest, *, trigger_ref: ScopeTriggerRef | None = None
|
|
319
|
+
):
|
|
320
|
+
async with self._operation(request.handle):
|
|
321
|
+
async for event in self._invoke(request, trigger_ref=trigger_ref):
|
|
322
|
+
yield event
|
|
323
|
+
|
|
324
|
+
async def _invoke(
|
|
325
|
+
self, request: A2AInvocationRequest, *, trigger_ref: ScopeTriggerRef | None = None
|
|
326
|
+
):
|
|
327
|
+
if request.spec.expected_version_id is None:
|
|
328
|
+
raise ValueError("A2A_VERSION_PIN_REQUIRED")
|
|
329
|
+
trigger = trigger_ref or (
|
|
330
|
+
self._trigger_resolver(request) if self._trigger_resolver else None
|
|
331
|
+
)
|
|
332
|
+
if not isinstance(trigger, ScopeTriggerRef):
|
|
333
|
+
raise ValueError("A2A_TRIGGER_REF_REQUIRED")
|
|
334
|
+
if (
|
|
335
|
+
trigger.scope_id != request.handle.parent_scope_id
|
|
336
|
+
or trigger.call_id != request.handle.call_id
|
|
337
|
+
):
|
|
338
|
+
raise ValueError("A2A_TRIGGER_REF_CONFLICT")
|
|
339
|
+
previous = self._states.get(request.scope_id)
|
|
340
|
+
if previous:
|
|
341
|
+
if previous.request != request or previous.descriptor.trigger_ref != trigger:
|
|
342
|
+
raise ValueError("A2A_INVOCATION_IDENTITY_CONFLICT")
|
|
343
|
+
if previous.snapshot and previous.snapshot.terminal:
|
|
344
|
+
for event in await self._terminal_replay(previous):
|
|
345
|
+
yield event
|
|
346
|
+
return
|
|
347
|
+
if previous.send_entered or previous.handle.remote_task_id is not None:
|
|
348
|
+
# A durable possible-send receipt forbids automatic re-send.
|
|
349
|
+
if previous.handle.platform_task_id is not None:
|
|
350
|
+
try:
|
|
351
|
+
async for event in self._recover_bound(
|
|
352
|
+
previous, request.deadline_unix_seconds
|
|
353
|
+
):
|
|
354
|
+
yield event
|
|
355
|
+
except ValueError as exc:
|
|
356
|
+
if exc.args != ("A2A_REMOTE_BINDING_UNKNOWN",):
|
|
357
|
+
raise
|
|
358
|
+
self._snapshot(
|
|
359
|
+
previous,
|
|
360
|
+
"unknown",
|
|
361
|
+
A2AInvocationError(
|
|
362
|
+
code="A2A_SEND_OUTCOME_UNKNOWN",
|
|
363
|
+
message="Remote send outcome is unknown",
|
|
364
|
+
),
|
|
365
|
+
)
|
|
366
|
+
return
|
|
367
|
+
if previous.snapshot and previous.snapshot.terminal:
|
|
368
|
+
return
|
|
369
|
+
descriptor = ExecutionScopeDescriptor(
|
|
370
|
+
scope_id=request.scope_id,
|
|
371
|
+
parent_scope_id=request.handle.parent_scope_id,
|
|
372
|
+
trigger_ref=trigger,
|
|
373
|
+
binding_id=request.spec.binding_id,
|
|
374
|
+
agent=PublicAgentRef(
|
|
375
|
+
id=request.spec.target_agent_id,
|
|
376
|
+
name=request.spec.local_name,
|
|
377
|
+
version_id=request.spec.expected_version_id,
|
|
378
|
+
),
|
|
379
|
+
status="submitted",
|
|
380
|
+
started_at=float(self._clock()),
|
|
381
|
+
cancel=ScopeCancelState(capability="unknown"),
|
|
382
|
+
)
|
|
383
|
+
state = previous or _State(request=request, handle=request.handle, descriptor=descriptor)
|
|
384
|
+
state.active = True
|
|
385
|
+
self._states[request.scope_id] = state
|
|
386
|
+
state.tokens[request.idempotency_token] = request.message_sha256
|
|
387
|
+
try:
|
|
388
|
+
await self._save_state(state)
|
|
389
|
+
# Save the immutable original request and explicit not-sent state
|
|
390
|
+
# before a local approval pause. Recovery can cancel it locally or
|
|
391
|
+
# re-authorize this exact request without inventing a new attempt.
|
|
392
|
+
await self._authorize(request)
|
|
393
|
+
prepared = await self._timed(
|
|
394
|
+
self._control_plane.prepare_call(
|
|
395
|
+
space_id=request.spec.space_id,
|
|
396
|
+
target_agent_id=request.spec.target_agent_id,
|
|
397
|
+
expected_version_id=request.spec.expected_version_id,
|
|
398
|
+
message_id=request.message.message_id,
|
|
399
|
+
message_sha256=request.message_sha256,
|
|
400
|
+
idempotency_token=request.idempotency_token,
|
|
401
|
+
),
|
|
402
|
+
request.deadline_unix_seconds,
|
|
403
|
+
"prepare",
|
|
404
|
+
)
|
|
405
|
+
self._validate_prepared(request, prepared)
|
|
406
|
+
await self._authorize(request, prepared.route.kind)
|
|
407
|
+
state.handle = state.handle.with_platform_task(prepared.platform_task_id)
|
|
408
|
+
if prepared.remote_task is not None:
|
|
409
|
+
state.handle = state.handle.bind_remote_task(
|
|
410
|
+
prepared.remote_task.remote_task_id, prepared.remote_task.remote_context_id
|
|
411
|
+
)
|
|
412
|
+
descriptor_id = stable_item_id("a2a", state.handle.scope_id, "execution.scope")
|
|
413
|
+
facts = await self._event_store.list(
|
|
414
|
+
state.handle.session_id, run_id=state.handle.run_id
|
|
415
|
+
)
|
|
416
|
+
if not any(getattr(event, "item_id", None) == descriptor_id for event in facts):
|
|
417
|
+
submitted = await self._descriptor_event(state, "submitted", initial=True)
|
|
418
|
+
if prepared.remote_task is None:
|
|
419
|
+
yield submitted
|
|
420
|
+
if prepared.remote_task is not None:
|
|
421
|
+
# Durable receipt already bound: reconcile rather than submit twice.
|
|
422
|
+
async for event in self._recover_bound(state, request.deadline_unix_seconds):
|
|
423
|
+
yield event
|
|
424
|
+
else:
|
|
425
|
+
async for event in self._send(
|
|
426
|
+
state,
|
|
427
|
+
prepared,
|
|
428
|
+
request.message.to_wire(),
|
|
429
|
+
request.deadline_unix_seconds,
|
|
430
|
+
):
|
|
431
|
+
yield event
|
|
432
|
+
except Exception:
|
|
433
|
+
if state.send_entered and state.snapshot is None:
|
|
434
|
+
self._snapshot(
|
|
435
|
+
state,
|
|
436
|
+
"unknown",
|
|
437
|
+
A2AInvocationError(
|
|
438
|
+
code="A2A_SEND_OUTCOME_UNKNOWN",
|
|
439
|
+
message="Remote send outcome is unknown",
|
|
440
|
+
retryable=False,
|
|
441
|
+
),
|
|
442
|
+
)
|
|
443
|
+
if state.snapshot is not None and state.snapshot.status == "unknown":
|
|
444
|
+
# The local call failed; the remote outcome is still unknown.
|
|
445
|
+
# Publish safe facts without converting that uncertainty into
|
|
446
|
+
# a remote cancellation or permitting another send.
|
|
447
|
+
item = stable_item_id("a2a", state.handle.scope_id, "outcome-error")
|
|
448
|
+
common = dict(
|
|
449
|
+
schema_version=2,
|
|
450
|
+
seq=state.cursor + 1,
|
|
451
|
+
timestamp=float(self._clock()),
|
|
452
|
+
run_id=state.handle.run_id,
|
|
453
|
+
scope_id=state.handle.scope_id,
|
|
454
|
+
parent_scope_id=state.handle.parent_scope_id,
|
|
455
|
+
source=SourceRef(framework="a2a"),
|
|
456
|
+
item_id=item,
|
|
457
|
+
item_kind="status",
|
|
458
|
+
)
|
|
459
|
+
yield await self._publish(
|
|
460
|
+
state,
|
|
461
|
+
ItemStarted(
|
|
462
|
+
**common,
|
|
463
|
+
event_id=stable_event_id(
|
|
464
|
+
"a2a",
|
|
465
|
+
state.handle.scope_id,
|
|
466
|
+
item,
|
|
467
|
+
"item.started",
|
|
468
|
+
"error",
|
|
469
|
+
"outcome-error",
|
|
470
|
+
0,
|
|
471
|
+
),
|
|
472
|
+
initial=ContentSnapshot(parts=()),
|
|
473
|
+
),
|
|
474
|
+
)
|
|
475
|
+
common.update(seq=state.cursor + 1, timestamp=float(self._clock()))
|
|
476
|
+
yield await self._publish(
|
|
477
|
+
state,
|
|
478
|
+
ItemFailed(
|
|
479
|
+
**common,
|
|
480
|
+
event_id=stable_event_id(
|
|
481
|
+
"a2a",
|
|
482
|
+
state.handle.scope_id,
|
|
483
|
+
item,
|
|
484
|
+
"item.failed",
|
|
485
|
+
"error",
|
|
486
|
+
"outcome-error",
|
|
487
|
+
0,
|
|
488
|
+
),
|
|
489
|
+
error=ErrorInfo(
|
|
490
|
+
code="A2A_SEND_OUTCOME_UNKNOWN",
|
|
491
|
+
message="Remote result is unknown; do not resend the call.",
|
|
492
|
+
source="a2a",
|
|
493
|
+
scope_id=state.handle.scope_id,
|
|
494
|
+
item_id=item,
|
|
495
|
+
),
|
|
496
|
+
),
|
|
497
|
+
)
|
|
498
|
+
yield await self._descriptor_event(state, "failed")
|
|
499
|
+
raise
|
|
500
|
+
finally:
|
|
501
|
+
state.active = False
|
|
502
|
+
|
|
503
|
+
async def _send(self, state, prepared, message, deadline):
|
|
504
|
+
lease = self._checkpoint_leases.get(state.handle.scope_id)
|
|
505
|
+
if lease is not None and await lease.cancel_requested():
|
|
506
|
+
if not state.send_entered:
|
|
507
|
+
yield await self._descriptor_event(
|
|
508
|
+
state,
|
|
509
|
+
"cancelled",
|
|
510
|
+
cancel=ScopeCancelState(capability="unknown", request_state="local_confirmed"),
|
|
511
|
+
)
|
|
512
|
+
self._snapshot(state, "cancelled")
|
|
513
|
+
await lease.clear_cancel_request()
|
|
514
|
+
else:
|
|
515
|
+
delivery = await self._cancel_delivery(state, deadline)
|
|
516
|
+
for event in delivery.events:
|
|
517
|
+
yield event
|
|
518
|
+
return
|
|
519
|
+
async with AsyncExitStack() as stack:
|
|
520
|
+
started_at = time.perf_counter()
|
|
521
|
+
native_wait_seconds = 0.0
|
|
522
|
+
reconcile_seconds = 0.0
|
|
523
|
+
native_events = 0
|
|
524
|
+
projected_events = 0
|
|
525
|
+
# Entering the wire port is conservatively treated as possible send.
|
|
526
|
+
state.send_entered = True
|
|
527
|
+
await self._save_state(state)
|
|
528
|
+
if state.descriptor.status != "working":
|
|
529
|
+
yield await self._descriptor_event(state, "working")
|
|
530
|
+
stream = await self._timed(
|
|
531
|
+
stack.enter_async_context(
|
|
532
|
+
self._space_client.open_prepared_message_stream(prepared, message)
|
|
533
|
+
),
|
|
534
|
+
deadline,
|
|
535
|
+
"connect",
|
|
536
|
+
)
|
|
537
|
+
iterator = aiter(stream)
|
|
538
|
+
stack.push_async_callback(self._stop_pending_read, state)
|
|
539
|
+
first, terminal, direct = True, False, False
|
|
540
|
+
while True:
|
|
541
|
+
wait_started = time.perf_counter()
|
|
542
|
+
try:
|
|
543
|
+
native = await self._timed(
|
|
544
|
+
self._next_native(state, iterator, deadline),
|
|
545
|
+
deadline,
|
|
546
|
+
"first_event" if first else "idle",
|
|
547
|
+
)
|
|
548
|
+
except StopAsyncIteration:
|
|
549
|
+
native_wait_seconds += time.perf_counter() - wait_started
|
|
550
|
+
break
|
|
551
|
+
native_wait_seconds += time.perf_counter() - wait_started
|
|
552
|
+
if isinstance(native, _CancelDelivery):
|
|
553
|
+
for event in native.events:
|
|
554
|
+
yield event
|
|
555
|
+
if native.terminal:
|
|
556
|
+
return
|
|
557
|
+
continue
|
|
558
|
+
native_events += 1
|
|
559
|
+
first = False
|
|
560
|
+
payload = (
|
|
561
|
+
getattr(native, native.WhichOneof("payload"))
|
|
562
|
+
if isinstance(native, StreamResponse) and native.WhichOneof("payload")
|
|
563
|
+
else native
|
|
564
|
+
)
|
|
565
|
+
await self._bind(state, payload, deadline)
|
|
566
|
+
if isinstance(payload, Message) and not payload.context_id:
|
|
567
|
+
normalized = Message()
|
|
568
|
+
normalized.CopyFrom(payload)
|
|
569
|
+
normalized.context_id = state.context.context_id
|
|
570
|
+
payload = normalized
|
|
571
|
+
events = state.adapter.map_event(
|
|
572
|
+
payload, state.context, timestamp=float(self._clock())
|
|
573
|
+
)
|
|
574
|
+
async for event in self._mapped(state, events):
|
|
575
|
+
projected_events += 1
|
|
576
|
+
yield event
|
|
577
|
+
status = (
|
|
578
|
+
payload.status.state
|
|
579
|
+
if isinstance(payload, (Task, TaskStatusUpdateEvent))
|
|
580
|
+
else None
|
|
581
|
+
)
|
|
582
|
+
if status in {
|
|
583
|
+
TaskState.TASK_STATE_INPUT_REQUIRED,
|
|
584
|
+
TaskState.TASK_STATE_AUTH_REQUIRED,
|
|
585
|
+
}:
|
|
586
|
+
state.source = "stream"
|
|
587
|
+
yield await self._descriptor_event(state, "input_required")
|
|
588
|
+
self._snapshot(
|
|
589
|
+
state,
|
|
590
|
+
"auth_required"
|
|
591
|
+
if status == TaskState.TASK_STATE_AUTH_REQUIRED
|
|
592
|
+
else "input_required",
|
|
593
|
+
)
|
|
594
|
+
terminal |= status in {
|
|
595
|
+
TaskState.TASK_STATE_COMPLETED,
|
|
596
|
+
TaskState.TASK_STATE_FAILED,
|
|
597
|
+
TaskState.TASK_STATE_CANCELED,
|
|
598
|
+
TaskState.TASK_STATE_REJECTED,
|
|
599
|
+
}
|
|
600
|
+
direct |= isinstance(payload, Message) and not state.handle.remote_task_id
|
|
601
|
+
if state.handle.remote_task_id and (
|
|
602
|
+
terminal or state.descriptor.status != "input_required"
|
|
603
|
+
):
|
|
604
|
+
reconcile_started = time.perf_counter()
|
|
605
|
+
async for event in self._observe_bound(state, deadline):
|
|
606
|
+
projected_events += 1
|
|
607
|
+
yield event
|
|
608
|
+
reconcile_seconds = time.perf_counter() - reconcile_started
|
|
609
|
+
elif direct:
|
|
610
|
+
yield await self._descriptor_event(state, "completed")
|
|
611
|
+
self._snapshot(state)
|
|
612
|
+
elif state.descriptor.status != "input_required":
|
|
613
|
+
self._snapshot(
|
|
614
|
+
state,
|
|
615
|
+
"unknown",
|
|
616
|
+
A2AInvocationError(
|
|
617
|
+
code="A2A_REMOTE_BINDING_UNKNOWN",
|
|
618
|
+
message="Remote task binding is not yet confirmed",
|
|
619
|
+
retryable=True,
|
|
620
|
+
),
|
|
621
|
+
)
|
|
622
|
+
elapsed = time.perf_counter() - started_at
|
|
623
|
+
logger.info(
|
|
624
|
+
"A2A stream timing task=%s native_events=%d projected_events=%d "
|
|
625
|
+
"elapsed_ms=%.1f native_wait_ms=%.1f consume_ms=%.1f reconcile_ms=%.1f",
|
|
626
|
+
state.handle.platform_task_id,
|
|
627
|
+
native_events,
|
|
628
|
+
projected_events,
|
|
629
|
+
elapsed * 1000,
|
|
630
|
+
native_wait_seconds * 1000,
|
|
631
|
+
max(0.0, elapsed - native_wait_seconds - reconcile_seconds) * 1000,
|
|
632
|
+
reconcile_seconds * 1000,
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
async def _cancel_delivery(self, state, deadline):
|
|
636
|
+
before = state.cursor
|
|
637
|
+
result = await self._cancel(state.handle, deadline_unix_seconds=deadline)
|
|
638
|
+
facts = await self._event_store.list(state.handle.session_id, run_id=state.handle.run_id)
|
|
639
|
+
return _CancelDelivery(
|
|
640
|
+
[
|
|
641
|
+
event
|
|
642
|
+
for event in facts
|
|
643
|
+
if event.scope_id == state.handle.scope_id and event.seq > before
|
|
644
|
+
],
|
|
645
|
+
bool(result.snapshot and result.snapshot.terminal),
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
async def _next_native(self, state, iterator, deadline):
|
|
649
|
+
lease = self._checkpoint_leases.get(state.handle.scope_id)
|
|
650
|
+
if lease is None:
|
|
651
|
+
return await anext(iterator)
|
|
652
|
+
pending = state.pending_native
|
|
653
|
+
if pending is None:
|
|
654
|
+
pending = state.pending_native = asyncio.create_task(anext(iterator))
|
|
655
|
+
try:
|
|
656
|
+
while True:
|
|
657
|
+
key = (state.handle.platform_task_id, state.handle.remote_task_id)
|
|
658
|
+
if state.cancel_checked_for != key and await lease.cancel_requested():
|
|
659
|
+
state.cancel_checked_for = key
|
|
660
|
+
delivery = await self._cancel_delivery(state, deadline)
|
|
661
|
+
if delivery.terminal:
|
|
662
|
+
await self._stop_pending_read(state)
|
|
663
|
+
return delivery
|
|
664
|
+
done, _ = await asyncio.wait({pending}, timeout=0.2)
|
|
665
|
+
if done:
|
|
666
|
+
state.pending_native = None
|
|
667
|
+
return pending.result()
|
|
668
|
+
except BaseException:
|
|
669
|
+
await self._stop_pending_read(state)
|
|
670
|
+
raise
|
|
671
|
+
|
|
672
|
+
@staticmethod
|
|
673
|
+
async def _stop_pending_read(state):
|
|
674
|
+
pending, state.pending_native = state.pending_native, None
|
|
675
|
+
if pending is not None:
|
|
676
|
+
if not pending.done():
|
|
677
|
+
pending.cancel()
|
|
678
|
+
await asyncio.gather(pending, return_exceptions=True)
|
|
679
|
+
|
|
680
|
+
async def _prepare_bound(self, state, operation, deadline, **kwargs):
|
|
681
|
+
prepared = await self._timed(
|
|
682
|
+
self._control_plane.prepare_task_operation(
|
|
683
|
+
platform_task_id=state.handle.platform_task_id, operation=operation, **kwargs
|
|
684
|
+
),
|
|
685
|
+
deadline,
|
|
686
|
+
"prepare",
|
|
687
|
+
)
|
|
688
|
+
self._validate_prepared(state.request, prepared)
|
|
689
|
+
if prepared.remote_task is None:
|
|
690
|
+
raise ValueError("A2A_REMOTE_BINDING_UNKNOWN")
|
|
691
|
+
state.handle = state.handle.bind_remote_task(
|
|
692
|
+
prepared.remote_task.remote_task_id, prepared.remote_task.remote_context_id
|
|
693
|
+
)
|
|
694
|
+
# A bound get/cancel keeps the originally approved invocation identity.
|
|
695
|
+
# Its separate I/O deadline must not invalidate that durable approval.
|
|
696
|
+
await self._authorize(state.request, prepared.route.kind, deadline=deadline)
|
|
697
|
+
return prepared
|
|
698
|
+
|
|
699
|
+
async def _observe_bound(self, state, deadline):
|
|
700
|
+
"""Observe an existing task to the caller's original stopping boundary."""
|
|
701
|
+
while True:
|
|
702
|
+
lease = self._checkpoint_leases.get(state.handle.scope_id)
|
|
703
|
+
if lease is not None and await lease.cancel_requested():
|
|
704
|
+
delivery = await self._cancel_delivery(state, deadline)
|
|
705
|
+
for event in delivery.events:
|
|
706
|
+
yield event
|
|
707
|
+
if delivery.terminal:
|
|
708
|
+
return
|
|
709
|
+
async for event in self._get(state, deadline):
|
|
710
|
+
yield event
|
|
711
|
+
if state.snapshot is not None and (
|
|
712
|
+
state.snapshot.terminal
|
|
713
|
+
or state.snapshot.status in {"input_required", "auth_required"}
|
|
714
|
+
):
|
|
715
|
+
return
|
|
716
|
+
remaining = deadline - self._clock()
|
|
717
|
+
if remaining <= 0:
|
|
718
|
+
raise TimeoutError("A2A_OVERALL_TIMEOUT")
|
|
719
|
+
await asyncio.sleep(min(0.1, remaining))
|
|
720
|
+
|
|
721
|
+
async def _recover_bound(self, state, deadline):
|
|
722
|
+
# Adapter dedupe protects persistence, but a new consumer still needs
|
|
723
|
+
# outputs committed before its stream began. Deliver existing facts in
|
|
724
|
+
# canonical order before observing new facts, without appending again.
|
|
725
|
+
await self._authorize(state.request, "hosted_gateway", deadline=deadline)
|
|
726
|
+
delivered = set()
|
|
727
|
+
for event in await self.replay(state.handle):
|
|
728
|
+
delivered.add(event.event_id)
|
|
729
|
+
yield event
|
|
730
|
+
async for event in self._observe_bound(state, deadline):
|
|
731
|
+
if event.event_id not in delivered:
|
|
732
|
+
delivered.add(event.event_id)
|
|
733
|
+
yield event
|
|
734
|
+
if state.snapshot is not None and state.snapshot.terminal:
|
|
735
|
+
await self._terminal_replay(state)
|
|
736
|
+
|
|
737
|
+
async def _get(self, state, deadline):
|
|
738
|
+
prepared = await self._prepare_bound(state, "get_task", deadline)
|
|
739
|
+
task = await self._timed(
|
|
740
|
+
self._space_client.get_prepared_task(prepared), deadline, "connect"
|
|
741
|
+
)
|
|
742
|
+
await self._bind(state, task, deadline)
|
|
743
|
+
|
|
744
|
+
class Fetched:
|
|
745
|
+
async def get_task(self, request):
|
|
746
|
+
return task
|
|
747
|
+
|
|
748
|
+
result = await state.adapter.reconcile(
|
|
749
|
+
Fetched(), state.context, reason="terminal", timestamp=float(self._clock())
|
|
750
|
+
)
|
|
751
|
+
if not result.consistent:
|
|
752
|
+
raise ValueError(result.error or "A2A_RECONCILIATION_FAILED")
|
|
753
|
+
async for event in self._mapped(state, result.events):
|
|
754
|
+
yield event
|
|
755
|
+
state.source = "get_task"
|
|
756
|
+
statuses = {
|
|
757
|
+
TaskState.TASK_STATE_COMPLETED: "completed",
|
|
758
|
+
TaskState.TASK_STATE_FAILED: "failed",
|
|
759
|
+
TaskState.TASK_STATE_REJECTED: "failed",
|
|
760
|
+
TaskState.TASK_STATE_CANCELED: "cancelled",
|
|
761
|
+
TaskState.TASK_STATE_INPUT_REQUIRED: "input_required",
|
|
762
|
+
TaskState.TASK_STATE_AUTH_REQUIRED: "input_required",
|
|
763
|
+
}
|
|
764
|
+
status = statuses.get(task.status.state, "working")
|
|
765
|
+
if state.descriptor.status in {"completed", "failed", "cancelled"}:
|
|
766
|
+
if state.snapshot is not None and not state.snapshot.terminal:
|
|
767
|
+
# Reconcile the private remote result without reopening the
|
|
768
|
+
# already terminal public local call.
|
|
769
|
+
self._snapshot(state, status)
|
|
770
|
+
return
|
|
771
|
+
cancel = (
|
|
772
|
+
ScopeCancelState(capability="supported", request_state="confirmed")
|
|
773
|
+
if status == "cancelled"
|
|
774
|
+
else None
|
|
775
|
+
)
|
|
776
|
+
if status != state.descriptor.status or cancel is not None:
|
|
777
|
+
yield await self._descriptor_event(state, status, cancel=cancel)
|
|
778
|
+
error = (
|
|
779
|
+
A2AInvocationError(code="A2A_REMOTE_FAILED", message="Remote task failed")
|
|
780
|
+
if status == "failed"
|
|
781
|
+
else None
|
|
782
|
+
)
|
|
783
|
+
self._snapshot(state, status, error)
|
|
784
|
+
|
|
785
|
+
async def get(self, handle, *, deadline_unix_seconds=None):
|
|
786
|
+
async with self._operation(handle):
|
|
787
|
+
return await self._get_snapshot(handle, deadline_unix_seconds=deadline_unix_seconds)
|
|
788
|
+
|
|
789
|
+
async def _get_snapshot(self, handle, *, deadline_unix_seconds=None):
|
|
790
|
+
state = self._state(handle)
|
|
791
|
+
if state.snapshot and state.snapshot.terminal and not state.send_entered:
|
|
792
|
+
return state.snapshot
|
|
793
|
+
lease = self._checkpoint_leases.get(handle.scope_id)
|
|
794
|
+
if (
|
|
795
|
+
lease is not None
|
|
796
|
+
and await lease.cancel_requested()
|
|
797
|
+
and not (state.snapshot and state.snapshot.terminal)
|
|
798
|
+
):
|
|
799
|
+
await self._cancel(state.handle, deadline_unix_seconds=deadline_unix_seconds)
|
|
800
|
+
if state.snapshot and state.snapshot.terminal:
|
|
801
|
+
return state.snapshot
|
|
802
|
+
async for _ in self._get(state, deadline_unix_seconds or float(self._clock()) + 60):
|
|
803
|
+
pass
|
|
804
|
+
return state.snapshot
|
|
805
|
+
|
|
806
|
+
def _state(self, handle):
|
|
807
|
+
state = self._states.get(handle.scope_id)
|
|
808
|
+
if state is None:
|
|
809
|
+
raise KeyError("A2A_INVOCATION_NOT_FOUND")
|
|
810
|
+
# Immutable identity must match; an older pre-bind handle is accepted.
|
|
811
|
+
if state.handle.model_dump(
|
|
812
|
+
exclude={"platform_task_id", "remote_task_id", "remote_context_id"}
|
|
813
|
+
) != handle.model_dump(exclude={"platform_task_id", "remote_task_id", "remote_context_id"}):
|
|
814
|
+
raise ValueError("A2A_HANDLE_CONFLICT")
|
|
815
|
+
# A pre-bind handle may omit remote locators. Once a caller supplies
|
|
816
|
+
# one, it must agree with the authoritative receipt; never silently
|
|
817
|
+
# retarget a stale or cross-task handle to this scope's cached task.
|
|
818
|
+
for locator_field in ("platform_task_id", "remote_task_id", "remote_context_id"):
|
|
819
|
+
supplied = getattr(handle, locator_field)
|
|
820
|
+
if supplied is not None and supplied != getattr(state.handle, locator_field):
|
|
821
|
+
raise ValueError("A2A_HANDLE_CONFLICT")
|
|
822
|
+
return state
|
|
823
|
+
|
|
824
|
+
async def continue_task(self, request: A2AContinuationRequest):
|
|
825
|
+
async with self._operation(request.handle):
|
|
826
|
+
async for event in self._continue_task(request):
|
|
827
|
+
yield event
|
|
828
|
+
|
|
829
|
+
async def _continue_task(self, request: A2AContinuationRequest):
|
|
830
|
+
state = self._state(request.handle)
|
|
831
|
+
deadline = min(request.deadline_unix_seconds, state.request.deadline_unix_seconds)
|
|
832
|
+
if state.active:
|
|
833
|
+
raise ValueError("A2A_INVOCATION_BUSY")
|
|
834
|
+
if request.spec != state.request.spec:
|
|
835
|
+
raise ValueError("A2A_TARGET_MISMATCH")
|
|
836
|
+
message = request.to_wire_message()
|
|
837
|
+
digest = canonical_message_sha256(message)
|
|
838
|
+
if request.idempotency_token in state.tokens:
|
|
839
|
+
if state.tokens[request.idempotency_token] != digest:
|
|
840
|
+
raise ValueError("A2A_IDEMPOTENCY_CONFLICT")
|
|
841
|
+
await self._authorize(request, deadline=deadline)
|
|
842
|
+
if state.snapshot is not None and state.snapshot.terminal:
|
|
843
|
+
for event in await self._terminal_replay(state):
|
|
844
|
+
yield event
|
|
845
|
+
else:
|
|
846
|
+
async for event in self._recover_bound(
|
|
847
|
+
state, deadline
|
|
848
|
+
):
|
|
849
|
+
yield event
|
|
850
|
+
state.pending_continuation = None
|
|
851
|
+
return
|
|
852
|
+
if state.descriptor.status != "input_required":
|
|
853
|
+
raise ValueError("A2A_CONTINUATION_NOT_ALLOWED")
|
|
854
|
+
if state.pending_continuation is not None and state.pending_continuation != request:
|
|
855
|
+
raise ValueError("A2A_PENDING_CONTINUATION_CONFLICT")
|
|
856
|
+
state.pending_continuation = request
|
|
857
|
+
await self._save_state(state)
|
|
858
|
+
await self._authorize(request, deadline=deadline)
|
|
859
|
+
state.active = True
|
|
860
|
+
try:
|
|
861
|
+
binding = await self._prepare_bound(state, "get_task", deadline)
|
|
862
|
+
if (
|
|
863
|
+
request.handle.remote_task_id != state.handle.remote_task_id
|
|
864
|
+
or request.handle.remote_context_id != state.handle.remote_context_id
|
|
865
|
+
):
|
|
866
|
+
raise ValueError("A2A_REMOTE_BINDING_CONFLICT")
|
|
867
|
+
prepared = await self._prepare_bound(
|
|
868
|
+
state,
|
|
869
|
+
"send_message",
|
|
870
|
+
deadline,
|
|
871
|
+
message_id=message.message_id,
|
|
872
|
+
message_sha256=digest,
|
|
873
|
+
idempotency_token=request.idempotency_token,
|
|
874
|
+
)
|
|
875
|
+
if prepared.remote_task != binding.remote_task:
|
|
876
|
+
raise ValueError("A2A_REMOTE_BINDING_CONFLICT")
|
|
877
|
+
state.tokens[request.idempotency_token] = digest
|
|
878
|
+
async for event in self._send(state, prepared, message, deadline):
|
|
879
|
+
yield event
|
|
880
|
+
state.pending_continuation = None
|
|
881
|
+
finally:
|
|
882
|
+
state.active = False
|
|
883
|
+
|
|
884
|
+
async def cancel(self, handle, *, deadline_unix_seconds=None):
|
|
885
|
+
from ksadk.a2a.invocation_checkpoint import InvocationCheckpointBusy
|
|
886
|
+
|
|
887
|
+
try:
|
|
888
|
+
async with self._operation(handle):
|
|
889
|
+
return await self._cancel(handle, deadline_unix_seconds=deadline_unix_seconds)
|
|
890
|
+
except InvocationCheckpointBusy:
|
|
891
|
+
requested = float(self._clock())
|
|
892
|
+
saved = await self._checkpoint_store.request_cancel(handle)
|
|
893
|
+
state = self._restore_state(saved)
|
|
894
|
+
return A2ACancelResult(
|
|
895
|
+
handle=state.handle,
|
|
896
|
+
outcome="already_terminal"
|
|
897
|
+
if state.snapshot and state.snapshot.terminal
|
|
898
|
+
else "cancel_requested_but_remote_unknown",
|
|
899
|
+
requested_at_unix_seconds=requested,
|
|
900
|
+
resolved_at_unix_seconds=float(self._clock()),
|
|
901
|
+
snapshot=state.snapshot,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
async def _cancel(self, handle, *, deadline_unix_seconds=None):
|
|
905
|
+
state = self._state(handle)
|
|
906
|
+
requested = float(self._clock())
|
|
907
|
+
deadline = deadline_unix_seconds or requested + 60
|
|
908
|
+
if state.snapshot and state.snapshot.terminal:
|
|
909
|
+
outcome = "already_terminal"
|
|
910
|
+
elif not state.send_entered and state.handle.remote_task_id is None and not state.active:
|
|
911
|
+
if state.cursor == 0:
|
|
912
|
+
await self._descriptor_event(state, "submitted", initial=True)
|
|
913
|
+
await self._descriptor_event(
|
|
914
|
+
state,
|
|
915
|
+
"cancelled",
|
|
916
|
+
cancel=ScopeCancelState(capability="unknown", request_state="local_confirmed"),
|
|
917
|
+
)
|
|
918
|
+
self._snapshot(state, "cancelled")
|
|
919
|
+
outcome = "local_cancelled_before_send"
|
|
920
|
+
else:
|
|
921
|
+
outcome = "cancel_requested_but_remote_unknown"
|
|
922
|
+
try:
|
|
923
|
+
prepared = await self._prepare_bound(
|
|
924
|
+
state,
|
|
925
|
+
"cancel_task",
|
|
926
|
+
deadline,
|
|
927
|
+
idempotency_token=stable_item_id("a2a", state.handle.scope_id, "cancel"),
|
|
928
|
+
)
|
|
929
|
+
task = await self._timed(
|
|
930
|
+
self._space_client.cancel_prepared_task(prepared), deadline, "connect"
|
|
931
|
+
)
|
|
932
|
+
if task.status.state == TaskState.TASK_STATE_CANCELED:
|
|
933
|
+
async for _ in self._get(state, deadline):
|
|
934
|
+
pass
|
|
935
|
+
if state.snapshot and state.snapshot.status == "cancelled":
|
|
936
|
+
outcome = "remote_cancelled"
|
|
937
|
+
elif task.status.state in {
|
|
938
|
+
TaskState.TASK_STATE_COMPLETED,
|
|
939
|
+
TaskState.TASK_STATE_FAILED,
|
|
940
|
+
TaskState.TASK_STATE_REJECTED,
|
|
941
|
+
}:
|
|
942
|
+
async for _ in self._get(state, deadline):
|
|
943
|
+
pass
|
|
944
|
+
outcome = "already_terminal"
|
|
945
|
+
except Exception as exc:
|
|
946
|
+
if type(exc).__name__ in {"TaskNotCancelableError", "UnsupportedOperationError"}:
|
|
947
|
+
outcome = "cancel_requested_but_remote_unsupported"
|
|
948
|
+
if outcome.startswith("cancel_requested"):
|
|
949
|
+
unsupported = outcome.endswith("unsupported")
|
|
950
|
+
await self._descriptor_event(
|
|
951
|
+
state,
|
|
952
|
+
state.descriptor.status,
|
|
953
|
+
cancel=ScopeCancelState(
|
|
954
|
+
capability="unsupported" if unsupported else "unknown",
|
|
955
|
+
request_state="unsupported" if unsupported else "unknown",
|
|
956
|
+
),
|
|
957
|
+
)
|
|
958
|
+
lease = self._checkpoint_leases.get(state.handle.scope_id)
|
|
959
|
+
if lease is not None and outcome in {
|
|
960
|
+
"local_cancelled_before_send",
|
|
961
|
+
"remote_cancelled",
|
|
962
|
+
"already_terminal",
|
|
963
|
+
"cancel_requested_but_remote_unsupported",
|
|
964
|
+
}:
|
|
965
|
+
await lease.clear_cancel_request()
|
|
966
|
+
return A2ACancelResult(
|
|
967
|
+
handle=state.handle,
|
|
968
|
+
outcome=outcome,
|
|
969
|
+
requested_at_unix_seconds=requested,
|
|
970
|
+
resolved_at_unix_seconds=float(self._clock()),
|
|
971
|
+
snapshot=state.snapshot,
|
|
972
|
+
)
|