agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__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.2.dist-info}/METADATA +22 -2
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
- 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 +243 -0
- ksadk/agui/a2ui_projection.py +12 -5
- ksadk/agui/agent.py +443 -259
- ksadk/api/client.py +525 -27
- ksadk/builders/code_builder.py +284 -8
- ksadk/builders/container_builder.py +8 -1
- ksadk/builders/managed_runtime_builder.py +39 -35
- ksadk/cli/__init__.py +17 -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_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 +428 -326
- ksadk/configs/env_registry.py +66 -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/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 +344 -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/context.py +126 -6
- ksadk/conversations/message_projection.py +292 -28
- ksadk/conversations/model_context.py +49 -3
- 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 +753 -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 +717 -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/runtime.py +160 -57
- 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 +61 -0
- ksadk/kernel/contracts.py +362 -0
- ksadk/kernel/control.py +219 -0
- ksadk/kernel/errors.py +79 -0
- ksadk/kernel/ingress.py +1102 -0
- ksadk/kernel/mapping.py +111 -0
- ksadk/kernel/memory_store.py +1133 -0
- ksadk/kernel/postgres_store.py +1756 -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 +1410 -0
- ksadk/kernel/state.py +145 -0
- ksadk/kernel/store.py +232 -0
- ksadk/kernel/worker.py +953 -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 +490 -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/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 +788 -0
- ksadk/runtime/adapter.py +118 -2
- ksadk/runtime/conversation_execution.py +316 -182
- ksadk/runtime/executor.py +144 -3
- ksadk/runtime/factory.py +193 -7
- ksadk/runtime/hosted_finalizer.py +268 -0
- ksadk/runtime/launch.py +14 -1
- ksadk/runtime/preprocessing.py +25 -3
- ksadk/runtime/runner_adapter.py +288 -508
- ksadk/runtime/usage.py +33 -0
- ksadk/sandbox/backends/e2b.py +35 -2
- 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-DSU1sWD_.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
- ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
- ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
- ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
- ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
- ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
- ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
- ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
- ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
- ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
- ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
- ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
- ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
- ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
- ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
- ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
- ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
- ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
- ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
- ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
- ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
- ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
- ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
- ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
- ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
- ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
- ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
- ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
- ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
- ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
- ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
- ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
- ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
- ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
- ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
- ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
- ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
- ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
- ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
- ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
- ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
- ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
- ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
- ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
- ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
- ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
- ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
- ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
- ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
- ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
- ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
- ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
- ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
- ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
- ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
- ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
- ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
- ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
- ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
- ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
- ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
- ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
- ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
- ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
- ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
- ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
- ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
- ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
- ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
- ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.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/studio/api.py +577 -25
- ksadk/studio/api_contracts.py +114 -5
- ksadk/studio/api_memory_routes.py +82 -0
- ksadk/studio/authoring.py +252 -27
- ksadk/studio/authoring_coordinator.py +799 -67
- ksadk/studio/builder.py +88 -11
- ksadk/studio/capabilities.py +17 -0
- ksadk/studio/cloud.py +1916 -95
- ksadk/studio/codex_agent_service.py +125 -14
- ksadk/studio/codex_authoring.py +469 -0
- ksadk/studio/codex_builder.py +138 -25
- ksadk/studio/codex_manifest.py +51 -22
- ksadk/studio/codex_run.py +46 -3
- ksadk/studio/compiler.py +1 -0
- ksadk/studio/contracts.py +114 -3
- ksadk/studio/event_store.py +25 -95
- ksadk/studio/framework_run.py +231 -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 +37 -5
- ksadk/studio/otel_trace.py +348 -42
- ksadk/studio/pcm_memory.py +85 -0
- ksadk/studio/repository.py +23 -2
- ksadk/studio/resource_catalog.py +43 -0
- ksadk/studio/run_service.py +563 -137
- ksadk/studio/runtime_source.py +42 -16
- ksadk/studio/service.py +1180 -72
- ksadk/studio/shared_web.py +32 -22
- ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
- ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/studio/templates.py +100 -8
- ksadk/studio/validator.py +19 -0
- ksadk/studio/workspace.py +30 -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/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.2.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
ksadk/studio/run_service.py
CHANGED
|
@@ -3,15 +3,44 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import logging
|
|
6
7
|
import time
|
|
7
8
|
from collections.abc import Callable, Mapping
|
|
8
|
-
from dataclasses import dataclass, field
|
|
9
|
+
from dataclasses import asdict, dataclass, field
|
|
9
10
|
from datetime import datetime, timezone
|
|
10
11
|
from typing import Any
|
|
11
12
|
from uuid import uuid4
|
|
12
13
|
|
|
13
14
|
from ksadk.agui.a2ui_projection import project_a2ui_operations
|
|
14
|
-
from ksadk.events.
|
|
15
|
+
from ksadk.events.canonical import (
|
|
16
|
+
ContinuationCreated,
|
|
17
|
+
ContinuationResumed,
|
|
18
|
+
ErrorInfo,
|
|
19
|
+
InteractionRequested,
|
|
20
|
+
InteractionResolved,
|
|
21
|
+
ItemCompleted,
|
|
22
|
+
ItemFailed,
|
|
23
|
+
ItemStarted,
|
|
24
|
+
ItemUpdated,
|
|
25
|
+
RunCanceled,
|
|
26
|
+
RunCompleted,
|
|
27
|
+
RunFailed,
|
|
28
|
+
RunInterrupted,
|
|
29
|
+
RunProgress,
|
|
30
|
+
RunStarted,
|
|
31
|
+
RuntimeEvent,
|
|
32
|
+
SourceRef,
|
|
33
|
+
UsageReported,
|
|
34
|
+
dump_runtime_event,
|
|
35
|
+
)
|
|
36
|
+
from ksadk.events.content import (
|
|
37
|
+
ContentSnapshot,
|
|
38
|
+
DataContent,
|
|
39
|
+
TextContent,
|
|
40
|
+
ToolCallContent,
|
|
41
|
+
ToolResultContent,
|
|
42
|
+
)
|
|
43
|
+
from ksadk.events.store import RuntimeEventStore
|
|
15
44
|
from ksadk.runtime import (
|
|
16
45
|
CONVERSATION_PREPROCESSING_METADATA_KEY,
|
|
17
46
|
PauseResult,
|
|
@@ -21,6 +50,8 @@ from ksadk.runtime import (
|
|
|
21
50
|
RuntimeLaunchContext,
|
|
22
51
|
StartRequest,
|
|
23
52
|
)
|
|
53
|
+
from ksadk.sessions.base import BaseSessionService
|
|
54
|
+
from ksadk.sessions.local_service import LocalSessionService
|
|
24
55
|
from ksadk.studio.contracts import RunEvent, RunRecord, RunStatus, Usage
|
|
25
56
|
from ksadk.studio.errors import StudioError
|
|
26
57
|
from ksadk.studio.event_store import RunEventStore
|
|
@@ -29,6 +60,9 @@ from ksadk.studio.workspace import Workspace
|
|
|
29
60
|
_CANCEL_TIMEOUT_SECONDS = 2.0
|
|
30
61
|
|
|
31
62
|
|
|
63
|
+
logger = logging.getLogger(__name__)
|
|
64
|
+
|
|
65
|
+
|
|
32
66
|
@dataclass(frozen=True)
|
|
33
67
|
class StudioRunSpec:
|
|
34
68
|
"""Product metadata plus the core launch context for one Studio run."""
|
|
@@ -50,15 +84,49 @@ class StudioRunService:
|
|
|
50
84
|
executor: RuntimeExecutor,
|
|
51
85
|
*,
|
|
52
86
|
event_store: RunEventStore | None = None,
|
|
87
|
+
session_service: BaseSessionService | None = None,
|
|
88
|
+
runtime_events: RuntimeEventStore | None = None,
|
|
53
89
|
) -> None:
|
|
54
90
|
self.workspace = workspace
|
|
55
91
|
self.executor = executor
|
|
56
92
|
self.event_store = event_store or RunEventStore(workspace)
|
|
93
|
+
self.session_service = session_service or LocalSessionService(
|
|
94
|
+
project_dir=str(workspace.root)
|
|
95
|
+
)
|
|
96
|
+
self.runtime_events = runtime_events or RuntimeEventStore(self.session_service)
|
|
57
97
|
self._active_handles: dict[str, Any] = {}
|
|
58
98
|
self._cancel_flags: dict[str, bool] = {}
|
|
59
99
|
self._control_queues: dict[str, asyncio.Queue[tuple[str, ResumePayload | None]]] = {}
|
|
60
100
|
self._waiting_modes: dict[str, str] = {}
|
|
61
101
|
|
|
102
|
+
async def recover_interrupted(self) -> None:
|
|
103
|
+
"""Settle local runs left active across a Studio restart.
|
|
104
|
+
|
|
105
|
+
An in-process handle cannot be resumed safely. Keep its durable event
|
|
106
|
+
history and record an explicit interruption; lease-backed hosted
|
|
107
|
+
recovery remains owned by the Kernel composition root.
|
|
108
|
+
"""
|
|
109
|
+
active = {RunStatus.RUNNING, RunStatus.WAITING_INPUT}
|
|
110
|
+
for record in self.event_store.list_runs():
|
|
111
|
+
if record.status not in active:
|
|
112
|
+
continue
|
|
113
|
+
record.status = RunStatus.INTERRUPTED
|
|
114
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
115
|
+
record.error = {
|
|
116
|
+
"code": "LOCAL_STUDIO_RESTARTED",
|
|
117
|
+
"message": "本地 Studio 重启,运行未在本地恢复。",
|
|
118
|
+
}
|
|
119
|
+
self.event_store.save(record)
|
|
120
|
+
self.event_store.append(
|
|
121
|
+
record.id,
|
|
122
|
+
"run.interrupted",
|
|
123
|
+
{"reason": "local_studio_restarted", "recoverable": False},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
async def events(self, run_id: str, *, after: int = 0) -> list[RunEvent]:
|
|
127
|
+
"""Read the durable Studio event timeline for API/SSE replay."""
|
|
128
|
+
return self.event_store.events(run_id, after=after)
|
|
129
|
+
|
|
62
130
|
async def run(
|
|
63
131
|
self,
|
|
64
132
|
spec: StudioRunSpec,
|
|
@@ -84,7 +152,9 @@ class StudioRunService:
|
|
|
84
152
|
goal_objective=str(spec.request_config.get("goal_objective") or ""),
|
|
85
153
|
input=user_input,
|
|
86
154
|
)
|
|
155
|
+
await self.session_service.create_session(spec.agent_id, "local-user", session)
|
|
87
156
|
self.event_store.create(record)
|
|
157
|
+
prepared_turn = await self._capture_pcm_evidence(record, spec, user_input)
|
|
88
158
|
created = self.event_store.append(
|
|
89
159
|
record.id,
|
|
90
160
|
"run.created",
|
|
@@ -103,13 +173,21 @@ class StudioRunService:
|
|
|
103
173
|
if on_event is not None:
|
|
104
174
|
on_event(created)
|
|
105
175
|
|
|
176
|
+
from ksadk.kernel.ingress import kernel_route_active
|
|
177
|
+
|
|
178
|
+
if kernel_route_active():
|
|
179
|
+
return await self._kernel_run(
|
|
180
|
+
spec, user_input, record=record, on_event=on_event
|
|
181
|
+
)
|
|
182
|
+
|
|
106
183
|
started = time.monotonic()
|
|
107
184
|
record.status = RunStatus.RUNNING
|
|
108
185
|
record.started_at = datetime.now(timezone.utc)
|
|
109
186
|
self.event_store.save(record)
|
|
110
187
|
|
|
111
|
-
def persist(runtime_event: RuntimeEvent) -> RunEvent:
|
|
112
|
-
|
|
188
|
+
async def persist(runtime_event: RuntimeEvent) -> RunEvent:
|
|
189
|
+
persisted = await self.runtime_events.append_one(record.session_id, runtime_event)
|
|
190
|
+
event_type, data = project_runtime_event(persisted)
|
|
113
191
|
stored = self.event_store.append(record.id, event_type, data)
|
|
114
192
|
if on_event is not None:
|
|
115
193
|
on_event(stored)
|
|
@@ -119,6 +197,7 @@ class StudioRunService:
|
|
|
119
197
|
final_text = ""
|
|
120
198
|
streamed_final = ""
|
|
121
199
|
runtime_duration_ms: int | None = None
|
|
200
|
+
item_phases: dict[tuple[str, str], str | None] = {}
|
|
122
201
|
control_queue: asyncio.Queue[tuple[str, ResumePayload | None]] = asyncio.Queue()
|
|
123
202
|
self._control_queues[run_id] = control_queue
|
|
124
203
|
try:
|
|
@@ -134,6 +213,8 @@ class StudioRunService:
|
|
|
134
213
|
conversation_request["request_metadata"] = {
|
|
135
214
|
"tool_approval_mode": tool_approval_mode,
|
|
136
215
|
}
|
|
216
|
+
if prepared_turn is not None:
|
|
217
|
+
conversation_request["prepared_turn"] = asdict(prepared_turn)
|
|
137
218
|
request = StartRequest(
|
|
138
219
|
input=runtime_input if runtime_input is not None else user_input,
|
|
139
220
|
user_id="local-user",
|
|
@@ -155,63 +236,72 @@ class StudioRunService:
|
|
|
155
236
|
terminal_seen = False
|
|
156
237
|
should_resume = False
|
|
157
238
|
async for event in self.executor.stream(handle):
|
|
158
|
-
persist(event)
|
|
239
|
+
await persist(event)
|
|
159
240
|
if self._cancel_flags.get(run_id):
|
|
160
241
|
raise asyncio.CancelledError()
|
|
161
|
-
if event
|
|
162
|
-
|
|
163
|
-
|
|
242
|
+
if isinstance(event, ItemStarted) and event.item_kind == "message":
|
|
243
|
+
item_phases[(event.scope_id, event.item_id)] = event.phase
|
|
244
|
+
elif (
|
|
245
|
+
isinstance(event, ItemUpdated)
|
|
246
|
+
and event.item_kind == "message"
|
|
247
|
+
and item_phases.get(
|
|
248
|
+
(event.scope_id, event.item_id), "final_answer"
|
|
249
|
+
)
|
|
250
|
+
== "final_answer"
|
|
251
|
+
):
|
|
252
|
+
text = event.update.text if isinstance(event.update, TextContent) else ""
|
|
253
|
+
if event.op == "replace":
|
|
164
254
|
streamed_final = text
|
|
165
255
|
else:
|
|
166
256
|
streamed_final += text
|
|
167
257
|
elif (
|
|
168
|
-
event
|
|
169
|
-
and event.
|
|
258
|
+
isinstance(event, ItemCompleted)
|
|
259
|
+
and event.item_kind == "message"
|
|
260
|
+
and item_phases.get(
|
|
261
|
+
(event.scope_id, event.item_id), "final_answer"
|
|
262
|
+
)
|
|
263
|
+
== "final_answer"
|
|
170
264
|
):
|
|
171
|
-
|
|
172
|
-
|
|
265
|
+
for part in event.snapshot.parts:
|
|
266
|
+
if isinstance(part, TextContent):
|
|
267
|
+
final_text = part.text
|
|
268
|
+
break
|
|
269
|
+
elif isinstance(event, UsageReported):
|
|
173
270
|
record.usage = Usage(
|
|
174
|
-
input_tokens=
|
|
175
|
-
output_tokens=
|
|
176
|
-
total_tokens=
|
|
177
|
-
cached_input_tokens=
|
|
178
|
-
reasoning_output_tokens=
|
|
271
|
+
input_tokens=event.input_tokens,
|
|
272
|
+
output_tokens=event.output_tokens,
|
|
273
|
+
total_tokens=event.total_tokens,
|
|
274
|
+
cached_input_tokens=event.cached_tokens,
|
|
275
|
+
reasoning_output_tokens=event.reasoning_tokens,
|
|
179
276
|
reported=True,
|
|
180
|
-
source=str(event.
|
|
277
|
+
source=str(event.source.framework or runtime_type),
|
|
181
278
|
)
|
|
182
|
-
elif event
|
|
183
|
-
EventType.APPROVAL_REQUESTED,
|
|
184
|
-
EventType.A2UI_INTERACTION,
|
|
185
|
-
}:
|
|
279
|
+
elif isinstance(event, InteractionRequested):
|
|
186
280
|
record.status = RunStatus.WAITING_INPUT
|
|
187
281
|
self._waiting_modes[run_id] = "live"
|
|
188
282
|
self.event_store.save(record)
|
|
189
|
-
if event.
|
|
283
|
+
if event.interaction_kind == "approval":
|
|
190
284
|
self._persist_approval_surface(record, event, on_event=on_event)
|
|
191
|
-
elif event
|
|
192
|
-
EventType.APPROVAL_RESOLVED,
|
|
193
|
-
EventType.A2UI_ACTION,
|
|
194
|
-
}:
|
|
285
|
+
elif isinstance(event, InteractionResolved):
|
|
195
286
|
record.status = RunStatus.RUNNING
|
|
196
287
|
self._waiting_modes.pop(run_id, None)
|
|
197
288
|
self.event_store.save(record)
|
|
198
|
-
elif event
|
|
289
|
+
elif isinstance(event, RunFailed):
|
|
199
290
|
terminal_seen = True
|
|
200
291
|
record.status = RunStatus.FAILED
|
|
201
292
|
record.error = {
|
|
202
293
|
"code": "RUNTIME_RUN_FAILED",
|
|
203
|
-
"message": str(event.
|
|
294
|
+
"message": str(event.error.message or "Runtime 运行失败"),
|
|
204
295
|
}
|
|
205
|
-
elif event
|
|
296
|
+
elif isinstance(event, RunCanceled):
|
|
206
297
|
terminal_seen = True
|
|
207
298
|
record.status = RunStatus.CANCELLED
|
|
208
299
|
record.error = {
|
|
209
300
|
"code": "RUN_CANCELLED",
|
|
210
|
-
"message": str(event.
|
|
301
|
+
"message": str(event.reason or "运行已取消"),
|
|
211
302
|
}
|
|
212
|
-
elif event
|
|
213
|
-
|
|
214
|
-
if interrupted_status == "paused":
|
|
303
|
+
elif isinstance(event, RunInterrupted):
|
|
304
|
+
if event.reason == "user_pause":
|
|
215
305
|
record.status = RunStatus.PAUSED
|
|
216
306
|
should_resume = True
|
|
217
307
|
elif record.status == RunStatus.WAITING_INPUT:
|
|
@@ -222,13 +312,17 @@ class StudioRunService:
|
|
|
222
312
|
record.status = RunStatus.INTERRUPTED
|
|
223
313
|
record.error = {
|
|
224
314
|
"code": "RUN_INTERRUPTED",
|
|
225
|
-
"message":
|
|
315
|
+
"message": event.reason or "运行已中断",
|
|
226
316
|
}
|
|
227
317
|
self.event_store.save(record)
|
|
228
|
-
elif event
|
|
318
|
+
elif isinstance(event, RunCompleted):
|
|
229
319
|
terminal_seen = True
|
|
230
320
|
record.status = RunStatus.COMPLETED
|
|
231
|
-
raw_duration = event.
|
|
321
|
+
raw_duration = event.source.metadata.get("duration_ms")
|
|
322
|
+
if raw_duration is None:
|
|
323
|
+
metrics = event.source.metadata.get("metrics")
|
|
324
|
+
if isinstance(metrics, dict):
|
|
325
|
+
raw_duration = metrics.get("duration_ms")
|
|
232
326
|
if raw_duration is not None:
|
|
233
327
|
runtime_duration_ms = max(0, int(raw_duration))
|
|
234
328
|
if terminal_seen:
|
|
@@ -282,30 +376,44 @@ class StudioRunService:
|
|
|
282
376
|
cancel_result = "cancel_failed"
|
|
283
377
|
record.status = RunStatus.CANCELLED
|
|
284
378
|
record.error = {"code": "RUN_CANCELLED", "message": "运行已取消"}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
379
|
+
cancelled_run_id = handle.run_id if handle is not None else run_id
|
|
380
|
+
cancelled = RunCanceled(
|
|
381
|
+
schema_version=2,
|
|
382
|
+
event_id=f"evt_cancel_{uuid4().hex}",
|
|
383
|
+
seq=len(self.event_store.events(run_id)) + 1,
|
|
384
|
+
timestamp=time.time(),
|
|
385
|
+
run_id=cancelled_run_id,
|
|
386
|
+
scope_id=cancelled_run_id,
|
|
387
|
+
source=SourceRef(
|
|
388
|
+
framework="ksadk",
|
|
389
|
+
metadata={"cancel_result": cancel_result},
|
|
390
|
+
),
|
|
391
|
+
status="canceled",
|
|
392
|
+
reason="cancelled",
|
|
293
393
|
)
|
|
294
|
-
persist(cancelled)
|
|
394
|
+
await persist(cancelled)
|
|
295
395
|
raise
|
|
296
396
|
except Exception as exc: # noqa: BLE001
|
|
297
397
|
record.status = RunStatus.FAILED
|
|
298
398
|
record.error = {"code": "RUNTIME_RUN_FAILED", "message": str(exc)}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
399
|
+
failed_run_id = handle.run_id if handle is not None else run_id
|
|
400
|
+
failure = RunFailed(
|
|
401
|
+
schema_version=2,
|
|
402
|
+
event_id=f"evt_fail_{uuid4().hex}",
|
|
403
|
+
seq=len(self.event_store.events(run_id)) + 1,
|
|
404
|
+
timestamp=time.time(),
|
|
405
|
+
run_id=failed_run_id,
|
|
406
|
+
scope_id=failed_run_id,
|
|
407
|
+
source=SourceRef(framework="ksadk"),
|
|
408
|
+
status="failed",
|
|
409
|
+
error=ErrorInfo(
|
|
410
|
+
code="RUNTIME_RUN_FAILED",
|
|
411
|
+
message=str(exc),
|
|
412
|
+
source="ksadk",
|
|
413
|
+
scope_id=failed_run_id,
|
|
414
|
+
),
|
|
307
415
|
)
|
|
308
|
-
persist(failure)
|
|
416
|
+
await persist(failure)
|
|
309
417
|
finally:
|
|
310
418
|
if handle is not None and self.executor.is_attached(handle):
|
|
311
419
|
try:
|
|
@@ -326,8 +434,98 @@ class StudioRunService:
|
|
|
326
434
|
record.duration_ms = int((time.monotonic() - started) * 1000)
|
|
327
435
|
record.duration_source = "studio"
|
|
328
436
|
self.event_store.save(record)
|
|
437
|
+
await self._sync_trace(record)
|
|
329
438
|
return record
|
|
330
439
|
|
|
440
|
+
async def _sync_trace(self, record: RunRecord) -> None:
|
|
441
|
+
self.event_store.trace_store.sync(record, await self.events(record.id))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
async def _kernel_run(
|
|
445
|
+
self,
|
|
446
|
+
spec: StudioRunSpec,
|
|
447
|
+
user_input: str,
|
|
448
|
+
*,
|
|
449
|
+
record: RunRecord,
|
|
450
|
+
on_event: Callable[[RunEvent], None] | None,
|
|
451
|
+
) -> RunRecord:
|
|
452
|
+
"""kernel 路径(灰度 opt-in):Studio run -> AgentControlCommand -> receipt。
|
|
453
|
+
|
|
454
|
+
mutation 只走 kernel.submit;Studio RunEvent shape 保留,cursor 源自
|
|
455
|
+
同一 Session seq(SessionEventSubscription.after_seq)。
|
|
456
|
+
"""
|
|
457
|
+
from ksadk.kernel import ingress as _kernel_ingress
|
|
458
|
+
|
|
459
|
+
started = time.monotonic()
|
|
460
|
+
record.status = RunStatus.RUNNING
|
|
461
|
+
record.started_at = datetime.now(timezone.utc)
|
|
462
|
+
self.event_store.save(record)
|
|
463
|
+
try:
|
|
464
|
+
trusted = _kernel_ingress.trusted_context(
|
|
465
|
+
source_kind="studio",
|
|
466
|
+
source_ref=record.id,
|
|
467
|
+
session_id=record.session_id,
|
|
468
|
+
operations=("enqueue",),
|
|
469
|
+
launch_context=spec.launch_context,
|
|
470
|
+
)
|
|
471
|
+
idempotency_key = str(
|
|
472
|
+
(spec.request_config or {}).get("idempotency_key") or record.id
|
|
473
|
+
)
|
|
474
|
+
command = _kernel_ingress.map_studio_request(
|
|
475
|
+
session_id=record.session_id,
|
|
476
|
+
idempotency_key=idempotency_key,
|
|
477
|
+
content=user_input,
|
|
478
|
+
run_id=record.id,
|
|
479
|
+
trusted=trusted,
|
|
480
|
+
)
|
|
481
|
+
receipt = await _kernel_ingress.submit_command(
|
|
482
|
+
command, permit=trusted.permit
|
|
483
|
+
)
|
|
484
|
+
if receipt.status not in ("accepted", "duplicate"):
|
|
485
|
+
record.status = RunStatus.FAILED
|
|
486
|
+
record.error = {
|
|
487
|
+
"code": "kernel_command_rejected",
|
|
488
|
+
"message": f"agent kernel rejected command: {receipt.status}",
|
|
489
|
+
}
|
|
490
|
+
self.event_store.save(record)
|
|
491
|
+
return record
|
|
492
|
+
output_text = ""
|
|
493
|
+
async for _seq, projected in _kernel_ingress.subscribe_projected(
|
|
494
|
+
record.session_id,
|
|
495
|
+
trusted=trusted,
|
|
496
|
+
after_seq=int(receipt.accepted_seq or 0),
|
|
497
|
+
projector=_studio_envelope_projection,
|
|
498
|
+
):
|
|
499
|
+
if projected is None:
|
|
500
|
+
continue
|
|
501
|
+
event_type, data = projected
|
|
502
|
+
if event_type == "message.delta":
|
|
503
|
+
output_text += str(data.get("delta") or "")
|
|
504
|
+
elif event_type == "run.completed":
|
|
505
|
+
output_text = str(data.get("output_text") or output_text)
|
|
506
|
+
stored = self.event_store.append(record.id, event_type, data)
|
|
507
|
+
if on_event is not None:
|
|
508
|
+
on_event(stored)
|
|
509
|
+
record.output = output_text
|
|
510
|
+
record.status = RunStatus.COMPLETED
|
|
511
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
512
|
+
record.duration_ms = int((time.monotonic() - started) * 1000)
|
|
513
|
+
self.event_store.save(record)
|
|
514
|
+
await self._sync_trace(record)
|
|
515
|
+
return record
|
|
516
|
+
except Exception as exc: # noqa: BLE001
|
|
517
|
+
logger.exception("Studio kernel ingress failed for run %s", record.id)
|
|
518
|
+
record.status = RunStatus.FAILED
|
|
519
|
+
record.error = {
|
|
520
|
+
"code": "kernel_ingress_failed",
|
|
521
|
+
"message": str(exc),
|
|
522
|
+
}
|
|
523
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
524
|
+
record.duration_ms = int((time.monotonic() - started) * 1000)
|
|
525
|
+
self.event_store.save(record)
|
|
526
|
+
await self._sync_trace(record)
|
|
527
|
+
return record
|
|
528
|
+
|
|
331
529
|
async def cancel_run(self, run_id: str) -> dict[str, str]:
|
|
332
530
|
"""Request cancellation; the flag and executor perform the actual stop."""
|
|
333
531
|
self._cancel_flags[run_id] = True
|
|
@@ -488,21 +686,103 @@ class StudioRunService:
|
|
|
488
686
|
"resolutionEventId": resolved.id,
|
|
489
687
|
}
|
|
490
688
|
|
|
689
|
+
async def _capture_pcm_evidence(
|
|
690
|
+
self,
|
|
691
|
+
record: RunRecord,
|
|
692
|
+
spec: StudioRunSpec,
|
|
693
|
+
user_input: str,
|
|
694
|
+
) -> Any | None:
|
|
695
|
+
"""Prepare once and persist the exact prompt/context input sent to Runtime."""
|
|
696
|
+
|
|
697
|
+
try:
|
|
698
|
+
from ksadk.conversations.runtime_preparation import build_run_input
|
|
699
|
+
from ksadk.sessions.in_memory import InMemorySessionService
|
|
700
|
+
|
|
701
|
+
temporary_sessions = InMemorySessionService()
|
|
702
|
+
await temporary_sessions.create_session(
|
|
703
|
+
agent_id=spec.agent_id,
|
|
704
|
+
user_id="local-user",
|
|
705
|
+
session_id=record.session_id,
|
|
706
|
+
)
|
|
707
|
+
config = dict(spec.request_config or {})
|
|
708
|
+
prepared = await build_run_input(
|
|
709
|
+
agent_id=spec.agent_id,
|
|
710
|
+
user_id="local-user",
|
|
711
|
+
session_id=record.session_id,
|
|
712
|
+
messages=self._conversation_messages(
|
|
713
|
+
spec.agent_id,
|
|
714
|
+
record.session_id,
|
|
715
|
+
user_input,
|
|
716
|
+
),
|
|
717
|
+
model=spec.model,
|
|
718
|
+
instructions=str(config.get("instructions") or ""),
|
|
719
|
+
agent_system=str(config.get("agent_system") or ""),
|
|
720
|
+
agent_task=str(config.get("agent_task") or ""),
|
|
721
|
+
prompt_integration_mode=str(config.get("prompt_integration_mode") or ""),
|
|
722
|
+
context_engine_rollout=str(config.get("context_engine_rollout") or "") or None,
|
|
723
|
+
memory_recall_enabled=config.get("memory_recall_enabled"),
|
|
724
|
+
memory_write_rollout=str(config.get("memory_write_rollout") or "") or None,
|
|
725
|
+
memory_enabled=config.get("memory_enabled"),
|
|
726
|
+
memory_write_mode=str(config.get("memory_write_mode") or "candidate"),
|
|
727
|
+
flush_before_compaction=bool(config.get("flush_before_compaction", True)),
|
|
728
|
+
provider_ref=str(config.get("provider_ref") or "local-default"),
|
|
729
|
+
runtime_type=spec.launch_context.runtime_type,
|
|
730
|
+
deployment_mode=spec.launch_context.deployment_mode,
|
|
731
|
+
invocation_id=record.id,
|
|
732
|
+
session_service_provider=lambda: temporary_sessions,
|
|
733
|
+
agent_max_input_tokens=config.get("max_input_tokens"),
|
|
734
|
+
agent_reserve_output_tokens=config.get("reserve_output_tokens"),
|
|
735
|
+
)
|
|
736
|
+
record.context_plan = prepared.context_plan
|
|
737
|
+
compiled = prepared.compiled_prompt
|
|
738
|
+
shadow = prepared.shadow_context_plan or {}
|
|
739
|
+
record.prompt_evidence = {
|
|
740
|
+
"contentHash": (compiled or {}).get("prompt_content_hash"),
|
|
741
|
+
"stablePrefixHash": (compiled or {}).get("prompt_stable_prefix_hash"),
|
|
742
|
+
"sectionHashes": (compiled or {}).get("prompt_section_hashes", {}),
|
|
743
|
+
"tokensBySection": (compiled or {}).get("prompt_tokens_by_section", {}),
|
|
744
|
+
"estimatedTokens": (compiled or {}).get("prompt_estimated_tokens"),
|
|
745
|
+
"sectionCount": (compiled or {}).get("prompt_section_count"),
|
|
746
|
+
"integrationMode": shadow.get("integration_mode")
|
|
747
|
+
or config.get("prompt_integration_mode")
|
|
748
|
+
or "native",
|
|
749
|
+
"accountingAccuracy": shadow.get("accounting_accuracy") or "opaque",
|
|
750
|
+
"promptOwner": shadow.get("prompt_owner") or "runtime",
|
|
751
|
+
"runtimeType": shadow.get("runtime_type") or spec.launch_context.runtime_type,
|
|
752
|
+
"deploymentMode": shadow.get("deployment_mode")
|
|
753
|
+
or spec.launch_context.deployment_mode,
|
|
754
|
+
"capabilityHash": shadow.get("capability_hash"),
|
|
755
|
+
"tokensByKind": shadow.get("tokens_by_kind", {}),
|
|
756
|
+
"plannedInputTokens": shadow.get("planned_input_tokens"),
|
|
757
|
+
}
|
|
758
|
+
record.working_state = prepared.working_state
|
|
759
|
+
for event in getattr(prepared, "memory_recall_events", []):
|
|
760
|
+
self.event_store.append(
|
|
761
|
+
record.id,
|
|
762
|
+
event.get("type", "memory.recall.event"),
|
|
763
|
+
event,
|
|
764
|
+
)
|
|
765
|
+
self.event_store.save(record)
|
|
766
|
+
return prepared
|
|
767
|
+
except Exception: # noqa: BLE001 - evidence collection is best effort
|
|
768
|
+
logger.exception("Studio PCM evidence capture failed for run %s", record.id)
|
|
769
|
+
return None
|
|
770
|
+
|
|
491
771
|
def _persist_approval_surface(
|
|
492
772
|
self,
|
|
493
773
|
record: RunRecord,
|
|
494
|
-
event:
|
|
774
|
+
event: InteractionRequested,
|
|
495
775
|
*,
|
|
496
776
|
on_event: Callable[[RunEvent], None] | None,
|
|
497
777
|
) -> None:
|
|
498
|
-
approval_id =
|
|
778
|
+
approval_id = event.interaction_id
|
|
499
779
|
if not approval_id:
|
|
500
780
|
return
|
|
501
781
|
surface_id = f"approval-{approval_id}"
|
|
502
|
-
|
|
503
|
-
detail =
|
|
782
|
+
detail_value = event.request.detail
|
|
783
|
+
detail = detail_value if isinstance(detail_value, dict) else {}
|
|
504
784
|
command = str(detail.get("command") or detail.get("reason") or "")
|
|
505
|
-
kind = str(event.
|
|
785
|
+
kind = str(event.request.kind or "tool")
|
|
506
786
|
components = [
|
|
507
787
|
{
|
|
508
788
|
"id": "root",
|
|
@@ -602,106 +882,252 @@ class StudioRunService:
|
|
|
602
882
|
|
|
603
883
|
|
|
604
884
|
def project_runtime_event(event: RuntimeEvent) -> tuple[str, dict[str, Any]]:
|
|
605
|
-
"""Project the canonical RuntimeEvent into Studio's persisted event view.
|
|
885
|
+
"""Project the canonical RuntimeEvent into Studio's persisted event view.
|
|
606
886
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
887
|
+
公开承诺字段(契约声明见 ``ksadk/events/projections.py``,执行形态为
|
|
888
|
+
``tests/protocol/test_cross_projection_golden.py``):
|
|
889
|
+
- 所有事件 payload 必含 ``runId``/``scopeId``;
|
|
890
|
+
- ``message.*``/``thinking.*`` 含 ``itemId``(delta 另含 ``partId``);
|
|
891
|
+
- ``tool.*``/``command.*``/``approval.*``/``a2ui.*`` 含 ``itemId``;
|
|
892
|
+
- ``a2ui.surface.*`` 含 ``surfaceId`` 与 ``a2uiOperations`` 列表。
|
|
893
|
+
|
|
894
|
+
内部不保证字段:除上述外的 payload 附加键、事件类型枚举的完备性
|
|
895
|
+
(新增 canonical 事件类型在未适配前可能整条丢弃)。
|
|
896
|
+
"""
|
|
897
|
+
|
|
898
|
+
if isinstance(event, RunStarted):
|
|
610
899
|
projected = "run.started"
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
elif
|
|
625
|
-
projected = "
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
900
|
+
payload: dict[str, Any] = {}
|
|
901
|
+
elif isinstance(event, RunProgress):
|
|
902
|
+
projected = "run.progress"
|
|
903
|
+
payload = {"progress": event.progress, "message": event.message}
|
|
904
|
+
elif isinstance(event, RunCompleted):
|
|
905
|
+
projected = "run.completed"
|
|
906
|
+
payload = {}
|
|
907
|
+
elif isinstance(event, RunFailed):
|
|
908
|
+
projected = "run.failed"
|
|
909
|
+
payload = {"error": event.error.message or ""}
|
|
910
|
+
elif isinstance(event, RunCanceled):
|
|
911
|
+
projected = "run.cancelled"
|
|
912
|
+
payload = {"reason": event.reason or ""}
|
|
913
|
+
elif isinstance(event, RunInterrupted):
|
|
914
|
+
projected = "run.paused" if event.reason == "user_pause" else "run.interrupted"
|
|
915
|
+
payload = {"reason": event.reason or ""}
|
|
916
|
+
elif isinstance(event, ItemUpdated) and event.item_kind in {"message", "reasoning"}:
|
|
917
|
+
text = event.update.text if isinstance(event.update, TextContent) else ""
|
|
918
|
+
is_thinking = event.item_kind == "reasoning"
|
|
919
|
+
projected = "thinking.delta" if is_thinking else "message.delta"
|
|
920
|
+
payload = {"text": text}
|
|
921
|
+
elif isinstance(event, ItemCompleted) and event.item_kind in {"message", "reasoning"}:
|
|
922
|
+
text = ""
|
|
923
|
+
for part in event.snapshot.parts:
|
|
924
|
+
if isinstance(part, TextContent):
|
|
925
|
+
text = part.text
|
|
926
|
+
break
|
|
927
|
+
is_thinking = event.item_kind == "reasoning"
|
|
928
|
+
projected = "thinking.completed" if is_thinking else "message.completed"
|
|
929
|
+
payload = {"text": text}
|
|
930
|
+
elif isinstance(event, ItemStarted) and event.item_kind == "tool_call":
|
|
931
|
+
tool_part = _first_content(event.initial, ToolCallContent) if event.initial else None
|
|
932
|
+
if tool_part is not None and tool_part.name == "codex.command":
|
|
933
|
+
projected = "command.started"
|
|
934
|
+
args = tool_part.arguments if isinstance(tool_part.arguments, dict) else {}
|
|
629
935
|
payload = {
|
|
630
|
-
"callId":
|
|
936
|
+
"callId": tool_part.call_id,
|
|
631
937
|
"command": str(args.get("command") or ""),
|
|
632
938
|
"cwd": str(args.get("cwd") or ""),
|
|
633
939
|
"commandActions": args.get("command_actions") or [],
|
|
634
940
|
}
|
|
635
|
-
|
|
941
|
+
elif tool_part is not None:
|
|
942
|
+
projected = "tool.started"
|
|
636
943
|
payload = {
|
|
637
|
-
"callId":
|
|
638
|
-
"tool":
|
|
639
|
-
"args":
|
|
944
|
+
"callId": tool_part.call_id,
|
|
945
|
+
"tool": tool_part.name,
|
|
946
|
+
"args": tool_part.arguments,
|
|
640
947
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
948
|
+
else:
|
|
949
|
+
projected = "tool.started"
|
|
950
|
+
payload = {}
|
|
951
|
+
elif isinstance(event, ItemCompleted) and event.item_kind == "tool_call":
|
|
952
|
+
tool_call = _first_content(event.snapshot, ToolCallContent)
|
|
953
|
+
tool_result = _first_content(event.snapshot, ToolResultContent)
|
|
954
|
+
if tool_call is not None and tool_call.name == "codex.command":
|
|
955
|
+
projected = "command.completed"
|
|
956
|
+
result = (
|
|
957
|
+
tool_result.result
|
|
958
|
+
if tool_result is not None and isinstance(tool_result.result, dict)
|
|
959
|
+
else {}
|
|
960
|
+
)
|
|
648
961
|
payload = {
|
|
649
|
-
"callId":
|
|
962
|
+
"callId": tool_call.call_id,
|
|
650
963
|
"status": str(result.get("status") or ""),
|
|
651
964
|
"exitCode": result.get("exit_code"),
|
|
652
965
|
"durationMs": result.get("duration_ms"),
|
|
653
966
|
"output": str(result.get("output") or ""),
|
|
654
967
|
}
|
|
655
968
|
else:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
969
|
+
projected = "tool.completed"
|
|
970
|
+
result = (
|
|
971
|
+
tool_result.result
|
|
972
|
+
if tool_result is not None and isinstance(tool_result.result, dict)
|
|
973
|
+
else {}
|
|
659
974
|
)
|
|
660
|
-
tool_status = str(
|
|
661
|
-
if tool_result.
|
|
975
|
+
tool_status = str(result.get("status") or "")
|
|
976
|
+
if tool_result is not None and tool_result.is_error:
|
|
662
977
|
tool_status = "failed"
|
|
978
|
+
call_id = (tool_call or tool_result).call_id if (tool_call or tool_result) else ""
|
|
663
979
|
payload = {
|
|
664
|
-
"callId":
|
|
665
|
-
"tool":
|
|
980
|
+
"callId": call_id,
|
|
981
|
+
"tool": tool_call.name if tool_call is not None else "",
|
|
666
982
|
"status": tool_status,
|
|
667
|
-
"durationMs":
|
|
668
|
-
"output": str(
|
|
669
|
-
**({"error": str(tool_result["error"])} if tool_result.get("error") else {}),
|
|
983
|
+
"durationMs": result.get("duration_ms"),
|
|
984
|
+
"output": str(result.get("output") or ""),
|
|
670
985
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
elif
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
projected = (
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
if
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
986
|
+
if tool_result is not None and tool_result.is_error:
|
|
987
|
+
payload["error"] = str(result.get("error") or "")
|
|
988
|
+
elif (
|
|
989
|
+
isinstance(event, (ItemStarted, ItemUpdated, ItemCompleted))
|
|
990
|
+
and event.item_kind == "data"
|
|
991
|
+
and event.source.protocol == "a2ui"
|
|
992
|
+
):
|
|
993
|
+
projected, payload = _project_a2ui_surface(event)
|
|
994
|
+
elif isinstance(event, InteractionRequested):
|
|
995
|
+
if event.interaction_kind == "approval":
|
|
996
|
+
projected = "approval.requested"
|
|
997
|
+
payload = {
|
|
998
|
+
"approvalId": event.interaction_id,
|
|
999
|
+
"callId": event.request.call_id or "",
|
|
1000
|
+
"kind": event.request.kind,
|
|
1001
|
+
"detail": event.request.detail,
|
|
1002
|
+
}
|
|
1003
|
+
else:
|
|
1004
|
+
projected = "a2ui.interaction"
|
|
1005
|
+
payload = {
|
|
1006
|
+
"interactionId": event.interaction_id,
|
|
1007
|
+
"kind": "form",
|
|
1008
|
+
"inputSchema": {},
|
|
1009
|
+
}
|
|
1010
|
+
elif isinstance(event, InteractionResolved):
|
|
1011
|
+
if event.interaction_kind == "approval":
|
|
1012
|
+
projected = "approval.resolved"
|
|
1013
|
+
payload = {
|
|
1014
|
+
"approvalId": event.interaction_id,
|
|
1015
|
+
"callId": "",
|
|
1016
|
+
"decision": "",
|
|
1017
|
+
}
|
|
1018
|
+
else:
|
|
1019
|
+
projected = "a2ui.action"
|
|
1020
|
+
payload = {"interactionId": event.interaction_id}
|
|
1021
|
+
elif isinstance(event, ContinuationCreated):
|
|
1022
|
+
projected = "checkpoint.created"
|
|
1023
|
+
payload = {
|
|
1024
|
+
"checkpointId": event.continuation_id,
|
|
1025
|
+
"granularity": event.ref.get("granularity", "snapshot"),
|
|
1026
|
+
"resumable": event.resumable,
|
|
1027
|
+
}
|
|
1028
|
+
elif isinstance(event, ContinuationResumed):
|
|
1029
|
+
projected = "checkpoint.resumed"
|
|
1030
|
+
payload = {
|
|
1031
|
+
"checkpointId": event.continuation_id,
|
|
1032
|
+
"resumeAttemptId": event.resume_attempt_id,
|
|
1033
|
+
}
|
|
1034
|
+
elif isinstance(event, UsageReported):
|
|
1035
|
+
projected = "usage.reported"
|
|
1036
|
+
payload = {
|
|
1037
|
+
"inputTokens": event.input_tokens,
|
|
1038
|
+
"outputTokens": event.output_tokens,
|
|
1039
|
+
"totalTokens": event.total_tokens,
|
|
1040
|
+
"cachedTokens": event.cached_tokens,
|
|
1041
|
+
"reasoningTokens": event.reasoning_tokens,
|
|
1042
|
+
}
|
|
701
1043
|
else:
|
|
702
|
-
projected =
|
|
703
|
-
|
|
1044
|
+
projected = event.event_type
|
|
1045
|
+
payload = {}
|
|
1046
|
+
|
|
1047
|
+
_attach_studio_identity(payload, event)
|
|
1048
|
+
payload["runtimeEvent"] = dump_runtime_event(event)
|
|
704
1049
|
return projected, payload
|
|
705
1050
|
|
|
706
1051
|
|
|
1052
|
+
def _first_content(
|
|
1053
|
+
snapshot: ContentSnapshot | None, content_type: type
|
|
1054
|
+
) -> Any | None:
|
|
1055
|
+
if snapshot is None:
|
|
1056
|
+
return None
|
|
1057
|
+
for part in snapshot.parts:
|
|
1058
|
+
if isinstance(part, content_type):
|
|
1059
|
+
return part
|
|
1060
|
+
return None
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
def _project_a2ui_surface(
|
|
1064
|
+
event: ItemStarted | ItemUpdated | ItemCompleted,
|
|
1065
|
+
) -> tuple[str, dict[str, Any]]:
|
|
1066
|
+
if isinstance(event, ItemStarted):
|
|
1067
|
+
projected = "a2ui.surface.begin"
|
|
1068
|
+
data_parts = event.initial.parts if event.initial is not None else ()
|
|
1069
|
+
elif isinstance(event, ItemUpdated):
|
|
1070
|
+
projected = "a2ui.surface.update"
|
|
1071
|
+
data_parts = (event.update,) if isinstance(event.update, DataContent) else ()
|
|
1072
|
+
else:
|
|
1073
|
+
projected = "a2ui.surface.end"
|
|
1074
|
+
data_parts = event.snapshot.parts
|
|
1075
|
+
|
|
1076
|
+
surface_id = str(event.source.metadata.get("surface_id") or "")
|
|
1077
|
+
operations: list[dict[str, Any]] = []
|
|
1078
|
+
for part in data_parts:
|
|
1079
|
+
if isinstance(part, DataContent):
|
|
1080
|
+
data = part.data
|
|
1081
|
+
if isinstance(data, list):
|
|
1082
|
+
operations.extend(dict(op) for op in data if isinstance(op, Mapping))
|
|
1083
|
+
if not operations:
|
|
1084
|
+
operations = project_a2ui_operations(projected, {"surface_id": surface_id})
|
|
1085
|
+
payload: dict[str, Any] = {
|
|
1086
|
+
"surfaceId": surface_id,
|
|
1087
|
+
"a2uiOperations": operations,
|
|
1088
|
+
}
|
|
1089
|
+
return projected, payload
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
def _attach_studio_identity(payload: dict[str, Any], event: RuntimeEvent) -> None:
|
|
1093
|
+
"""Attach §8.4 identity fields (runId/scopeId/itemId/partId/operation)."""
|
|
1094
|
+
payload["runId"] = event.run_id
|
|
1095
|
+
payload["scopeId"] = event.scope_id
|
|
1096
|
+
if isinstance(event, (ItemStarted, ItemUpdated, ItemCompleted, ItemFailed)):
|
|
1097
|
+
payload["itemId"] = event.item_id
|
|
1098
|
+
if isinstance(event, ItemUpdated):
|
|
1099
|
+
payload["operation"] = event.op
|
|
1100
|
+
if isinstance(event, ItemUpdated) and hasattr(event.update, "part_id"):
|
|
1101
|
+
payload["partId"] = event.update.part_id
|
|
1102
|
+
if isinstance(event, ItemStarted) and event.initial is not None:
|
|
1103
|
+
for part in event.initial.parts:
|
|
1104
|
+
if hasattr(part, "part_id"):
|
|
1105
|
+
payload["partId"] = part.part_id
|
|
1106
|
+
break
|
|
1107
|
+
if isinstance(event, ItemCompleted):
|
|
1108
|
+
for part in event.snapshot.parts:
|
|
1109
|
+
if hasattr(part, "part_id"):
|
|
1110
|
+
payload["partId"] = part.part_id
|
|
1111
|
+
break
|
|
1112
|
+
if isinstance(event, (InteractionRequested, InteractionResolved)):
|
|
1113
|
+
payload["itemId"] = event.interaction_id
|
|
1114
|
+
if isinstance(event, (ContinuationCreated, ContinuationResumed)):
|
|
1115
|
+
payload["itemId"] = event.continuation_id
|
|
1116
|
+
|
|
1117
|
+
|
|
707
1118
|
__all__ = ["StudioRunService", "StudioRunSpec", "project_runtime_event"]
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def _studio_envelope_projection(envelope) -> tuple[str, dict[str, Any]] | None:
|
|
1122
|
+
"""Session envelope -> Studio RunEvent 投影;cursor 仍用 envelope.seq。"""
|
|
1123
|
+
|
|
1124
|
+
payload = envelope.payload or {}
|
|
1125
|
+
if envelope.event_type == "run.completed":
|
|
1126
|
+
return "run.completed", {
|
|
1127
|
+
"runId": envelope.run_id or "",
|
|
1128
|
+
"output_text": str(payload.get("output_text") or ""),
|
|
1129
|
+
}
|
|
1130
|
+
text = str(payload.get("delta") or payload.get("text") or "")
|
|
1131
|
+
if text:
|
|
1132
|
+
return "message.delta", {"delta": text}
|
|
1133
|
+
return None
|