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/api_contracts.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Literal
|
|
6
6
|
|
|
7
|
-
from pydantic import Field, SecretStr
|
|
7
|
+
from pydantic import Field, SecretStr, field_validator
|
|
8
8
|
|
|
9
9
|
from ksadk.evaluation import EvaluationConfig as PublicEvaluationConfig
|
|
10
10
|
from ksadk.evaluation import TargetRef
|
|
@@ -48,6 +48,28 @@ class BuildRequest(ContractModel):
|
|
|
48
48
|
evaluation_suite_refs: list[str] = Field(default_factory=list)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
class ImportRootRequest(ContractModel):
|
|
52
|
+
"""一键导入根 Framework 项目(方案 §6.1)。"""
|
|
53
|
+
name: str | None = None
|
|
54
|
+
slug: str | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class PromptCompileRequest(ContractModel):
|
|
58
|
+
"""PR-S2:Prompt 编译预览请求(方案 §6.2)。只读,不写 Session/Trace。"""
|
|
59
|
+
revision: int = Field(default=1, ge=1)
|
|
60
|
+
request_instructions: str = Field(default="", max_length=32768)
|
|
61
|
+
include_content: bool = False # local debug 显式请求正文
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ContextPreviewRequest(ContractModel):
|
|
65
|
+
"""PR-S2:Context 预览请求(方案 §6.2)。复用真实 Planner,不调模型。"""
|
|
66
|
+
revision: int = Field(default=1, ge=1)
|
|
67
|
+
user_input: str = Field(default="", max_length=1_000_000)
|
|
68
|
+
request_instructions: str = Field(default="", max_length=32768)
|
|
69
|
+
simulated_history: list[MessageInput] = Field(default_factory=list)
|
|
70
|
+
include_content: bool = False
|
|
71
|
+
|
|
72
|
+
|
|
51
73
|
class MessageInput(ContractModel):
|
|
52
74
|
role: str = "user"
|
|
53
75
|
content: str = Field(min_length=1, max_length=1_000_000)
|
|
@@ -69,7 +91,29 @@ class AuthoringConversationMessage(ContractModel):
|
|
|
69
91
|
|
|
70
92
|
class ConversationAuthoringRequest(ContractModel):
|
|
71
93
|
messages: list[AuthoringConversationMessage] = Field(min_length=1, max_length=100)
|
|
94
|
+
# This is the single profile used to generate the Draft Patch. It is not
|
|
95
|
+
# implicitly the deployed Agent's only model: callers may bind a separate
|
|
96
|
+
# allow-list and default below.
|
|
72
97
|
model_profile_id: str = Field(min_length=3, max_length=256)
|
|
98
|
+
# Runtime is a Studio-owned deployment choice. The authoring model only
|
|
99
|
+
# supplies semantic intent and must not choose an incompatible framework
|
|
100
|
+
# source contract on its own.
|
|
101
|
+
runtime_type: Literal["codex", "adk", "langgraph"] = "codex"
|
|
102
|
+
agent_model_profile_ids: list[str] = Field(default_factory=list, max_length=100)
|
|
103
|
+
agent_default_model_profile_id: str | None = Field(
|
|
104
|
+
default=None,
|
|
105
|
+
min_length=3,
|
|
106
|
+
max_length=256,
|
|
107
|
+
)
|
|
108
|
+
# Resource identities are selected by the Studio user, never generated by
|
|
109
|
+
# the authoring model. The API resolves and validates their contracts
|
|
110
|
+
# before adding them to the returned Draft Patch.
|
|
111
|
+
tool_resource_ids: list[str] = Field(default_factory=list, max_length=100)
|
|
112
|
+
mcp_resource_ids: list[str] = Field(default_factory=list, max_length=100)
|
|
113
|
+
skill_resource_ids: list[str] = Field(default_factory=list, max_length=100)
|
|
114
|
+
# 可选的进度关联标识:前端生成后随请求带上,可通过
|
|
115
|
+
# GET /api/v1/authoring/conversations:status/{request_id} 轮询构建阶段。
|
|
116
|
+
request_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
73
117
|
|
|
74
118
|
|
|
75
119
|
class AuthoringCommitRequest(ContractModel):
|
|
@@ -96,10 +140,71 @@ class InteractionSubmitRequest(ContractModel):
|
|
|
96
140
|
data: dict[str, Any] = Field(default_factory=dict)
|
|
97
141
|
|
|
98
142
|
|
|
99
|
-
class
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
143
|
+
class CloudChatMessageRequest(ContractModel):
|
|
144
|
+
"""One cloud turn submitted through Studio's loopback control plane.
|
|
145
|
+
|
|
146
|
+
The loopback process, never the browser, owns the AK/SK used to admit the
|
|
147
|
+
message through the cloud control plane. ``content`` uses the same
|
|
148
|
+
OpenAI-compatible text/image/file part shape as RunAgent; execution-policy
|
|
149
|
+
fields remain bounded enums and are revalidated by Server/Runtime.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
content: str | list[dict[str, Any]]
|
|
153
|
+
model: str | None = Field(default=None, min_length=1, max_length=256)
|
|
154
|
+
model_options: dict[str, Any] = Field(default_factory=dict)
|
|
155
|
+
tool_approval_mode: Literal["ask", "risk", "full"] = "risk"
|
|
156
|
+
collaboration_mode: Literal["default", "plan"] | None = None
|
|
157
|
+
goal_objective: str | None = Field(default=None, min_length=1, max_length=4096)
|
|
158
|
+
|
|
159
|
+
@field_validator("content")
|
|
160
|
+
@classmethod
|
|
161
|
+
def validate_content(cls, value):
|
|
162
|
+
if isinstance(value, str):
|
|
163
|
+
if not value.strip():
|
|
164
|
+
raise ValueError("content must not be empty")
|
|
165
|
+
if len(value) > 1_000_000:
|
|
166
|
+
raise ValueError("text content is too large")
|
|
167
|
+
return value
|
|
168
|
+
if not 1 <= len(value) <= 9:
|
|
169
|
+
raise ValueError("content must contain between 1 and 9 parts")
|
|
170
|
+
attachment_count = 0
|
|
171
|
+
for part in value:
|
|
172
|
+
kind = str(part.get("type") or "")
|
|
173
|
+
if kind == "input_text":
|
|
174
|
+
text = part.get("text")
|
|
175
|
+
if not isinstance(text, str) or not text.strip() or len(text) > 1_000_000:
|
|
176
|
+
raise ValueError("input_text must contain bounded non-empty text")
|
|
177
|
+
continue
|
|
178
|
+
if kind == "input_image":
|
|
179
|
+
attachment_count += 1
|
|
180
|
+
url = part.get("image_url")
|
|
181
|
+
if not isinstance(url, str) or not url or len(url) > 14_000_000:
|
|
182
|
+
raise ValueError("input_image must contain a bounded image_url")
|
|
183
|
+
continue
|
|
184
|
+
if kind == "input_file":
|
|
185
|
+
attachment_count += 1
|
|
186
|
+
filename = part.get("filename")
|
|
187
|
+
data = part.get("file_data") or part.get("file_url")
|
|
188
|
+
if not isinstance(filename, str) or not filename.strip():
|
|
189
|
+
raise ValueError("input_file must contain a filename")
|
|
190
|
+
if not isinstance(data, str) or not data or len(data) > 14_000_000:
|
|
191
|
+
raise ValueError("input_file must contain bounded file data")
|
|
192
|
+
continue
|
|
193
|
+
raise ValueError(f"unsupported content part: {kind or '<missing>'}")
|
|
194
|
+
if attachment_count > 8:
|
|
195
|
+
raise ValueError("a turn supports at most 8 attachments")
|
|
196
|
+
return value
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class CloudChatInteractionSubmitRequest(ContractModel):
|
|
200
|
+
"""Public Interaction/v1 fields accepted by the local cloud-chat proxy."""
|
|
201
|
+
|
|
202
|
+
run_id: str = Field(min_length=1, max_length=256)
|
|
203
|
+
interaction_id: str = Field(min_length=1, max_length=256)
|
|
204
|
+
expected_revision: int = Field(ge=1)
|
|
205
|
+
action: Literal["approve", "reject", "submit", "cancel"]
|
|
206
|
+
response: dict[str, Any] = Field(default_factory=dict)
|
|
207
|
+
idempotency_key: str = Field(min_length=1, max_length=256)
|
|
103
208
|
|
|
104
209
|
|
|
105
210
|
class StudioEvaluationCreate(ContractModel):
|
|
@@ -123,6 +228,10 @@ class RollbackRequest(ContractModel):
|
|
|
123
228
|
target_build_id: str
|
|
124
229
|
|
|
125
230
|
|
|
231
|
+
class CloudAgentVersionRollbackRequest(ContractModel):
|
|
232
|
+
version_id: str = Field(min_length=1, max_length=256)
|
|
233
|
+
|
|
234
|
+
|
|
126
235
|
class ModelProfileCreateRequest(ContractModel):
|
|
127
236
|
name: str = Field(pattern=r"^[a-z][a-z0-9._-]{1,127}$")
|
|
128
237
|
display_name: str = Field(min_length=1, max_length=128)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Memory observability and local management routes for Studio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from fastapi import FastAPI, Query
|
|
8
|
+
|
|
9
|
+
from ksadk.memory.coordinator import agent_user_scope_id
|
|
10
|
+
from ksadk.memory.models import MemoryDeleteRequest, MemorySearchRequest
|
|
11
|
+
from ksadk.memory.providers.local_sqlite import resolve_default_memory_provider
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_memory_routes(app: FastAPI, studio: Any) -> None:
|
|
15
|
+
"""Register PCM memory routes without expanding the central API module."""
|
|
16
|
+
|
|
17
|
+
@app.get("/api/v1/runs/{run_id}/memory-events")
|
|
18
|
+
async def get_run_memory_events(run_id: str):
|
|
19
|
+
events = studio.event_store.events(run_id)
|
|
20
|
+
items = []
|
|
21
|
+
for event in events:
|
|
22
|
+
data = event.data or {}
|
|
23
|
+
has_memory_payload = isinstance(data, dict) and (
|
|
24
|
+
"memory_event" in data
|
|
25
|
+
or any(
|
|
26
|
+
isinstance(value, str) and value.startswith("memory.")
|
|
27
|
+
for value in data.values()
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
if "memory" in str(event.type or "").lower() or has_memory_payload:
|
|
31
|
+
items.append({"id": event.id, "type": event.type, "data": data})
|
|
32
|
+
return {"items": items}
|
|
33
|
+
|
|
34
|
+
@app.get("/api/v1/memories")
|
|
35
|
+
async def list_memories(
|
|
36
|
+
user_id: str = Query(default="local-user", alias="userId"),
|
|
37
|
+
agent_id: str | None = Query(default=None, alias="agentId"),
|
|
38
|
+
):
|
|
39
|
+
provider = resolve_default_memory_provider()
|
|
40
|
+
scope_id = agent_user_scope_id(agent_id=agent_id, user_id=user_id) if agent_id else user_id
|
|
41
|
+
result = provider.search(
|
|
42
|
+
MemorySearchRequest(
|
|
43
|
+
query="",
|
|
44
|
+
scopes=[("user", scope_id)],
|
|
45
|
+
memory_types=["profile", "fact", "episode"],
|
|
46
|
+
top_k=100,
|
|
47
|
+
max_tokens=10000,
|
|
48
|
+
min_score=0.0,
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
return {
|
|
52
|
+
"items": [
|
|
53
|
+
{
|
|
54
|
+
"memory_id": record.memory_id,
|
|
55
|
+
"scope": record.scope,
|
|
56
|
+
"scope_id": record.scope_id,
|
|
57
|
+
"memory_type": record.memory_type,
|
|
58
|
+
"content": record.content[:200],
|
|
59
|
+
"summary": record.summary,
|
|
60
|
+
"status": record.status,
|
|
61
|
+
"confidence": record.confidence,
|
|
62
|
+
"created_at": record.created_at,
|
|
63
|
+
}
|
|
64
|
+
for record in result.records
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@app.delete("/api/v1/memories/{memory_id}")
|
|
69
|
+
async def delete_memory(memory_id: str):
|
|
70
|
+
provider = resolve_default_memory_provider()
|
|
71
|
+
record = provider.get(memory_id)
|
|
72
|
+
if record is None:
|
|
73
|
+
return {"deleted": False, "status": "ok"}
|
|
74
|
+
result = provider.delete(
|
|
75
|
+
MemoryDeleteRequest(
|
|
76
|
+
memory_id=memory_id,
|
|
77
|
+
scope=record.scope,
|
|
78
|
+
scope_id=record.scope_id,
|
|
79
|
+
hard=True,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
return {"deleted": result.deleted, "status": result.status}
|
ksadk/studio/authoring.py
CHANGED
|
@@ -7,6 +7,7 @@ ID allocation to the server.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import copy
|
|
10
11
|
import hashlib
|
|
11
12
|
import io
|
|
12
13
|
import json
|
|
@@ -21,14 +22,15 @@ from urllib.parse import urlparse
|
|
|
21
22
|
from uuid import uuid4
|
|
22
23
|
|
|
23
24
|
import yaml # type: ignore[import-untyped]
|
|
24
|
-
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|
25
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
|
25
26
|
|
|
26
27
|
from ksadk.detection.detector import FrameworkDetector
|
|
28
|
+
from ksadk.managed_runtime import installed_runtime_version
|
|
27
29
|
from ksadk.studio.capabilities import canonical_json, sha256_digest
|
|
28
30
|
from ksadk.studio.codex_manifest import CodexAgentManifest
|
|
29
31
|
from ksadk.studio.contracts import (
|
|
30
32
|
AgentDraft,
|
|
31
|
-
|
|
33
|
+
AgentSpec,
|
|
32
34
|
NetworkPolicy,
|
|
33
35
|
RuntimeRef,
|
|
34
36
|
)
|
|
@@ -48,7 +50,33 @@ class ConversationProposal(BaseModel):
|
|
|
48
50
|
slug: str = Field(min_length=1, max_length=63)
|
|
49
51
|
runtimeType: Literal["codex", "adk", "langgraph"]
|
|
50
52
|
description: str = Field(default="", max_length=1024)
|
|
51
|
-
|
|
53
|
+
spec: AgentSpec
|
|
54
|
+
|
|
55
|
+
@model_validator(mode="before")
|
|
56
|
+
@classmethod
|
|
57
|
+
def migrate_prompt_only_proposal(cls, value: Any) -> Any:
|
|
58
|
+
"""Accept one release of old model output without persisting its data loss.
|
|
59
|
+
|
|
60
|
+
Older authoring prompts asked the model for only ``instructions``. Turn
|
|
61
|
+
that response into a complete AgentSpec at the boundary so callers only
|
|
62
|
+
ever consume the lossless proposal shape.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
if not isinstance(value, dict) or "spec" in value:
|
|
66
|
+
return value
|
|
67
|
+
payload = dict(value)
|
|
68
|
+
instructions = payload.pop("instructions", None)
|
|
69
|
+
payload["spec"] = {
|
|
70
|
+
"description": str(payload.get("description") or ""),
|
|
71
|
+
"instructions": instructions or {},
|
|
72
|
+
}
|
|
73
|
+
return payload
|
|
74
|
+
|
|
75
|
+
@model_validator(mode="after")
|
|
76
|
+
def validate_runtime_type(self) -> "ConversationProposal":
|
|
77
|
+
if self.spec.runtime is not None and self.spec.runtime.type != self.runtimeType:
|
|
78
|
+
raise ValueError("spec.runtime.type 必须与 runtimeType 一致")
|
|
79
|
+
return self
|
|
52
80
|
|
|
53
81
|
|
|
54
82
|
@dataclass(frozen=True)
|
|
@@ -82,16 +110,11 @@ class AgentAuthoringService:
|
|
|
82
110
|
normalized = f"agent-{normalized}" if normalized else "agent"
|
|
83
111
|
return normalized[:48].rstrip("-")
|
|
84
112
|
|
|
85
|
-
def allocate_agent_id(self,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Callers retain the normalized slug in Agent metadata for display and search,
|
|
89
|
-
while source directories only use this opaque identifier.
|
|
90
|
-
"""
|
|
91
|
-
|
|
113
|
+
def allocate_agent_id(self, slug: str) -> str:
|
|
114
|
+
base = self.normalize_slug(slug)
|
|
92
115
|
for _attempt in range(100):
|
|
93
|
-
candidate = f"
|
|
94
|
-
if not
|
|
116
|
+
candidate = f"{base}-{uuid4().hex[:12]}"
|
|
117
|
+
if not self.workspace.resolve(Path("agents") / candidate).exists():
|
|
95
118
|
return candidate
|
|
96
119
|
raise StudioError(
|
|
97
120
|
"AGENT_ID_ALLOCATION_FAILED",
|
|
@@ -111,7 +134,13 @@ class AgentAuthoringService:
|
|
|
111
134
|
details={"runtimeType": normalized},
|
|
112
135
|
)
|
|
113
136
|
if normalized == "codex":
|
|
114
|
-
|
|
137
|
+
# A new YAML Agent must lock the CLI actually installed on this
|
|
138
|
+
# Studio host. Cloud admission resolves that explicit version via
|
|
139
|
+
# the Server-owned catalog instead of accepting a client image.
|
|
140
|
+
return RuntimeRef(
|
|
141
|
+
type="codex",
|
|
142
|
+
version=installed_runtime_version("codex") or "0.144.4",
|
|
143
|
+
)
|
|
115
144
|
return RuntimeRef(
|
|
116
145
|
type=cast(Any, normalized),
|
|
117
146
|
project_path=f"agents/{agent_id}/source",
|
|
@@ -291,17 +320,197 @@ class AgentAuthoringService:
|
|
|
291
320
|
path.unlink()
|
|
292
321
|
|
|
293
322
|
@staticmethod
|
|
294
|
-
def
|
|
323
|
+
def _sanitize_model_block(payload: dict[str, Any]) -> None:
|
|
324
|
+
"""清掉模型照抄示例或凭空编造的 spec.model 字段。
|
|
325
|
+
|
|
326
|
+
- baseUrl/endpointUrl 写着 example.com/placeholder 等占位域名的直接删掉
|
|
327
|
+
(Studio 会按选中的模型 Profile 注入真实 endpoint)
|
|
328
|
+
- parameters 只保留用户对话明确要求时模型写出的值;模型自行编造的
|
|
329
|
+
常见值(temperature 0.x + maxTokens 2048/4096 这类组合)无法与
|
|
330
|
+
用户意图区分时一并删除,交平台默认值兜底
|
|
331
|
+
"""
|
|
332
|
+
spec = payload.get("spec")
|
|
333
|
+
if not isinstance(spec, dict):
|
|
334
|
+
return
|
|
335
|
+
model = spec.get("model")
|
|
336
|
+
if not isinstance(model, dict):
|
|
337
|
+
return
|
|
338
|
+
for key in ("baseUrl", "endpointUrl"):
|
|
339
|
+
value = model.get(key)
|
|
340
|
+
if not isinstance(value, str):
|
|
341
|
+
continue
|
|
342
|
+
try:
|
|
343
|
+
hostname = (urlparse(value).hostname or "").lower().rstrip(".")
|
|
344
|
+
except ValueError:
|
|
345
|
+
hostname = ""
|
|
346
|
+
labels = hostname.split(".")
|
|
347
|
+
is_example_host = hostname == "example.com" or hostname.endswith(".example.com")
|
|
348
|
+
is_placeholder_host = any(label == "placeholder" for label in labels)
|
|
349
|
+
if is_example_host or is_placeholder_host:
|
|
350
|
+
model.pop(key, None)
|
|
351
|
+
# ModelSpec 校验要求 baseUrl/endpointUrl 二选一;模型没写或写了占位被删时,
|
|
352
|
+
# 置一个显式标记值,coordinator 会用选中模型 Profile 的真实 endpoint 覆写。
|
|
353
|
+
if not model.get("baseUrl") and not model.get("endpointUrl"):
|
|
354
|
+
model["baseUrl"] = "https://model-profile.invalid/placeholder"
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def _coerce_model_credential_ref(payload: dict[str, Any]) -> None:
|
|
358
|
+
"""容忍模型把 spec.model.credentialRef 写成对象/空值的常见错误形态。
|
|
359
|
+
|
|
360
|
+
模型偶尔会把字符串引用字段写成 {} 或 {"ref": ...};在进入 Pydantic 校验前
|
|
361
|
+
收敛为默认 env 引用,避免浪费唯一一次纠错重试。
|
|
362
|
+
"""
|
|
363
|
+
spec = payload.get("spec")
|
|
364
|
+
if not isinstance(spec, dict):
|
|
365
|
+
return
|
|
366
|
+
model = spec.get("model")
|
|
367
|
+
if not isinstance(model, dict):
|
|
368
|
+
return
|
|
369
|
+
ref = model.get("credentialRef")
|
|
370
|
+
if isinstance(ref, str) and ref.strip().startswith(("env://", "keychain://", "secret-manager://")):
|
|
371
|
+
return
|
|
372
|
+
if isinstance(ref, dict):
|
|
373
|
+
nested = ref.get("ref") or ref.get("credentialRef") or ref.get("value")
|
|
374
|
+
if isinstance(nested, str) and nested.strip().startswith(("env://", "keychain://", "secret-manager://")):
|
|
375
|
+
model["credentialRef"] = nested.strip()
|
|
376
|
+
return
|
|
377
|
+
model["credentialRef"] = "env://AGENTKIT_MODEL_API_KEY"
|
|
378
|
+
|
|
379
|
+
@staticmethod
|
|
380
|
+
def _coerce_runtime_type(payload: dict[str, Any]) -> None:
|
|
381
|
+
"""容忍模型把 spec.runtime.type 写成 provider 的常见错误形态。"""
|
|
382
|
+
spec = payload.get("spec")
|
|
383
|
+
if not isinstance(spec, dict):
|
|
384
|
+
return
|
|
385
|
+
runtime = spec.get("runtime")
|
|
386
|
+
if not isinstance(runtime, dict):
|
|
387
|
+
return
|
|
388
|
+
if not runtime.get("type") and runtime.get("provider"):
|
|
389
|
+
runtime["type"] = runtime.pop("provider")
|
|
390
|
+
elif runtime.get("provider") and runtime.get("type"):
|
|
391
|
+
runtime.pop("provider")
|
|
392
|
+
|
|
393
|
+
@staticmethod
|
|
394
|
+
def _conversation_json_object(content: str) -> dict[str, Any]:
|
|
395
|
+
"""Extract one JSON object without trusting surrounding model prose."""
|
|
396
|
+
|
|
295
397
|
text = str(content or "").strip()
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
398
|
+
candidates = [text]
|
|
399
|
+
candidates.extend(
|
|
400
|
+
match.group(1).strip()
|
|
401
|
+
for match in re.finditer(r"```(?:json)?\s*([\s\S]*?)```", text, re.IGNORECASE)
|
|
402
|
+
)
|
|
403
|
+
decoder = json.JSONDecoder()
|
|
404
|
+
for candidate in candidates:
|
|
405
|
+
try:
|
|
406
|
+
payload = json.loads(candidate)
|
|
407
|
+
except ValueError:
|
|
408
|
+
payload = None
|
|
409
|
+
if isinstance(payload, dict):
|
|
410
|
+
return cast(dict[str, Any], payload)
|
|
411
|
+
for start, character in enumerate(text):
|
|
412
|
+
if character != "{":
|
|
413
|
+
continue
|
|
414
|
+
try:
|
|
415
|
+
payload, _end = decoder.raw_decode(text, start)
|
|
416
|
+
except ValueError:
|
|
417
|
+
continue
|
|
418
|
+
if isinstance(payload, dict):
|
|
419
|
+
return cast(dict[str, Any], payload)
|
|
420
|
+
raise ValueError("model output does not contain a JSON object")
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def _merge_conversation_patch(
|
|
424
|
+
base: dict[str, Any], patch: dict[str, Any]
|
|
425
|
+
) -> dict[str, Any]:
|
|
426
|
+
merged = copy.deepcopy(base)
|
|
427
|
+
for key, value in patch.items():
|
|
428
|
+
if isinstance(value, dict) and isinstance(merged.get(key), dict):
|
|
429
|
+
merged[key] = AgentAuthoringService._merge_conversation_patch(
|
|
430
|
+
cast(dict[str, Any], merged[key]), value
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
merged[key] = copy.deepcopy(value)
|
|
434
|
+
return merged
|
|
435
|
+
|
|
436
|
+
@staticmethod
|
|
437
|
+
def _studio_owned_conversation_patch(
|
|
438
|
+
payload: dict[str, Any],
|
|
439
|
+
*,
|
|
440
|
+
runtime_type: str,
|
|
441
|
+
) -> dict[str, Any]:
|
|
442
|
+
"""Keep an authoring-model response to semantic fields only.
|
|
443
|
+
|
|
444
|
+
A conversation can describe an Agent, but it cannot produce a valid
|
|
445
|
+
local ADK/LangGraph project, resource identity, credential or runtime
|
|
446
|
+
binding. Treating its complete AgentSpec as deployable made a simple
|
|
447
|
+
conversation depend on it guessing every evolving Studio contract.
|
|
448
|
+
Studio owns those fields and injects them after this parser returns.
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
normalized = str(runtime_type or "").strip().lower()
|
|
452
|
+
if normalized not in _SUPPORTED_RUNTIMES:
|
|
453
|
+
raise ValueError("runtimeType is not supported")
|
|
454
|
+
raw_spec = payload.get("spec")
|
|
455
|
+
if not isinstance(raw_spec, dict):
|
|
456
|
+
raw_spec = {}
|
|
457
|
+
raw_instructions = raw_spec.get("instructions", payload.get("instructions"))
|
|
458
|
+
instructions = raw_instructions if isinstance(raw_instructions, dict) else {}
|
|
459
|
+
spec: dict[str, Any] = {
|
|
460
|
+
"instructions": {
|
|
461
|
+
key: str(value).strip()
|
|
462
|
+
for key in ("system", "task")
|
|
463
|
+
if isinstance((value := instructions.get(key)), str) and value.strip()
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if isinstance(raw_spec.get("description"), str):
|
|
467
|
+
spec["description"] = raw_spec["description"].strip()
|
|
468
|
+
return {
|
|
469
|
+
key: copy.deepcopy(payload[key])
|
|
470
|
+
for key in ("name", "slug", "description")
|
|
471
|
+
if key in payload
|
|
472
|
+
} | {
|
|
473
|
+
"runtimeType": normalized,
|
|
474
|
+
"spec": spec,
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@staticmethod
|
|
478
|
+
def parse_conversation_proposal(
|
|
479
|
+
content: str,
|
|
480
|
+
*,
|
|
481
|
+
base: ConversationProposal | dict[str, Any] | None = None,
|
|
482
|
+
runtime_type: str | None = None,
|
|
483
|
+
) -> ConversationProposal:
|
|
303
484
|
try:
|
|
304
|
-
payload =
|
|
485
|
+
payload = AgentAuthoringService._conversation_json_object(content)
|
|
486
|
+
for wrapper in ("proposal", "patch"):
|
|
487
|
+
wrapped = payload.get(wrapper)
|
|
488
|
+
if isinstance(wrapped, dict) and len(payload) == 1:
|
|
489
|
+
payload = cast(dict[str, Any], wrapped)
|
|
490
|
+
break
|
|
491
|
+
if runtime_type is not None:
|
|
492
|
+
payload = AgentAuthoringService._studio_owned_conversation_patch(
|
|
493
|
+
payload,
|
|
494
|
+
runtime_type=runtime_type,
|
|
495
|
+
)
|
|
496
|
+
AgentAuthoringService._coerce_model_credential_ref(payload)
|
|
497
|
+
AgentAuthoringService._sanitize_model_block(payload)
|
|
498
|
+
AgentAuthoringService._coerce_runtime_type(payload)
|
|
499
|
+
if base is not None:
|
|
500
|
+
base_payload = (
|
|
501
|
+
base.model_dump(by_alias=True, mode="json")
|
|
502
|
+
if isinstance(base, ConversationProposal)
|
|
503
|
+
else base
|
|
504
|
+
)
|
|
505
|
+
payload = AgentAuthoringService._merge_conversation_patch(
|
|
506
|
+
base_payload, payload
|
|
507
|
+
)
|
|
508
|
+
if runtime_type is not None:
|
|
509
|
+
# The merge can reintroduce an old runtimeType from a previous
|
|
510
|
+
# Draft Patch; the live Studio selector remains authoritative.
|
|
511
|
+
payload["runtimeType"] = str(runtime_type).strip().lower()
|
|
512
|
+
if isinstance(payload.get("spec"), dict):
|
|
513
|
+
payload["spec"].pop("runtime", None)
|
|
305
514
|
proposal = ConversationProposal.model_validate(payload)
|
|
306
515
|
except (ValueError, ValidationError) as exc:
|
|
307
516
|
raise StudioError(
|
|
@@ -314,7 +523,11 @@ class AgentAuthoringService:
|
|
|
314
523
|
return proposal.model_copy(update={"slug": normalized_slug})
|
|
315
524
|
|
|
316
525
|
@staticmethod
|
|
317
|
-
def conversation_messages(
|
|
526
|
+
def conversation_messages(
|
|
527
|
+
messages: list[dict[str, str]],
|
|
528
|
+
*,
|
|
529
|
+
runtime_type: str = "codex",
|
|
530
|
+
) -> list[dict[str, str]]:
|
|
318
531
|
if not messages:
|
|
319
532
|
raise StudioError(
|
|
320
533
|
"AUTHORING_CONVERSATION_EMPTY",
|
|
@@ -326,9 +539,14 @@ class AgentAuthoringService:
|
|
|
326
539
|
"role": "system",
|
|
327
540
|
"content": (
|
|
328
541
|
"你是 AgentKit Studio 的 Agent 设计助手。根据对话生成一个 JSON Draft Patch,"
|
|
329
|
-
"不得输出 Markdown
|
|
330
|
-
"
|
|
331
|
-
"
|
|
542
|
+
"不得输出 Markdown。只返回一个最小 JSON Draft Patch:首轮只包含 name、"
|
|
543
|
+
"slug、description、spec;spec 只包含 instructions,instructions 只允许"
|
|
544
|
+
"system 和 task。后续轮次只返回要更新的上述字段,由 Studio 与上一版 Patch"
|
|
545
|
+
"合并。当前 Runtime 已由 Studio 选择为 "
|
|
546
|
+
f"{runtime_type},不得输出 runtimeType、spec.runtime、execution、context、"
|
|
547
|
+
"memory、security、evaluation、model、bindings、capabilities 或任何资源 ID。"
|
|
548
|
+
"模型 Profile、运行 Runtime、模型参数、Tool、MCP、Skill、凭证、端点与"
|
|
549
|
+
"资源 ID 都由 Studio 按用户选择注入。只提出配置,不写文件、不宣称已经创建。"
|
|
332
550
|
),
|
|
333
551
|
}
|
|
334
552
|
]
|
|
@@ -469,7 +687,11 @@ class AgentAuthoringService:
|
|
|
469
687
|
|
|
470
688
|
@staticmethod
|
|
471
689
|
def _tree_digest(root: Path, *, exclude: set[str] | None = None) -> str:
|
|
472
|
-
|
|
690
|
+
# 默认排除 .agentkit(Studio 自身状态)与常见忽略目录,避免 inspect 后写 token json
|
|
691
|
+
# 改变 digest 导致 commit 时 PROJECT_CHANGED_AFTER_INSPECTION 误报(方案 §6.1)。
|
|
692
|
+
ignored = set(exclude or [])
|
|
693
|
+
ignored.add(".agentkit")
|
|
694
|
+
ignored.add(".git")
|
|
473
695
|
entries: list[dict[str, Any]] = []
|
|
474
696
|
for path in sorted(root.rglob("*"), key=lambda item: item.as_posix()):
|
|
475
697
|
if path.is_symlink():
|
|
@@ -481,7 +703,10 @@ class AgentAuthoringService:
|
|
|
481
703
|
)
|
|
482
704
|
if not path.is_file() or path.name in ignored:
|
|
483
705
|
continue
|
|
706
|
+
# 跳过 .agentkit / .git 目录下的文件
|
|
484
707
|
relative = path.relative_to(root).as_posix()
|
|
708
|
+
if any(relative.startswith(ignored_dir + "/") for ignored_dir in (".agentkit", ".git")):
|
|
709
|
+
continue
|
|
485
710
|
content = path.read_bytes()
|
|
486
711
|
entries.append(
|
|
487
712
|
{
|