agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.3__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.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/METADATA +25 -12
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/RECORD +421 -232
- ksadk/_build_provenance.json +1 -0
- ksadk/a2a/_space_client_events.py +381 -0
- ksadk/a2a/event_adapter.py +156 -64
- ksadk/a2a/executor.py +179 -63
- ksadk/a2a/space_client.py +6 -333
- ksadk/a2ui/core.py +222 -53
- ksadk/agui/_agent_helpers.py +247 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +443 -259
- ksadk/api/client.py +528 -27
- ksadk/builders/code_builder.py +284 -8
- ksadk/builders/container_builder.py +8 -1
- ksadk/builders/managed_runtime_builder.py +42 -35
- ksadk/cli/__init__.py +23 -0
- ksadk/cli/cmd_dashboard.py +15 -1
- ksadk/cli/cmd_deploy.py +10 -2
- ksadk/cli/cmd_eval.py +66 -9
- ksadk/cli/cmd_evalset.py +284 -0
- ksadk/cli/cmd_files.py +5 -1
- ksadk/cli/cmd_hermes.py +1 -126
- ksadk/cli/cmd_invoke.py +28 -38
- ksadk/cli/cmd_managed_runtime.py +95 -0
- ksadk/cli/cmd_observe.py +85 -0
- ksadk/cli/cmd_openclaw.py +1 -435
- ksadk/cli/cmd_plugin.py +657 -0
- ksadk/cli/cmd_replay.py +28 -4
- ksadk/cli/cmd_studio.py +99 -8
- ksadk/cli/cmd_web.py +10 -2
- ksadk/cli/hermes_env.py +151 -0
- ksadk/cli/invoke_payload.py +49 -0
- ksadk/cli/openclaw_env.py +458 -0
- ksadk/codex/client.py +180 -30
- ksadk/codex/runtime.py +475 -331
- ksadk/configs/env_registry.py +94 -12
- ksadk/configs/env_registry_pcm.py +181 -0
- ksadk/configs/env_var_spec.py +18 -0
- ksadk/configs/global_config.py +5 -0
- ksadk/configs/settings.py +9 -0
- ksadk/context_engine/__init__.py +98 -0
- ksadk/context_engine/assembler.py +176 -0
- ksadk/context_engine/baseline.py +419 -0
- ksadk/context_engine/cache_observability.py +228 -0
- ksadk/context_engine/capabilities.py +443 -0
- ksadk/context_engine/contributors.py +409 -0
- ksadk/context_engine/hosted_pipeline.py +417 -0
- ksadk/context_engine/models.py +117 -0
- ksadk/context_engine/planner.py +698 -0
- ksadk/context_engine/policies.py +319 -0
- ksadk/context_engine/projection.py +31 -0
- ksadk/context_engine/shadow_plan.py +266 -0
- ksadk/context_engine/tokenizer.py +164 -0
- ksadk/conversations/__init__.py +19 -0
- ksadk/conversations/context.py +230 -30
- ksadk/conversations/contracts.py +266 -0
- ksadk/conversations/message_projection.py +308 -26
- ksadk/conversations/model_context.py +49 -3
- ksadk/conversations/projector.py +616 -0
- ksadk/conversations/reducer.py +98 -0
- ksadk/conversations/runtime_compaction.py +207 -5
- ksadk/conversations/runtime_input.py +184 -2
- ksadk/conversations/runtime_invocation.py +81 -3
- ksadk/conversations/runtime_observability.py +141 -0
- ksadk/conversations/runtime_payloads.py +42 -0
- ksadk/conversations/runtime_preparation.py +272 -2
- ksadk/conversations/runtime_resume.py +20 -1
- ksadk/conversations/runtime_stream_events.py +94 -9
- ksadk/conversations/semantic_summary.py +311 -1
- ksadk/conversations/session_lock.py +51 -0
- ksadk/deployment/env_forward.py +72 -0
- ksadk/deployment/managed_runtime.py +5 -0
- ksadk/deployment/providers/serverless.py +143 -69
- ksadk/evaluation/__init__.py +32 -0
- ksadk/evaluation/a2a_adapter.py +9 -1
- ksadk/evaluation/adapters.py +18 -5
- ksadk/evaluation/agent_eval_client.py +395 -0
- ksadk/evaluation/cloud_binding.py +80 -0
- ksadk/evaluation/cloud_converter.py +194 -0
- ksadk/evaluation/cloud_service.py +199 -0
- ksadk/evaluation/contracts.py +73 -4
- ksadk/evaluation/evaluators.py +183 -17
- ksadk/evaluation/evidence.py +258 -0
- ksadk/evaluation/executor.py +31 -6
- ksadk/evaluation/local_adapter.py +550 -0
- ksadk/evaluation/service_env.py +27 -0
- ksadk/evaluation/studio_build_adapter.py +253 -0
- ksadk/evaluation/target.py +15 -6
- ksadk/events/__init__.py +13 -13
- ksadk/events/_v1_compat/__init__.py +1 -0
- ksadk/events/_v1_compat/models.py +299 -0
- ksadk/events/_v1_compat/parser.py +247 -0
- ksadk/events/_v1_compat/projection.py +755 -0
- ksadk/events/adapters/__init__.py +34 -0
- ksadk/events/adapters/_a2a_snapshot.py +622 -0
- ksadk/events/adapters/_a2a_support.py +275 -0
- ksadk/events/adapters/_codex_interactions.py +375 -0
- ksadk/events/adapters/_codex_items.py +801 -0
- ksadk/events/adapters/_codex_validators.py +311 -0
- ksadk/events/adapters/_langgraph_support.py +785 -0
- ksadk/events/adapters/a2a.py +841 -0
- ksadk/events/adapters/adk.py +503 -0
- ksadk/events/adapters/codex.py +907 -0
- ksadk/events/adapters/langgraph.py +745 -0
- ksadk/events/canonical.py +440 -0
- ksadk/events/canonical_replay.py +497 -0
- ksadk/events/canonical_store.py +468 -0
- ksadk/events/cold_recovery.py +257 -0
- ksadk/events/content.py +89 -0
- ksadk/events/identity.py +86 -0
- ksadk/events/pipeline.py +455 -0
- ksadk/events/projections.py +66 -0
- ksadk/events/reducer.py +661 -0
- ksadk/events/replay.py +3 -36
- ksadk/events/runtime_event.py +20 -265
- ksadk/events/session_event.py +324 -0
- ksadk/events/store.py +3 -316
- ksadk/events/v1_compat.py +41 -0
- ksadk/harness/__init__.py +45 -35
- ksadk/harness/runtime.py +220 -58
- ksadk/identity/resolver.py +4 -3
- ksadk/interaction/__init__.py +28 -0
- ksadk/interaction/contracts.py +136 -0
- ksadk/interaction/ledger.py +189 -0
- ksadk/interaction/provider.py +122 -0
- ksadk/interaction/providers/__init__.py +52 -0
- ksadk/interaction/providers/adk.py +40 -0
- ksadk/interaction/providers/codex.py +94 -0
- ksadk/interaction/providers/langgraph.py +58 -0
- ksadk/kernel/__init__.py +117 -0
- ksadk/kernel/authorization.py +258 -0
- ksadk/kernel/bootstrap.py +1074 -0
- ksadk/kernel/contract_fingerprints.py +62 -0
- ksadk/kernel/contracts.py +370 -0
- ksadk/kernel/control.py +220 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1123 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1134 -0
- ksadk/kernel/postgres_store.py +1757 -0
- ksadk/kernel/recovery.py +452 -0
- ksadk/kernel/runtime_identity.py +103 -0
- ksadk/kernel/sql/001_agent_kernel.sql +120 -0
- ksadk/kernel/sqlite_store.py +1411 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +964 -0
- ksadk/knowledge_base/client.py +7 -0
- ksadk/knowledge_base/service.py +32 -5
- ksadk/memory/__init__.py +61 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
- ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
- ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
- ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
- ksadk/memory/coordinator.py +461 -0
- ksadk/memory/events.py +202 -0
- ksadk/memory/extraction.py +228 -0
- ksadk/memory/ltm_backend_factory.py +16 -3
- ksadk/memory/models.py +316 -0
- ksadk/memory/policy.py +215 -0
- ksadk/memory/provider.py +47 -0
- ksadk/memory/provider_adapter.py +159 -0
- ksadk/memory/provider_resolver.py +71 -0
- ksadk/memory/providers/__init__.py +5 -0
- ksadk/memory/providers/local_sqlite.py +497 -0
- ksadk/memory/resolved_policy.py +145 -0
- ksadk/memory/service.py +188 -6
- ksadk/model_proxy/bootstrap.py +16 -9
- ksadk/model_proxy/detect.py +202 -9
- ksadk/model_proxy/namespace.py +39 -15
- ksadk/model_proxy/server.py +24 -5
- ksadk/model_proxy/transform.py +60 -2
- ksadk/observability/__init__.py +25 -0
- ksadk/observability/session_log.py +420 -0
- ksadk/observability/trajectory.py +105 -0
- ksadk/plugins/__init__.py +167 -0
- ksadk/plugins/bridges/__init__.py +45 -0
- ksadk/plugins/bridges/codex.py +552 -0
- ksadk/plugins/bridges/dsh.py +1116 -0
- ksadk/plugins/builtins.py +747 -0
- ksadk/plugins/bundle.py +246 -0
- ksadk/plugins/bundle_security.py +167 -0
- ksadk/plugins/composition.py +529 -0
- ksadk/plugins/context_contributor.py +437 -0
- ksadk/plugins/contracts.py +451 -0
- ksadk/plugins/dsh_toolchain.py +935 -0
- ksadk/plugins/ecosystem_bridge.py +1105 -0
- ksadk/plugins/ecosystem_probe.py +191 -0
- ksadk/plugins/host.py +724 -0
- ksadk/plugins/providers/__init__.py +85 -0
- ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
- ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
- ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
- ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
- ksadk/plugins/providers/codex.py +722 -0
- ksadk/plugins/providers/codex_dsh.py +83 -0
- ksadk/plugins/providers/dsh.py +951 -0
- ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
- ksadk/plugins/providers/harness.py +627 -0
- ksadk/plugins/providers/harness_dsh.py +91 -0
- ksadk/plugins/providers/legacy.py +215 -0
- ksadk/plugins/providers/legacy_catalog.py +100 -0
- ksadk/plugins/providers/shipped_dsh.py +370 -0
- ksadk/plugins/resolver.py +331 -0
- ksadk/plugins/subagent_providers/__init__.py +12 -0
- ksadk/plugins/subagent_providers/codex.py +443 -0
- ksadk/plugins/subagents.py +333 -0
- ksadk/prompts/__init__.py +71 -0
- ksadk/prompts/compiler.py +215 -0
- ksadk/prompts/models.py +82 -0
- ksadk/prompts/projection.py +89 -0
- ksadk/prompts/resolved.py +137 -0
- ksadk/prompts/sources.py +233 -0
- ksadk/runners/_langgraph_runner_streams.py +822 -0
- ksadk/runners/adk_runner.py +5 -5
- ksadk/runners/base_runner.py +16 -0
- ksadk/runners/factory.py +1 -2
- ksadk/runners/langgraph_runner.py +198 -437
- ksadk/runtime/_runner_adapter/__init__.py +1 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +797 -0
- ksadk/runtime/adapter.py +119 -2
- ksadk/runtime/conversation_execution.py +316 -182
- ksadk/runtime/executor.py +144 -3
- ksadk/runtime/factory.py +268 -8
- ksadk/runtime/framework_adapters.py +18 -0
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +14 -1
- ksadk/runtime/preprocessing.py +25 -3
- ksadk/runtime/runner_adapter.py +293 -508
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/__init__.py +2 -1
- ksadk/sandbox/backends/e2b.py +35 -2
- ksadk/sandbox/factory.py +72 -0
- ksadk/scheduler/__init__.py +18 -0
- ksadk/scheduler/calendar.py +102 -0
- ksadk/scheduler/contracts.py +254 -0
- ksadk/scheduler/dispatcher.py +129 -0
- ksadk/scheduler/engine.py +416 -0
- ksadk/scheduler/sqlite_store.py +563 -0
- ksadk/server/composition.py +8 -0
- ksadk/server/factory.py +69 -6
- ksadk/server/routes/kernel_ingress.py +258 -0
- ksadk/server/routes/openai_compat.py +88 -8
- ksadk/server/routes/projection.py +35 -20
- ksadk/server/routes/run.py +94 -0
- ksadk/server/routes/sessions.py +2 -0
- ksadk/server/routes/streaming.py +5 -2
- ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-10GPXIJ6.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-BXk1l-G1.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-D9uB55Oh.js} +4 -4
- ksadk/server/static/assets/MermaidBlock--OEYoXIJ.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DrEe1syq.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-eeTFJLvo.js +1 -0
- ksadk/server/static/assets/arc-S3ZNbIAd.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-CRjgEJDX.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-C7bsBoNH.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-BX3c33Ob.js +38 -0
- ksadk/server/static/assets/channel-s354Yo6o.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-bZ7ws-8m.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-D2r0pvhY.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-CZwF3oUP.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-DigFYCML.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-CnorGV5q.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-CexntQA-.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-CHTC7UxA.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BC1EWt_-.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-BTG24xN0.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-CkonRdAS.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-DEOCBe15.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DBThPuLa.js +4 -0
- ksadk/server/static/assets/dagre-ClTPrTht.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-DsZHrnpJ.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-BJR4nhVr.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-xzcERX0a.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-tn2xM1xu.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-BZPGBkw5.js +24 -0
- ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-4zN1_tq5.js} +1 -1
- ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-8VWI-2bZ.js} +1 -1
- ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-B-46q3Hk.js} +1 -1
- ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-B0gdk4rl.js} +1 -1
- ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-B1oWRmrH.js} +1 -1
- ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-BI1qQH4n.js} +1 -1
- ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-BJLUwH6W.js} +1 -1
- ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Bbwqa8TG.js} +1 -1
- ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-Btyo4vX4.js} +1 -1
- ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-C71CsujO.js} +1 -1
- ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-CXpH_Rdd.js} +1 -1
- ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-Cq4OL6nO.js} +1 -1
- ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-D1V3f1B4.js} +1 -1
- ksadk/server/static/assets/{dist-fhOuWds5.js → dist-DJmCPRVB.js} +1 -1
- ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DQAB0qn_.js} +2 -2
- ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DUwKa_l6.js} +1 -1
- ksadk/server/static/assets/dist-DdRBIceA.js +1 -0
- ksadk/server/static/assets/{dist-DWKByxWV.js → dist-DfGFrb0v.js} +1 -1
- ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-DjhwGUgZ.js} +1 -1
- ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-DyqV4fL4.js} +1 -1
- ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-M67Hwu0n.js} +1 -1
- ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-u8tVH5Ak.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-DVfMsLge.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-CWEZSYaw.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-TWIxoyYL.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-DDh2hd2S.js +106 -0
- ksadk/server/static/assets/index-B2k_urY8.js +241 -0
- ksadk/server/static/assets/index-ILPt6IWF.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-CgoyWiDR.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-CyRIu_om.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DN5JXHzN.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-CNP3ui66.js} +10 -10
- ksadk/server/static/assets/katex-DWEzQy1d.js +257 -0
- ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-C-qHp7d2.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-Cl-K943T.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-D5DB5mYm.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-BQVc9ia_.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-B6UCAvtY.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-mIr2d3eb.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-Dt3FFDmK.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-Cz_vQtgF.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-wlO1TsH5.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-CHRbElYq.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-DXX54iyO.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-Dp_lw9II.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-D8O5UlLG.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-8mfKcxvG.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-EnNl0DE7.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DBu5fbYr.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-BSVXs4IY.js +7 -0
- ksadk/server/static/index.html +2 -2
- ksadk/sessions/__init__.py +26 -9
- ksadk/sessions/_local_service_sync.py +688 -0
- ksadk/sessions/_local_tables.py +21 -0
- ksadk/sessions/_postgres_schema.py +305 -0
- ksadk/sessions/_postgres_tables.py +15 -0
- ksadk/sessions/base.py +80 -1
- ksadk/sessions/in_memory.py +53 -2
- ksadk/sessions/local_service.py +63 -604
- ksadk/sessions/postgres_service.py +58 -148
- ksadk/sessions/resilient.py +35 -1
- ksadk/skills/service_client.py +48 -0
- ksadk/studio/agent_lifecycle.py +26 -20
- ksadk/studio/api.py +1063 -69
- ksadk/studio/api_catalog_routes.py +5 -0
- ksadk/studio/api_contracts.py +148 -6
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/api_plugin_routes.py +480 -0
- ksadk/studio/attachment_store.py +187 -0
- ksadk/studio/authoring.py +259 -28
- ksadk/studio/authoring_coordinator.py +809 -67
- ksadk/studio/builder.py +197 -40
- ksadk/studio/capabilities.py +17 -0
- ksadk/studio/cloud.py +1920 -95
- ksadk/studio/codex_agent_service.py +137 -15
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +177 -25
- ksadk/studio/codex_manifest.py +71 -22
- ksadk/studio/codex_run.py +59 -4
- ksadk/studio/compatibility_report.py +445 -0
- ksadk/studio/compiler.py +7 -1
- ksadk/studio/contracts.py +234 -4
- ksadk/studio/dsh_provider_registration.py +768 -0
- ksadk/studio/event_store.py +79 -101
- ksadk/studio/framework_run.py +264 -3
- ksadk/studio/hosted_kernel.py +317 -0
- ksadk/studio/manifest_resolver.py +131 -0
- ksadk/studio/model_client.py +95 -9
- ksadk/studio/model_profile_service.py +1 -1
- ksadk/studio/operations.py +47 -5
- ksadk/studio/otel_trace.py +348 -42
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/plugin_composition.py +301 -0
- ksadk/studio/plugin_kernel_adapter.py +128 -0
- ksadk/studio/plugin_runtime.py +652 -0
- ksadk/studio/repository.py +23 -2
- ksadk/studio/resource_catalog.py +49 -1
- ksadk/studio/run_service.py +1266 -202
- ksadk/studio/runtime_source.py +51 -17
- ksadk/studio/scheduler_runtime.py +374 -0
- ksadk/studio/scheduler_service.py +211 -0
- ksadk/studio/service.py +1815 -117
- ksadk/studio/shared_web.py +199 -50
- ksadk/studio/soul.py +52 -0
- ksadk/studio/static/assets/index-DXs1Eq4M.css +1 -0
- ksadk/studio/static/assets/index-DeDqwTsD.js +283 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/studio/templates.py +100 -8
- ksadk/studio/validator.py +20 -1
- ksadk/studio/workspace.py +31 -32
- ksadk/tracing/setup.py +304 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
- ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
- ksadk_runtime_common/workspace_files/path_utils.py +41 -0
- ksadk/events/parser.py +0 -191
- ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
- ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
- ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
- ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
- ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
- ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
- ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
- ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
- ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
- ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
- ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
- ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
- ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
- ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
- ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
- ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
- ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
- ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
- ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
- ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
- ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
- ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
- ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
- ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
- ksadk/server/static/assets/index-R52pJJvg.js +0 -177
- ksadk/server/static/assets/index-jJr3_955.css +0 -2
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
- ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
- ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
- ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
- ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
- ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
- ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
- ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
- ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
- ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
- ksadk/studio/evaluation.py +0 -192
- ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
- ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/top_level.txt +0 -0
ksadk/codex/runtime.py
CHANGED
|
@@ -19,14 +19,33 @@
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
import asyncio
|
|
22
|
+
import base64
|
|
23
|
+
import binascii
|
|
24
|
+
import hashlib
|
|
22
25
|
import json
|
|
23
26
|
import logging
|
|
27
|
+
import re
|
|
28
|
+
import time
|
|
29
|
+
from collections.abc import Mapping
|
|
24
30
|
from dataclasses import dataclass, field
|
|
31
|
+
from pathlib import Path
|
|
25
32
|
from typing import Any, AsyncIterator, Optional
|
|
26
33
|
|
|
27
34
|
from ksadk.codex.client import CodexClient
|
|
28
|
-
from ksadk.codex
|
|
29
|
-
from ksadk.events.
|
|
35
|
+
from ksadk.events.adapters.codex import CodexAdapterContext, CodexEventAdapter
|
|
36
|
+
from ksadk.events.canonical import (
|
|
37
|
+
ErrorInfo,
|
|
38
|
+
InteractionRequested,
|
|
39
|
+
InteractionResolved,
|
|
40
|
+
RunCanceled,
|
|
41
|
+
RunCompleted,
|
|
42
|
+
RunFailed,
|
|
43
|
+
RunInterrupted,
|
|
44
|
+
RuntimeEvent,
|
|
45
|
+
SourceRef,
|
|
46
|
+
)
|
|
47
|
+
from ksadk.events.identity import stable_event_id, stable_item_id, stable_scope_id
|
|
48
|
+
from ksadk.kernel.contracts import RuntimeCapability, RuntimeCapabilityMatrix
|
|
30
49
|
from ksadk.runtime.adapter import (
|
|
31
50
|
BaseRuntime,
|
|
32
51
|
CancelResult,
|
|
@@ -76,6 +95,7 @@ class _CodexThread:
|
|
|
76
95
|
completed_at: int | None = None
|
|
77
96
|
duration_ms: int | None = None
|
|
78
97
|
goal_mode: bool = False
|
|
98
|
+
continuation_preexisting: bool = False
|
|
79
99
|
|
|
80
100
|
|
|
81
101
|
class CodexRuntimeAdapter(RuntimeAdapter):
|
|
@@ -87,11 +107,13 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
87
107
|
*,
|
|
88
108
|
sandbox_read_only: bool = True,
|
|
89
109
|
turn_timeout_seconds: Optional[float] = None,
|
|
110
|
+
bound_skill_paths: Mapping[str, str] | None = None,
|
|
90
111
|
) -> None:
|
|
91
112
|
super().__init__(_CodexAsBaseRuntime(client))
|
|
92
113
|
self._client = client
|
|
93
114
|
self._sandbox_read_only = sandbox_read_only
|
|
94
115
|
self._turn_timeout_seconds = turn_timeout_seconds
|
|
116
|
+
self._bound_skill_paths = dict(bound_skill_paths or {})
|
|
95
117
|
self._threads: dict[str, _CodexThread] = {}
|
|
96
118
|
self._requests: dict[str, StartRequest] = {}
|
|
97
119
|
self._known_threads: set[str] = set()
|
|
@@ -101,12 +123,59 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
101
123
|
# 可观测:最近一次 cancel 级联丢弃的审批集(contract test 断言用)。
|
|
102
124
|
self.last_cancel_dropped_approvals: set[str] = set()
|
|
103
125
|
self._seq = 0
|
|
126
|
+
self._closed = False
|
|
127
|
+
|
|
128
|
+
# ---- capability matrix(v1,诚实声明) ----
|
|
129
|
+
|
|
130
|
+
def capabilities(self) -> RuntimeCapabilityMatrix:
|
|
131
|
+
"""Codex 真实矩阵:thread 级 cancel/pause/resume + 审批 submit + snapshot
|
|
132
|
+
checkpoint 均为后端原生能力;attach/durable_restore 未实现(线程表在本进程,
|
|
133
|
+
attach seam 缺失),steer/inject 无原生通道。
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
def _unavailable(reason: str) -> RuntimeCapability:
|
|
137
|
+
return RuntimeCapability(supported=False, mode="unavailable", reason=reason)
|
|
138
|
+
|
|
139
|
+
return RuntimeCapabilityMatrix(
|
|
140
|
+
cancel=RuntimeCapability(supported=True, mode="native"),
|
|
141
|
+
pause=RuntimeCapability(supported=True, mode="native"),
|
|
142
|
+
resume=RuntimeCapability(supported=True, mode="native"),
|
|
143
|
+
submit_interaction=RuntimeCapability(supported=True, mode="native"),
|
|
144
|
+
attach=_unavailable("codex_process_local_thread_table"),
|
|
145
|
+
steer=_unavailable("runtime_no_native_steer"),
|
|
146
|
+
inject=_unavailable("runtime_no_native_inject"),
|
|
147
|
+
checkpoint=RuntimeCapability(supported=True, mode="native"),
|
|
148
|
+
durable_restore=_unavailable("codex_durable_restore_requires_attach_seam"),
|
|
149
|
+
goal=RuntimeCapability(supported=True, mode="native"),
|
|
150
|
+
loop=_unavailable("codex_loop_requires_run_control_spec"),
|
|
151
|
+
plan=RuntimeCapability(supported=True, mode="native"),
|
|
152
|
+
interaction_mode="live_submit",
|
|
153
|
+
)
|
|
104
154
|
|
|
105
155
|
# ---- 六动词 ----
|
|
106
156
|
|
|
107
157
|
async def start(self, request: StartRequest) -> RunHandle:
|
|
108
158
|
# 新 thread 由后端分配真实 thread_id(thread_start);metadata 携带的 thread_id
|
|
109
159
|
# 表示接入既有 thread(resume 语义,run_turn 时按 resume 接入)。
|
|
160
|
+
if self._bound_skill_paths:
|
|
161
|
+
skills = request.config.get("skills") if request.config else None
|
|
162
|
+
if isinstance(skills, list):
|
|
163
|
+
projected_skills = []
|
|
164
|
+
for item in skills:
|
|
165
|
+
if not isinstance(item, dict):
|
|
166
|
+
continue
|
|
167
|
+
name = str(item.get("name") or "").strip()
|
|
168
|
+
projected_skills.append(
|
|
169
|
+
{
|
|
170
|
+
**item,
|
|
171
|
+
"path": self._bound_skill_paths.get(
|
|
172
|
+
name, str(item.get("path") or "")
|
|
173
|
+
),
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
request = request.model_copy(
|
|
177
|
+
update={"config": {**request.config, "skills": projected_skills}}
|
|
178
|
+
)
|
|
110
179
|
provided = request.metadata.get("thread_id")
|
|
111
180
|
if provided:
|
|
112
181
|
thread_id = str(provided)
|
|
@@ -128,9 +197,18 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
128
197
|
cwd = request.config.get("cwd")
|
|
129
198
|
if cwd:
|
|
130
199
|
thread_config["cwd"] = str(cwd)
|
|
200
|
+
# AgentKernel creates one adapter/transport per durable turn and
|
|
201
|
+
# closes it after the canonical terminal event. The next turn
|
|
202
|
+
# therefore resumes the native thread from a new app-server
|
|
203
|
+
# process; an ephemeral Codex thread has no rollout and cannot be
|
|
204
|
+
# resumed across that transport boundary.
|
|
205
|
+
thread_config.setdefault("ephemeral", False)
|
|
131
206
|
thread_id = await self._client.start_thread(thread_config)
|
|
132
207
|
self._known_threads.add(thread_id)
|
|
133
|
-
thread = _CodexThread(
|
|
208
|
+
thread = _CodexThread(
|
|
209
|
+
thread_id=thread_id,
|
|
210
|
+
continuation_preexisting=bool(provided),
|
|
211
|
+
)
|
|
134
212
|
thread.__dict__["_start_request"] = request
|
|
135
213
|
self._threads[thread_id] = thread
|
|
136
214
|
self._requests[thread_id] = request
|
|
@@ -245,7 +323,10 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
245
323
|
raise ValueError(f"thread {handle.run_id} 已被中断/杀进程,不持久化,不可 resume")
|
|
246
324
|
self._pending_cancels.discard(handle.run_id)
|
|
247
325
|
self._known_threads.add(target.id)
|
|
248
|
-
thread = _CodexThread(
|
|
326
|
+
thread = _CodexThread(
|
|
327
|
+
thread_id=target.id,
|
|
328
|
+
continuation_preexisting=True,
|
|
329
|
+
)
|
|
249
330
|
thread.__dict__["_resume"] = {"target": target, "payload": payload}
|
|
250
331
|
request = self._requests.get(handle.run_id)
|
|
251
332
|
if request is not None:
|
|
@@ -294,19 +375,43 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
294
375
|
)
|
|
295
376
|
|
|
296
377
|
async def close(self, handle: RunHandle) -> None:
|
|
297
|
-
|
|
298
|
-
self.
|
|
299
|
-
|
|
378
|
+
self._do_not_persist.add(handle.run_id)
|
|
379
|
+
await self.close_all()
|
|
380
|
+
|
|
381
|
+
async def close_all(self) -> None:
|
|
382
|
+
"""Dispose every thread and the activation-owned App Server process.
|
|
383
|
+
|
|
384
|
+
One ``CodexRuntimeAdapter`` owns one client transport. Closing any
|
|
385
|
+
attached Kernel handle therefore closes the transport as a unit; this
|
|
386
|
+
additive helper also lets a draining AgentProvider clean up a runtime
|
|
387
|
+
that has been created but not started yet.
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
if self._closed:
|
|
391
|
+
return
|
|
392
|
+
self._closed = True
|
|
393
|
+
threads = tuple(self._threads.values())
|
|
394
|
+
active_threads = tuple(
|
|
395
|
+
thread for thread in threads if thread.streaming and not thread.done
|
|
396
|
+
)
|
|
397
|
+
for thread in active_threads:
|
|
300
398
|
thread.interrupt_event.set()
|
|
301
399
|
try:
|
|
302
|
-
|
|
303
|
-
|
|
400
|
+
for thread in active_threads:
|
|
401
|
+
await self._client.interrupt_active_turn(thread.thread_id)
|
|
304
402
|
finally:
|
|
305
403
|
# AsyncCodex.close owns terminate/wait/kill for the app-server child.
|
|
306
404
|
await self._client.close()
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
405
|
+
thread_ids = {
|
|
406
|
+
*self._known_threads,
|
|
407
|
+
*self._threads,
|
|
408
|
+
*self._requests,
|
|
409
|
+
}
|
|
410
|
+
self._do_not_persist.update(thread_ids)
|
|
411
|
+
self._threads.clear()
|
|
412
|
+
self._requests.clear()
|
|
413
|
+
self._known_threads.clear()
|
|
414
|
+
self._pending_cancels.clear()
|
|
310
415
|
|
|
311
416
|
# ---- stream → RuntimeEvent(phase 翻译 + 中断竞速) ----
|
|
312
417
|
|
|
@@ -326,18 +431,12 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
326
431
|
|
|
327
432
|
if handle.run_id in self._pending_cancels:
|
|
328
433
|
self._pending_cancels.discard(handle.run_id)
|
|
329
|
-
yield self.
|
|
434
|
+
yield self._make_run_canceled(
|
|
330
435
|
handle,
|
|
331
|
-
|
|
332
|
-
{
|
|
333
|
-
"status": "cancelled",
|
|
334
|
-
"cancel_result": CancelResult.PENDING_CANCEL_RECORDED.value,
|
|
335
|
-
},
|
|
436
|
+
reason=f"pending_cancel:{CancelResult.PENDING_CANCEL_RECORDED.value}",
|
|
336
437
|
)
|
|
337
438
|
return
|
|
338
439
|
|
|
339
|
-
yield self._event(handle, EventType.RUN_STARTED, {"status": "in_progress"})
|
|
340
|
-
tracker = CodexPhaseTracker()
|
|
341
440
|
request = thread.__dict__.get("_start_request")
|
|
342
441
|
resume_state = thread.__dict__.get("_resume")
|
|
343
442
|
if request is not None:
|
|
@@ -350,21 +449,8 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
350
449
|
thread.streaming = True
|
|
351
450
|
thread.turn_id = thread.turn_id or f"turn_{thread.thread_id}"
|
|
352
451
|
try:
|
|
353
|
-
async for event in self._map_codex_stream(handle, thread,
|
|
452
|
+
async for event in self._map_codex_stream(handle, thread, run_input):
|
|
354
453
|
yield event
|
|
355
|
-
# 正常结束(非 interrupt):补 RUN_COMPLETED(AGUI 投射器据此发 RunFinished success)
|
|
356
|
-
if not thread.interrupted:
|
|
357
|
-
completed_payload: dict[str, Any] = {
|
|
358
|
-
"status": "completed",
|
|
359
|
-
"source": "codex",
|
|
360
|
-
}
|
|
361
|
-
if thread.started_at is not None:
|
|
362
|
-
completed_payload["started_at"] = thread.started_at
|
|
363
|
-
if thread.completed_at is not None:
|
|
364
|
-
completed_payload["completed_at"] = thread.completed_at
|
|
365
|
-
if thread.duration_ms is not None:
|
|
366
|
-
completed_payload["duration_ms"] = thread.duration_ms
|
|
367
|
-
yield self._event(handle, EventType.RUN_COMPLETED, completed_payload)
|
|
368
454
|
except asyncio.CancelledError:
|
|
369
455
|
thread.interrupted = True
|
|
370
456
|
self._do_not_persist.add(handle.run_id)
|
|
@@ -377,18 +463,10 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
377
463
|
# Closing the SDK transport terminates and waits for the app-server
|
|
378
464
|
# child even when the stream is stuck between notifications.
|
|
379
465
|
await self._client.close()
|
|
380
|
-
yield self.
|
|
381
|
-
|
|
382
|
-
EventType.RUN_FAILED,
|
|
383
|
-
{"status": "failed", "error": "codex turn timed out"},
|
|
384
|
-
)
|
|
385
|
-
except Exception as exc: # noqa: BLE001 通用兜底:任何异常都发 RUN_FAILED
|
|
466
|
+
yield self._make_run_failed(handle, "codex turn timed out")
|
|
467
|
+
except Exception as exc: # noqa: BLE001 通用兜底:任何异常都发 RunFailed
|
|
386
468
|
self._do_not_persist.add(handle.run_id)
|
|
387
|
-
yield self.
|
|
388
|
-
handle,
|
|
389
|
-
EventType.RUN_FAILED,
|
|
390
|
-
{"status": "failed", "error": str(exc)},
|
|
391
|
-
)
|
|
469
|
+
yield self._make_run_failed(handle, str(exc))
|
|
392
470
|
finally:
|
|
393
471
|
thread.streaming = False
|
|
394
472
|
thread.done = True
|
|
@@ -398,10 +476,15 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
398
476
|
self,
|
|
399
477
|
handle: RunHandle,
|
|
400
478
|
thread: _CodexThread,
|
|
401
|
-
tracker: CodexPhaseTracker,
|
|
402
479
|
prompt: Any,
|
|
403
480
|
) -> AsyncIterator[RuntimeEvent]:
|
|
404
481
|
request = thread.__dict__.get("_start_request") or thread.__dict__.get("_request_config")
|
|
482
|
+
adapter = CodexEventAdapter(
|
|
483
|
+
known_thread_ids=(thread.thread_id,)
|
|
484
|
+
if thread.continuation_preexisting
|
|
485
|
+
else (),
|
|
486
|
+
)
|
|
487
|
+
context = CodexAdapterContext(run_id=self._event_run_id(handle))
|
|
405
488
|
run_config: dict[str, Any] = {"sandbox_read_only": self._sandbox_read_only}
|
|
406
489
|
if request is not None and request.config:
|
|
407
490
|
for key in ("sandbox", "approval_mode", "summary", "collaboration_mode"):
|
|
@@ -496,14 +579,11 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
496
579
|
chunk_task = asyncio.ensure_future(_anext_or_stop(codex_gen))
|
|
497
580
|
chunk_task = None
|
|
498
581
|
thread.interrupted = True
|
|
499
|
-
#
|
|
500
|
-
|
|
582
|
+
# Runtime interrupt (user pause) — adapter doesn't know;
|
|
583
|
+
# emit canonical RunInterrupted explicitly.
|
|
584
|
+
yield self._make_run_interrupted(
|
|
501
585
|
handle,
|
|
502
|
-
|
|
503
|
-
{
|
|
504
|
-
"status": "paused" if thread.paused else "interrupted",
|
|
505
|
-
"reason": "user_pause" if thread.paused else "runtime_interrupt",
|
|
506
|
-
},
|
|
586
|
+
reason="user_pause" if thread.paused else "runtime_interrupt",
|
|
507
587
|
)
|
|
508
588
|
return
|
|
509
589
|
for task in pending:
|
|
@@ -513,8 +593,58 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
513
593
|
chunk = chunk_task.result()
|
|
514
594
|
if chunk is _STREAM_STOP:
|
|
515
595
|
return
|
|
516
|
-
event
|
|
517
|
-
|
|
596
|
+
# TODO(runtime-event-v2): use real native cursor from chunk if
|
|
597
|
+
# available; fallback to thread:seq for now.
|
|
598
|
+
native_cursor = f"{thread.thread_id}:{self._next_seq()}"
|
|
599
|
+
# autoApprovalReview 不产生 canonical 事件(adapter 静默),但
|
|
600
|
+
# cancel 级联丢弃审批的契约依赖 runtime 的 pending 跟踪。
|
|
601
|
+
chunk_method = (
|
|
602
|
+
str((chunk or {}).get("method") or "")
|
|
603
|
+
if isinstance(chunk, dict)
|
|
604
|
+
else ""
|
|
605
|
+
)
|
|
606
|
+
if chunk_method in {
|
|
607
|
+
"item/autoApprovalReview/started",
|
|
608
|
+
"item/autoApprovalReview/completed",
|
|
609
|
+
}:
|
|
610
|
+
review_params = chunk.get("params") or {}
|
|
611
|
+
review_id = str(
|
|
612
|
+
review_params.get("reviewId")
|
|
613
|
+
or review_params.get("review_id")
|
|
614
|
+
or ""
|
|
615
|
+
)
|
|
616
|
+
if review_id:
|
|
617
|
+
if chunk_method.endswith("started"):
|
|
618
|
+
thread.pending_approvals.add(review_id)
|
|
619
|
+
else:
|
|
620
|
+
thread.pending_approvals.discard(review_id)
|
|
621
|
+
for event in adapter.map_protocol_message(
|
|
622
|
+
chunk,
|
|
623
|
+
context,
|
|
624
|
+
native_cursor=native_cursor,
|
|
625
|
+
timestamp=time.time(),
|
|
626
|
+
):
|
|
627
|
+
event = self._with_caller_scope(event, request)
|
|
628
|
+
# 跟踪 pending 审批(cancel 级联丢弃契约依赖该集合)。
|
|
629
|
+
if isinstance(event, InteractionRequested):
|
|
630
|
+
if event.interaction_id:
|
|
631
|
+
thread.pending_approvals.add(event.interaction_id)
|
|
632
|
+
call_id = getattr(event.request, "call_id", None)
|
|
633
|
+
if call_id:
|
|
634
|
+
thread.pending_approvals.add(str(call_id))
|
|
635
|
+
elif isinstance(event, InteractionResolved):
|
|
636
|
+
thread.pending_approvals.discard(event.interaction_id)
|
|
637
|
+
call_id = getattr(event.response, "call_id", None)
|
|
638
|
+
if call_id:
|
|
639
|
+
thread.pending_approvals.discard(str(call_id))
|
|
640
|
+
if isinstance(event, (RunCompleted, RunFailed, RunCanceled)):
|
|
641
|
+
# The Kernel stops consuming as soon as it persists a
|
|
642
|
+
# canonical terminal fact, so generator ``finally`` may
|
|
643
|
+
# not run before worker cleanup calls ``close``. Mark the
|
|
644
|
+
# native turn terminal before yielding that fact; close
|
|
645
|
+
# must terminate the transport without sending a stale
|
|
646
|
+
# turn/interrupt RPC to an already-completed app-server.
|
|
647
|
+
thread.done = True
|
|
518
648
|
yield event
|
|
519
649
|
finally:
|
|
520
650
|
waiter_tasks = [task for task in (chunk_task, interrupt_task) if task is not None]
|
|
@@ -530,285 +660,155 @@ class CodexRuntimeAdapter(RuntimeAdapter):
|
|
|
530
660
|
except Exception: # noqa: BLE001
|
|
531
661
|
pass
|
|
532
662
|
|
|
533
|
-
|
|
663
|
+
# ---- canonical run.* helpers (for runtime-owned lifecycle) ----
|
|
664
|
+
|
|
665
|
+
def _event_run_id(self, handle: RunHandle) -> str:
|
|
666
|
+
"""事件的 canonical run_id:调用方 invocation_id 优先,退回 thread id。
|
|
667
|
+
|
|
668
|
+
``handle.run_id`` 是 codex 原生 thread id(resume/cancel 按 thread 寻址);
|
|
669
|
+
但 canonical RuntimeEvent 的 run_id 必须与调用方
|
|
670
|
+
``StartRequest.metadata['invocation_id']`` 一致(conversation kernel 的
|
|
671
|
+
event scope 校验),否则 hosted/web 执行路径会在首个事件上 fail。
|
|
672
|
+
"""
|
|
673
|
+
request = self._requests.get(handle.run_id)
|
|
674
|
+
if request is not None:
|
|
675
|
+
invocation_id = str(
|
|
676
|
+
(getattr(request, "metadata", None) or {}).get("invocation_id") or ""
|
|
677
|
+
).strip()
|
|
678
|
+
if invocation_id:
|
|
679
|
+
return invocation_id
|
|
680
|
+
return handle.run_id
|
|
681
|
+
|
|
682
|
+
def _make_source(self, handle: RunHandle) -> SourceRef:
|
|
683
|
+
request = self._requests.get(handle.run_id)
|
|
684
|
+
return SourceRef(
|
|
685
|
+
framework="codex",
|
|
686
|
+
native_run_id=handle.run_id,
|
|
687
|
+
metadata={
|
|
688
|
+
"agent_id": (
|
|
689
|
+
str(request.agent_id or "codex") if request is not None else "codex"
|
|
690
|
+
),
|
|
691
|
+
"user_id": (
|
|
692
|
+
request.user_id
|
|
693
|
+
if request is not None
|
|
694
|
+
else str(handle.native_ref.get("user_id") or "user")
|
|
695
|
+
),
|
|
696
|
+
"session_id": handle.session_id,
|
|
697
|
+
"invocation_id": (
|
|
698
|
+
str(request.metadata.get("invocation_id") or handle.run_id)
|
|
699
|
+
if request is not None
|
|
700
|
+
else handle.run_id
|
|
701
|
+
),
|
|
702
|
+
},
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
def _with_caller_scope(self, event: RuntimeEvent, request: Any) -> RuntimeEvent:
|
|
706
|
+
"""把调用方 scope(request 的 agent/user/session/invocation)并入事件 source。"""
|
|
707
|
+
|
|
708
|
+
if request is None:
|
|
709
|
+
return event
|
|
710
|
+
caller_scope = {
|
|
711
|
+
"agent_id": str(getattr(request, "agent_id", "") or "codex"),
|
|
712
|
+
"user_id": str(getattr(request, "user_id", "") or "user"),
|
|
713
|
+
"session_id": str(getattr(request, "session_id", "") or ""),
|
|
714
|
+
"invocation_id": str(
|
|
715
|
+
(getattr(request, "metadata", None) or {}).get("invocation_id")
|
|
716
|
+
or ""
|
|
717
|
+
),
|
|
718
|
+
}
|
|
719
|
+
merged = {**caller_scope, **dict(event.source.metadata or {})}
|
|
720
|
+
# adapter 自身字段优先;仅补齐缺失的调用方 scope 键。
|
|
721
|
+
for key, value in caller_scope.items():
|
|
722
|
+
if not merged.get(key):
|
|
723
|
+
merged[key] = value
|
|
724
|
+
source = event.source.model_copy(update={"metadata": merged})
|
|
725
|
+
return event.model_copy(update={"source": source})
|
|
726
|
+
|
|
727
|
+
def _canonical_kwargs(
|
|
534
728
|
self,
|
|
535
729
|
handle: RunHandle,
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return None
|
|
557
|
-
|
|
558
|
-
if method == "thread/tokenUsage/updated":
|
|
559
|
-
token_usage = params.get("token_usage") or params.get("tokenUsage") or {}
|
|
560
|
-
last = token_usage.get("last") if isinstance(token_usage, dict) else {}
|
|
561
|
-
if not isinstance(last, dict):
|
|
562
|
-
last = {}
|
|
563
|
-
return self._event(
|
|
564
|
-
handle,
|
|
565
|
-
EventType.USAGE_REPORTED,
|
|
566
|
-
{
|
|
567
|
-
"input_tokens": int(last.get("input_tokens", last.get("inputTokens", 0)) or 0),
|
|
568
|
-
"cached_tokens": int(
|
|
569
|
-
last.get("cached_input_tokens", last.get("cachedInputTokens", 0)) or 0
|
|
570
|
-
),
|
|
571
|
-
"output_tokens": int(
|
|
572
|
-
last.get("output_tokens", last.get("outputTokens", 0)) or 0
|
|
573
|
-
),
|
|
574
|
-
"reasoning_tokens": int(
|
|
575
|
-
last.get(
|
|
576
|
-
"reasoning_output_tokens",
|
|
577
|
-
last.get("reasoningOutputTokens", 0),
|
|
578
|
-
)
|
|
579
|
-
or 0
|
|
580
|
-
),
|
|
581
|
-
"total_tokens": int(last.get("total_tokens", last.get("totalTokens", 0)) or 0),
|
|
582
|
-
"source": "codex",
|
|
583
|
-
},
|
|
584
|
-
)
|
|
585
|
-
if method == "thread/goal/updated":
|
|
586
|
-
goal = params.get("goal") if isinstance(params, dict) else {}
|
|
587
|
-
goal = goal if isinstance(goal, dict) else {}
|
|
588
|
-
status = str(goal.get("status") or "").lower()
|
|
589
|
-
if status in {"paused", "blocked", "usage_limited", "budget_limited"}:
|
|
590
|
-
thread.paused = status == "paused"
|
|
591
|
-
thread.interrupted = True
|
|
592
|
-
return self._event(
|
|
593
|
-
handle,
|
|
594
|
-
EventType.RUN_INTERRUPTED,
|
|
595
|
-
{"status": status, "reason": "goal_status", "goal": goal},
|
|
596
|
-
)
|
|
597
|
-
return self._event(
|
|
598
|
-
handle,
|
|
599
|
-
EventType.RUN_PROGRESS,
|
|
600
|
-
{"native_event": "goal.updated", "native_data": goal},
|
|
601
|
-
)
|
|
602
|
-
if method in {"turn/started", "turn/completed"}:
|
|
603
|
-
raw_turn = params.get("turn")
|
|
604
|
-
turn: dict[str, Any] = raw_turn if isinstance(raw_turn, dict) else {}
|
|
605
|
-
started_at = turn.get("started_at", turn.get("startedAt"))
|
|
606
|
-
completed_at = turn.get("completed_at", turn.get("completedAt"))
|
|
607
|
-
duration_ms = turn.get("duration_ms", turn.get("durationMs"))
|
|
608
|
-
if started_at is not None:
|
|
609
|
-
thread.started_at = int(started_at)
|
|
610
|
-
if completed_at is not None:
|
|
611
|
-
thread.completed_at = int(completed_at)
|
|
612
|
-
if duration_ms is not None:
|
|
613
|
-
thread.duration_ms = max(0, int(duration_ms))
|
|
614
|
-
return None
|
|
615
|
-
|
|
616
|
-
if method == "a2ui/surface":
|
|
617
|
-
surface_id = str(params.get("surface_id") or params.get("surfaceId") or "")
|
|
618
|
-
return self._event(
|
|
619
|
-
handle,
|
|
620
|
-
EventType.A2UI_SURFACE_BEGIN,
|
|
621
|
-
{
|
|
622
|
-
"surface_id": surface_id,
|
|
623
|
-
"surface": params.get("surface")
|
|
624
|
-
if isinstance(params.get("surface"), dict)
|
|
625
|
-
else {},
|
|
626
|
-
},
|
|
627
|
-
)
|
|
628
|
-
if method == "a2ui/interaction":
|
|
629
|
-
interaction_id = str(params.get("interaction_id") or params.get("interactionId") or "")
|
|
630
|
-
if interaction_id:
|
|
631
|
-
thread.pending_approvals.add(interaction_id)
|
|
632
|
-
return self._event(
|
|
633
|
-
handle,
|
|
634
|
-
EventType.A2UI_INTERACTION,
|
|
635
|
-
{
|
|
636
|
-
"surface_id": str(params.get("surface_id") or params.get("surfaceId") or ""),
|
|
637
|
-
"interaction_id": interaction_id,
|
|
638
|
-
"kind": str(params.get("kind") or "form"),
|
|
639
|
-
"input_schema": params.get("input_schema")
|
|
640
|
-
if isinstance(params.get("input_schema"), dict)
|
|
641
|
-
else {},
|
|
642
|
-
"is_blocking": bool(params.get("is_blocking", True)),
|
|
643
|
-
},
|
|
644
|
-
)
|
|
730
|
+
*,
|
|
731
|
+
scope_id: str,
|
|
732
|
+
item_id: str,
|
|
733
|
+
event_type: str,
|
|
734
|
+
part_id: str,
|
|
735
|
+
) -> dict[str, Any]:
|
|
736
|
+
framework = "codex"
|
|
737
|
+
run_id = self._event_run_id(handle)
|
|
738
|
+
n = self._next_seq()
|
|
739
|
+
return {
|
|
740
|
+
"schema_version": 2,
|
|
741
|
+
"event_id": stable_event_id(
|
|
742
|
+
framework, scope_id, item_id, event_type, part_id, run_id, n
|
|
743
|
+
),
|
|
744
|
+
"seq": n,
|
|
745
|
+
"timestamp": time.time(),
|
|
746
|
+
"run_id": run_id,
|
|
747
|
+
"scope_id": scope_id,
|
|
748
|
+
"source": self._make_source(handle),
|
|
749
|
+
}
|
|
645
750
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
"call_id": call_id,
|
|
656
|
-
"name": "codex.command",
|
|
657
|
-
"args": {
|
|
658
|
-
"command": str(item.get("command") or ""),
|
|
659
|
-
"cwd": str(item.get("cwd") or ""),
|
|
660
|
-
"command_actions": item.get("commandActions")
|
|
661
|
-
or item.get("command_actions")
|
|
662
|
-
or [],
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
)
|
|
666
|
-
if item.get("type") == "mcpToolCall":
|
|
667
|
-
call_id = str(item.get("id") or "")
|
|
668
|
-
server = str(item.get("server") or "")
|
|
669
|
-
tool = str(item.get("tool") or "")
|
|
670
|
-
return self._event(
|
|
671
|
-
handle,
|
|
672
|
-
EventType.TOOL_CALL_BEGIN,
|
|
673
|
-
{
|
|
674
|
-
"call_id": call_id,
|
|
675
|
-
"name": f"mcp.{server}.{tool}" if server else f"mcp.{tool}",
|
|
676
|
-
"args": {
|
|
677
|
-
"server": server,
|
|
678
|
-
"tool": tool,
|
|
679
|
-
"arguments": item.get("arguments"),
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
)
|
|
683
|
-
return None
|
|
684
|
-
if method == "item/completed":
|
|
685
|
-
item = params.get("item") or params
|
|
686
|
-
item_type = item.get("type")
|
|
687
|
-
if item_type == "commandExecution":
|
|
688
|
-
tracker.forget_item(params)
|
|
689
|
-
call_id = str(item.get("id") or "")
|
|
690
|
-
return self._event(
|
|
691
|
-
handle,
|
|
692
|
-
EventType.TOOL_CALL_END,
|
|
693
|
-
{
|
|
694
|
-
"call_id": call_id,
|
|
695
|
-
"name": "codex.command",
|
|
696
|
-
"result": {
|
|
697
|
-
"status": str(item.get("status") or "completed"),
|
|
698
|
-
"exit_code": item.get("exitCode", item.get("exit_code")),
|
|
699
|
-
"duration_ms": item.get("durationMs", item.get("duration_ms")),
|
|
700
|
-
"output": str(
|
|
701
|
-
item.get("aggregatedOutput") or item.get("aggregated_output") or ""
|
|
702
|
-
),
|
|
703
|
-
},
|
|
704
|
-
},
|
|
705
|
-
)
|
|
706
|
-
if item_type == "mcpToolCall":
|
|
707
|
-
tracker.forget_item(params)
|
|
708
|
-
call_id = str(item.get("id") or "")
|
|
709
|
-
server = str(item.get("server") or "")
|
|
710
|
-
tool = str(item.get("tool") or "")
|
|
711
|
-
raw_result = item.get("result")
|
|
712
|
-
result_obj: dict[str, Any] = raw_result if isinstance(raw_result, dict) else {}
|
|
713
|
-
raw_error = item.get("error")
|
|
714
|
-
error_obj: dict[str, Any] = raw_error if isinstance(raw_error, dict) else {}
|
|
715
|
-
output = self._mcp_result_text(result_obj)
|
|
716
|
-
error_message = str(error_obj.get("message") or "")
|
|
717
|
-
if not output and error_message:
|
|
718
|
-
output = error_message
|
|
719
|
-
return self._event(
|
|
720
|
-
handle,
|
|
721
|
-
EventType.TOOL_CALL_END,
|
|
722
|
-
{
|
|
723
|
-
"call_id": call_id,
|
|
724
|
-
"name": f"mcp.{server}.{tool}" if server else f"mcp.{tool}",
|
|
725
|
-
"result": {
|
|
726
|
-
"status": str(item.get("status") or "completed"),
|
|
727
|
-
"duration_ms": item.get("durationMs", item.get("duration_ms")),
|
|
728
|
-
"output": output,
|
|
729
|
-
**({"error": error_message} if error_message else {}),
|
|
730
|
-
},
|
|
731
|
-
},
|
|
732
|
-
)
|
|
733
|
-
if item_type != "agentMessage":
|
|
734
|
-
tracker.forget_item(params)
|
|
735
|
-
return None
|
|
736
|
-
phase = tracker.runtime_phase_for_item(params)
|
|
737
|
-
tracker.forget_item(params)
|
|
738
|
-
text = str(item.get("text") or "")
|
|
739
|
-
return self._event(
|
|
751
|
+
def _make_run_canceled(
|
|
752
|
+
self, handle: RunHandle, *, reason: str | None = None
|
|
753
|
+
) -> RunCanceled:
|
|
754
|
+
framework = "codex"
|
|
755
|
+
run_id = self._event_run_id(handle)
|
|
756
|
+
scope_id = stable_scope_id(framework, run_id)
|
|
757
|
+
item_id = stable_item_id(framework, run_id, "$run")
|
|
758
|
+
return RunCanceled(
|
|
759
|
+
**self._canonical_kwargs(
|
|
740
760
|
handle,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
review_id = str(params.get("review_id") or params.get("reviewId") or "")
|
|
760
|
-
thread.pending_approvals.discard(review_id)
|
|
761
|
-
return None
|
|
762
|
-
if (
|
|
763
|
-
"approval" in method.lower()
|
|
764
|
-
or "requestPermission" in method
|
|
765
|
-
or "approval" in str(chunk.get("type") or "").lower()
|
|
766
|
-
):
|
|
767
|
-
call_id = str(
|
|
768
|
-
params.get("id") or params.get("call_id") or params.get("requestId") or ""
|
|
769
|
-
)
|
|
770
|
-
if call_id:
|
|
771
|
-
thread.pending_approvals.add(call_id)
|
|
772
|
-
return self._event(
|
|
761
|
+
scope_id=scope_id,
|
|
762
|
+
item_id=item_id,
|
|
763
|
+
event_type="run.canceled",
|
|
764
|
+
part_id="run",
|
|
765
|
+
),
|
|
766
|
+
status="canceled",
|
|
767
|
+
reason=reason,
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
def _make_run_interrupted(
|
|
771
|
+
self, handle: RunHandle, *, reason: str | None = None
|
|
772
|
+
) -> RunInterrupted:
|
|
773
|
+
framework = "codex"
|
|
774
|
+
run_id = self._event_run_id(handle)
|
|
775
|
+
scope_id = stable_scope_id(framework, run_id)
|
|
776
|
+
item_id = stable_item_id(framework, run_id, "$run")
|
|
777
|
+
return RunInterrupted(
|
|
778
|
+
**self._canonical_kwargs(
|
|
773
779
|
handle,
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
},
|
|
783
|
-
)
|
|
784
|
-
return None
|
|
780
|
+
scope_id=scope_id,
|
|
781
|
+
item_id=item_id,
|
|
782
|
+
event_type="run.interrupted",
|
|
783
|
+
part_id="run",
|
|
784
|
+
),
|
|
785
|
+
status="interrupted",
|
|
786
|
+
reason=reason,
|
|
787
|
+
)
|
|
785
788
|
|
|
786
|
-
def
|
|
787
|
-
self,
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if request is not None
|
|
801
|
-
else str(handle.native_ref.get("user_id") or "user")
|
|
789
|
+
def _make_run_failed(
|
|
790
|
+
self, handle: RunHandle, error_message: str
|
|
791
|
+
) -> RunFailed:
|
|
792
|
+
framework = "codex"
|
|
793
|
+
run_id = self._event_run_id(handle)
|
|
794
|
+
scope_id = stable_scope_id(framework, run_id)
|
|
795
|
+
item_id = stable_item_id(framework, run_id, "$run")
|
|
796
|
+
return RunFailed(
|
|
797
|
+
**self._canonical_kwargs(
|
|
798
|
+
handle,
|
|
799
|
+
scope_id=scope_id,
|
|
800
|
+
item_id=item_id,
|
|
801
|
+
event_type="run.failed",
|
|
802
|
+
part_id="run",
|
|
802
803
|
),
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
804
|
+
status="failed",
|
|
805
|
+
error=ErrorInfo(
|
|
806
|
+
code="codex_runtime_failed",
|
|
807
|
+
message=error_message,
|
|
808
|
+
source="codex",
|
|
809
|
+
scope_id=scope_id,
|
|
810
|
+
source_ref=self._make_source(handle),
|
|
808
811
|
),
|
|
809
|
-
seq_id=self._next_seq(),
|
|
810
|
-
phase=phase,
|
|
811
|
-
payload=payload,
|
|
812
812
|
)
|
|
813
813
|
|
|
814
814
|
@staticmethod
|
|
@@ -850,6 +850,34 @@ def _resume_prompt(payload: Optional[ResumePayload]) -> Any:
|
|
|
850
850
|
return json.dumps(payload.data, ensure_ascii=False, sort_keys=True)
|
|
851
851
|
|
|
852
852
|
|
|
853
|
+
def _coerce_prompt_text(value: Any) -> Any:
|
|
854
|
+
"""把 canonical message 形态的 input 压成 SDK 可接受的文本。
|
|
855
|
+
|
|
856
|
+
``openai-codex`` 0.147 的 run input 只接受 TextInput/str;请求侧没有
|
|
857
|
+
conversation preprocessing 时 ``request.input`` 可能是
|
|
858
|
+
``[{role, content}]`` 历史列表,直接透传会 ``unsupported input item``。
|
|
859
|
+
"""
|
|
860
|
+
if isinstance(value, str) or value is None:
|
|
861
|
+
return value
|
|
862
|
+
if isinstance(value, dict):
|
|
863
|
+
content = value.get("content") if "role" in value else value.get("text")
|
|
864
|
+
if isinstance(content, str):
|
|
865
|
+
return content
|
|
866
|
+
if isinstance(content, dict):
|
|
867
|
+
text = content.get("text") or content.get("content")
|
|
868
|
+
if isinstance(text, str):
|
|
869
|
+
return text
|
|
870
|
+
return str(value)
|
|
871
|
+
if isinstance(value, list):
|
|
872
|
+
texts = [
|
|
873
|
+
text
|
|
874
|
+
for text in (_coerce_prompt_text(item) for item in value)
|
|
875
|
+
if isinstance(text, str) and text
|
|
876
|
+
]
|
|
877
|
+
return "\n".join(texts) if texts else str(value)
|
|
878
|
+
return str(value)
|
|
879
|
+
|
|
880
|
+
|
|
853
881
|
def _request_prompt(request: StartRequest) -> Any:
|
|
854
882
|
"""Render canonical conversation history for a native Codex turn.
|
|
855
883
|
|
|
@@ -859,11 +887,21 @@ def _request_prompt(request: StartRequest) -> Any:
|
|
|
859
887
|
# A resumed Codex thread already owns its transcript. Re-sending Studio's
|
|
860
888
|
# transport-neutral history would duplicate every prior turn after refresh.
|
|
861
889
|
if str(request.metadata.get("thread_id") or "").strip():
|
|
862
|
-
return
|
|
890
|
+
return (
|
|
891
|
+
request.input
|
|
892
|
+
if _is_structured_turn_input(request.input)
|
|
893
|
+
else _coerce_prompt_text(request.input)
|
|
894
|
+
)
|
|
863
895
|
|
|
864
896
|
conversation = request.conversation_preprocessing()
|
|
865
897
|
if conversation is None or not conversation.messages:
|
|
866
|
-
|
|
898
|
+
# Keep native text/image/mention parts intact for _build_run_input().
|
|
899
|
+
# Flattening this list turns an image dict into user-visible text.
|
|
900
|
+
return (
|
|
901
|
+
request.input
|
|
902
|
+
if _is_structured_turn_input(request.input)
|
|
903
|
+
else _coerce_prompt_text(request.input)
|
|
904
|
+
)
|
|
867
905
|
|
|
868
906
|
lines: list[str] = []
|
|
869
907
|
for message in conversation.messages:
|
|
@@ -880,6 +918,13 @@ def _request_prompt(request: StartRequest) -> Any:
|
|
|
880
918
|
return "\n".join(lines) or request.input
|
|
881
919
|
|
|
882
920
|
|
|
921
|
+
def _is_structured_turn_input(value: Any) -> bool:
|
|
922
|
+
return isinstance(value, list) and any(
|
|
923
|
+
isinstance(item, dict) and isinstance(item.get("type"), str)
|
|
924
|
+
for item in value
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
|
|
883
928
|
def _build_run_input(request: Optional[StartRequest], prompt: Any) -> Any:
|
|
884
929
|
"""Compose Codex skills and native text/image/mention turn input."""
|
|
885
930
|
try:
|
|
@@ -905,7 +950,11 @@ def _build_run_input(request: Optional[StartRequest], prompt: Any) -> Any:
|
|
|
905
950
|
if not isinstance(item, dict):
|
|
906
951
|
continue
|
|
907
952
|
kind = str(item.get("type") or "")
|
|
908
|
-
if kind
|
|
953
|
+
if not kind and "role" in item:
|
|
954
|
+
# canonical conversation message({role, content});当前 input
|
|
955
|
+
# 已由 prompt(或 conversation preprocessing)承载,跳过历史项。
|
|
956
|
+
continue
|
|
957
|
+
if kind in {"text", "input_text"}:
|
|
909
958
|
text = str(
|
|
910
959
|
prompt
|
|
911
960
|
if not text_replaced and isinstance(prompt, str)
|
|
@@ -914,10 +963,35 @@ def _build_run_input(request: Optional[StartRequest], prompt: Any) -> Any:
|
|
|
914
963
|
text_replaced = True
|
|
915
964
|
if text:
|
|
916
965
|
native_items.append(TextInput(text=text))
|
|
917
|
-
elif kind
|
|
918
|
-
|
|
966
|
+
elif kind in {"image", "input_image"} and (
|
|
967
|
+
item.get("url") or item.get("image_url")
|
|
968
|
+
):
|
|
969
|
+
native_items.append(
|
|
970
|
+
ImageInput(url=str(item.get("url") or item.get("image_url")))
|
|
971
|
+
)
|
|
919
972
|
elif kind == "localImage" and item.get("path"):
|
|
920
973
|
native_items.append(LocalImageInput(path=str(item["path"])))
|
|
974
|
+
elif kind == "input_file" and (
|
|
975
|
+
item.get("file_data")
|
|
976
|
+
or str(item.get("file_url") or "").startswith("data:")
|
|
977
|
+
):
|
|
978
|
+
file_path = _materialize_inline_file(
|
|
979
|
+
str(item.get("file_data") or item.get("file_url")),
|
|
980
|
+
str(item.get("filename") or "attachment"),
|
|
981
|
+
)
|
|
982
|
+
if file_path is not None:
|
|
983
|
+
# App Server's ``mention`` input is presentation metadata:
|
|
984
|
+
# current Codex versions do not include it in the model's
|
|
985
|
+
# user message. Always add an explicit model-visible
|
|
986
|
+
# attachment context as well. Small textual files are
|
|
987
|
+
# inlined deterministically; binary/large files expose a
|
|
988
|
+
# sandbox-readable path that Codex can inspect with tools.
|
|
989
|
+
native_items.append(
|
|
990
|
+
TextInput(text=_attachment_context_text(file_path, item))
|
|
991
|
+
)
|
|
992
|
+
native_items.append(
|
|
993
|
+
MentionInput(name=file_path.name, path=str(file_path))
|
|
994
|
+
)
|
|
921
995
|
elif kind == "mention" and item.get("path"):
|
|
922
996
|
native_items.append(
|
|
923
997
|
MentionInput(
|
|
@@ -937,4 +1011,74 @@ def _build_run_input(request: Optional[StartRequest], prompt: Any) -> Any:
|
|
|
937
1011
|
return combined
|
|
938
1012
|
|
|
939
1013
|
|
|
1014
|
+
def _materialize_inline_file(data_url: str, filename: str) -> Path | None:
|
|
1015
|
+
"""Materialize a bounded Studio inline attachment for native Codex."""
|
|
1016
|
+
|
|
1017
|
+
match = re.fullmatch(r"data:([^;,]+)?;base64,([A-Za-z0-9+/=\s]+)", data_url)
|
|
1018
|
+
encoded = match.group(2) if match is not None else data_url.strip()
|
|
1019
|
+
try:
|
|
1020
|
+
payload = base64.b64decode(encoded, validate=True)
|
|
1021
|
+
except (ValueError, binascii.Error):
|
|
1022
|
+
return None
|
|
1023
|
+
if not payload or len(payload) > 10 * 1024 * 1024:
|
|
1024
|
+
return None
|
|
1025
|
+
safe_name = re.sub(r"[^A-Za-z0-9._-]+", "-", Path(filename).name).strip(".-")
|
|
1026
|
+
safe_name = safe_name[:120] or "attachment"
|
|
1027
|
+
digest = hashlib.sha256(payload).hexdigest()
|
|
1028
|
+
path = Path("/tmp/ksadk-codex-attachments") / digest / safe_name
|
|
1029
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1030
|
+
if not path.exists():
|
|
1031
|
+
path.write_bytes(payload)
|
|
1032
|
+
return path
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
_MAX_INLINE_ATTACHMENT_TEXT_BYTES = 64 * 1024
|
|
1036
|
+
_TEXT_ATTACHMENT_SUFFIXES = {
|
|
1037
|
+
".csv",
|
|
1038
|
+
".html",
|
|
1039
|
+
".htm",
|
|
1040
|
+
".ini",
|
|
1041
|
+
".json",
|
|
1042
|
+
".jsonl",
|
|
1043
|
+
".log",
|
|
1044
|
+
".md",
|
|
1045
|
+
".py",
|
|
1046
|
+
".rst",
|
|
1047
|
+
".toml",
|
|
1048
|
+
".tsv",
|
|
1049
|
+
".txt",
|
|
1050
|
+
".xml",
|
|
1051
|
+
".yaml",
|
|
1052
|
+
".yml",
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def _attachment_context_text(path: Path, item: Mapping[str, Any]) -> str:
|
|
1057
|
+
"""Build model-visible context for a materialized Responses input file."""
|
|
1058
|
+
|
|
1059
|
+
name = str(item.get("filename") or path.name).replace('"', "'")
|
|
1060
|
+
inline_data = item.get("inlineData")
|
|
1061
|
+
inline_mime = inline_data.get("mimeType") if isinstance(inline_data, Mapping) else None
|
|
1062
|
+
mime_type = str(item.get("mime_type") or inline_mime or "").strip().lower()
|
|
1063
|
+
source = str(item.get("file_data") or item.get("file_url") or "")
|
|
1064
|
+
data_url_match = re.match(r"data:([^;,]+)", source)
|
|
1065
|
+
if not mime_type and data_url_match is not None:
|
|
1066
|
+
mime_type = data_url_match.group(1).strip().lower()
|
|
1067
|
+
is_text = mime_type.startswith("text/") or path.suffix.lower() in _TEXT_ATTACHMENT_SUFFIXES
|
|
1068
|
+
header = f'<uploaded_attachment name="{name}" path="{path}">'
|
|
1069
|
+
if not is_text:
|
|
1070
|
+
return (
|
|
1071
|
+
f"{header}\n"
|
|
1072
|
+
"The uploaded file is available at the path above. Read it with an appropriate "
|
|
1073
|
+
"tool before answering questions about its contents.\n"
|
|
1074
|
+
"</uploaded_attachment>"
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
raw = path.read_bytes()
|
|
1078
|
+
truncated = len(raw) > _MAX_INLINE_ATTACHMENT_TEXT_BYTES
|
|
1079
|
+
text = raw[:_MAX_INLINE_ATTACHMENT_TEXT_BYTES].decode("utf-8", errors="replace")
|
|
1080
|
+
suffix = "\n[attachment content truncated]" if truncated else ""
|
|
1081
|
+
return f"{header}\n{text}{suffix}\n</uploaded_attachment>"
|
|
1082
|
+
|
|
1083
|
+
|
|
940
1084
|
__all__ = ["CodexRuntimeAdapter"]
|