agentengine-sdk-python 0.8.2__py3-none-any.whl → 0.8.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/METADATA +18 -25
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/RECORD +224 -169
- ksadk/_build_provenance.json +1 -0
- ksadk/agui/_agent_helpers.py +4 -0
- ksadk/api/client.py +3 -0
- ksadk/builders/managed_runtime_builder.py +3 -0
- ksadk/cli/__init__.py +6 -0
- ksadk/cli/cmd_plugin.py +657 -0
- ksadk/codex/runtime.py +50 -8
- ksadk/configs/env_registry.py +31 -3
- ksadk/configs/settings.py +9 -0
- ksadk/context_engine/contributors.py +73 -8
- ksadk/conversations/__init__.py +19 -0
- ksadk/conversations/context.py +104 -24
- ksadk/conversations/contracts.py +266 -0
- ksadk/conversations/message_projection.py +19 -1
- ksadk/conversations/projector.py +616 -0
- ksadk/conversations/reducer.py +98 -0
- ksadk/events/_v1_compat/projection.py +2 -0
- ksadk/events/adapters/codex.py +190 -0
- ksadk/harness/__init__.py +45 -35
- ksadk/harness/runtime.py +60 -1
- ksadk/kernel/contract_fingerprints.py +5 -4
- ksadk/kernel/contracts.py +8 -0
- ksadk/kernel/control.py +1 -0
- ksadk/kernel/ingress.py +21 -0
- ksadk/kernel/memory_store.py +1 -0
- ksadk/kernel/postgres_store.py +1 -0
- ksadk/kernel/sqlite_store.py +1 -0
- ksadk/kernel/worker.py +11 -0
- ksadk/memory/providers/local_sqlite.py +7 -0
- ksadk/plugins/__init__.py +167 -0
- ksadk/plugins/bridges/__init__.py +45 -0
- ksadk/plugins/bridges/codex.py +552 -0
- ksadk/plugins/bridges/dsh.py +1116 -0
- ksadk/plugins/builtins.py +747 -0
- ksadk/plugins/bundle.py +246 -0
- ksadk/plugins/bundle_security.py +167 -0
- ksadk/plugins/composition.py +529 -0
- ksadk/plugins/context_contributor.py +437 -0
- ksadk/plugins/contracts.py +451 -0
- ksadk/plugins/dsh_toolchain.py +935 -0
- ksadk/plugins/ecosystem_bridge.py +1105 -0
- ksadk/plugins/ecosystem_probe.py +191 -0
- ksadk/plugins/host.py +724 -0
- ksadk/plugins/providers/__init__.py +85 -0
- ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
- ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
- ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
- ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
- ksadk/plugins/providers/codex.py +722 -0
- ksadk/plugins/providers/codex_dsh.py +83 -0
- ksadk/plugins/providers/dsh.py +951 -0
- ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
- ksadk/plugins/providers/harness.py +627 -0
- ksadk/plugins/providers/harness_dsh.py +91 -0
- ksadk/plugins/providers/legacy.py +215 -0
- ksadk/plugins/providers/legacy_catalog.py +100 -0
- ksadk/plugins/providers/shipped_dsh.py +370 -0
- ksadk/plugins/resolver.py +331 -0
- ksadk/plugins/subagent_providers/__init__.py +12 -0
- ksadk/plugins/subagent_providers/codex.py +443 -0
- ksadk/plugins/subagents.py +333 -0
- ksadk/runtime/_runner_adapter/stream_mapping.py +51 -42
- ksadk/runtime/adapter.py +1 -0
- ksadk/runtime/factory.py +76 -2
- ksadk/runtime/framework_adapters.py +18 -0
- ksadk/runtime/runner_adapter.py +5 -0
- ksadk/sandbox/__init__.py +2 -1
- ksadk/sandbox/factory.py +72 -0
- ksadk/scheduler/__init__.py +18 -0
- ksadk/scheduler/calendar.py +102 -0
- ksadk/scheduler/contracts.py +254 -0
- ksadk/scheduler/dispatcher.py +129 -0
- ksadk/scheduler/engine.py +416 -0
- ksadk/scheduler/sqlite_store.py +563 -0
- ksadk/server/static/assets/{ArtifactsPanel-DSU1sWD_.js → ArtifactsPanel-10GPXIJ6.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-DOH6MVcn.js → CodeBlock-BXk1l-G1.js} +1 -1
- ksadk/server/static/assets/{MathMessageMarkdown-BL8my1R2.js → MathMessageMarkdown-D9uB55Oh.js} +1 -1
- ksadk/server/static/assets/{MermaidBlock-Dz4IP-Tx.js → MermaidBlock--OEYoXIJ.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-DUrK0JpZ.js → NativeTerminalPanel-DrEe1syq.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-g20pFzNV.js → abnfDiagram-VCTEODGH-eeTFJLvo.js} +1 -1
- ksadk/server/static/assets/{arc-C4FzinUA.js → arc-S3ZNbIAd.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-DpIqL5h4.js → architectureDiagram-5GKGNRK7-CRjgEJDX.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-BjkljTNz.js → blockDiagram-NRAW4CY4-C7bsBoNH.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-Dk_ieq2X.js → c4Diagram-UCG6FXSJ-BX3c33Ob.js} +1 -1
- ksadk/server/static/assets/channel-s354Yo6o.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-BVHAchFb.js → chunk-5VM5RSS4-bZ7ws-8m.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-C-ipQzuS.js → chunk-F27PBJKO-D2r0pvhY.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-C2EyJbIK.js → chunk-G27WJ6UU-CZwF3oUP.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-vYvVJb_M.js → chunk-JWPE2WC7-DigFYCML.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-BcI6ysPT.js → chunk-LCL6LL3I-CnorGV5q.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-C030x_Z1.js → chunk-POPQ4Y6H-CexntQA-.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-yyGpsz4n.js → chunk-RHFEMEQ7-CHTC7UxA.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-BLTlmMU7.js → chunk-SVP7TREG-BC1EWt_-.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-C_32ArgP.js → chunk-XXDRQBXY-BTG24xN0.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-9YQYwdl0.js → cose-bilkent-JH36ORCC-CkonRdAS.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-ErLF5N13.js → cynefinDiagram-5FMLGOSQ-DEOCBe15.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-DFiQF-6f.js → dagre-3AP2YEHR-DBThPuLa.js} +1 -1
- ksadk/server/static/assets/{dagre-BuhGnRI1.js → dagre-ClTPrTht.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-Bt1v8-GC.js → diagram-S7CK7UJ4-DsZHrnpJ.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-DSCxJdBK.js → diagram-UQ7AKVKN-BJR4nhVr.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-DHfYwp_9.js → diagram-VSXAHHWV-xzcERX0a.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-DdzD-4Le.js → diagram-VX7I27RA-tn2xM1xu.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-Du-nAJ9F.js → diagram-Z3DM3KII-BZPGBkw5.js} +1 -1
- ksadk/server/static/assets/{dist-ClxGviKw.js → dist-4zN1_tq5.js} +1 -1
- ksadk/server/static/assets/{dist-BE7bu-DL.js → dist-8VWI-2bZ.js} +1 -1
- ksadk/server/static/assets/{dist-BX8z72IC.js → dist-B-46q3Hk.js} +1 -1
- ksadk/server/static/assets/{dist-BjU6y-A2.js → dist-B0gdk4rl.js} +1 -1
- ksadk/server/static/assets/{dist-C_wsv-Qd.js → dist-B1oWRmrH.js} +1 -1
- ksadk/server/static/assets/{dist-ngtN0DJB.js → dist-BI1qQH4n.js} +1 -1
- ksadk/server/static/assets/{dist-dydu68Fl.js → dist-BJLUwH6W.js} +1 -1
- ksadk/server/static/assets/{dist-Dq9gLD7L.js → dist-Bbwqa8TG.js} +1 -1
- ksadk/server/static/assets/{dist-CttYUqzS.js → dist-Btyo4vX4.js} +1 -1
- ksadk/server/static/assets/{dist-CXYYBw3_.js → dist-C71CsujO.js} +1 -1
- ksadk/server/static/assets/{dist-dDdADKFA.js → dist-CXpH_Rdd.js} +1 -1
- ksadk/server/static/assets/{dist-CSh_DE14.js → dist-Cq4OL6nO.js} +1 -1
- ksadk/server/static/assets/{dist-8hqcx0sU.js → dist-D1V3f1B4.js} +1 -1
- ksadk/server/static/assets/{dist-BVDffZ0U.js → dist-DJmCPRVB.js} +1 -1
- ksadk/server/static/assets/{dist-DnfXs8Vn.js → dist-DQAB0qn_.js} +1 -1
- ksadk/server/static/assets/{dist-DNp6rLRA.js → dist-DUwKa_l6.js} +1 -1
- ksadk/server/static/assets/{dist-B7seoj3d.js → dist-DdRBIceA.js} +1 -1
- ksadk/server/static/assets/{dist-BytlxIDT.js → dist-DfGFrb0v.js} +1 -1
- ksadk/server/static/assets/{dist-Bf2M8m3N.js → dist-DjhwGUgZ.js} +1 -1
- ksadk/server/static/assets/{dist-W-TIVntx.js → dist-DyqV4fL4.js} +1 -1
- ksadk/server/static/assets/{dist-DUX-id_F.js → dist-M67Hwu0n.js} +1 -1
- ksadk/server/static/assets/{dist-DmldrHd_.js → dist-u8tVH5Ak.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-Da9C9NO1.js → ebnfDiagram-PWID7BFC-DVfMsLge.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-EAGqqR79.js → erDiagram-SSCWMZ5O-CWEZSYaw.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DqJsKb59.js → ganttDiagram-EL5Y4UJY-TWIxoyYL.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-B6g4dtDi.js → gitGraphDiagram-WWUBYQGX-DDh2hd2S.js} +1 -1
- ksadk/server/static/assets/index-B2k_urY8.js +241 -0
- ksadk/server/static/assets/index-ILPt6IWF.css +2 -0
- ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-Djm_nbd5.js → infoDiagram-RXCK75RN-CgoyWiDR.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-DWya9SG2.js → ishikawaDiagram-5VMMS53U-CyRIu_om.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DutxhSNI.js → journeyDiagram-EYS64GPL-DN5JXHzN.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-DJdOF8Fm.js → kanban-definition-3QL26DDD-CNP3ui66.js} +1 -1
- ksadk/server/static/assets/{katex-vFWytM5c.js → katex-DWEzQy1d.js} +1 -1
- ksadk/server/static/assets/{linear-BHjG8b-J.js → linear-C-qHp7d2.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-KGSy4jWT.js → mermaid-parser.core-Cl-K943T.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-9-HC88D4.js → mindmap-definition-FBJOCRG2-D5DB5mYm.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-DrEZd4fD.js → pegDiagram-XKGWAZYB-BQVc9ia_.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-DpjqOFFp.js → pieDiagram-E7YTZNPT-B6UCAvtY.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-BiNhgOXX.js → quadrantDiagram-AXDQQJYC-mIr2d3eb.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-_Y3Ojg6G.js → railroadDiagram-O6MQD6OU-Dt3FFDmK.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-DeCFdg9K.js → requirementDiagram-EFPCY7ZU-Cz_vQtgF.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-CPH75rhw.js → sankeyDiagram-P5KCCOFB-wlO1TsH5.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-OEpQQdr4.js → sequenceDiagram-WJ2MYXX4-CHRbElYq.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-heUErzhI.js → sizeCapture-X5ZJPWSS-DXX54iyO.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-DvA3jSMB.js → stateDiagram-HBIQ2CUA-Dp_lw9II.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-CR-dfN_t.js → swimlanes-XN3QIQJK-D8O5UlLG.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-DAVJf1TX.js → timeline-definition-24CTP7MA-8mfKcxvG.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-DwM8eDkh.js → vennDiagram-4TSXK5OY-EnNl0DE7.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DZL-Zz2t.js → wardleyDiagram-VM6X3IG4-DBu5fbYr.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js → xychartDiagram-S5SC5T6Z-BSVXs4IY.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/skills/service_client.py +48 -0
- ksadk/studio/agent_lifecycle.py +26 -20
- ksadk/studio/api.py +513 -71
- ksadk/studio/api_catalog_routes.py +5 -0
- ksadk/studio/api_contracts.py +34 -1
- ksadk/studio/api_plugin_routes.py +480 -0
- ksadk/studio/attachment_store.py +187 -0
- ksadk/studio/authoring.py +10 -4
- ksadk/studio/authoring_coordinator.py +12 -2
- ksadk/studio/builder.py +123 -43
- ksadk/studio/cloud.py +4 -0
- ksadk/studio/codex_agent_service.py +12 -1
- ksadk/studio/codex_builder.py +54 -15
- ksadk/studio/codex_manifest.py +21 -1
- ksadk/studio/codex_run.py +16 -4
- ksadk/studio/compatibility_report.py +445 -0
- ksadk/studio/compiler.py +6 -1
- ksadk/studio/contracts.py +123 -4
- ksadk/studio/dsh_provider_registration.py +768 -0
- ksadk/studio/event_store.py +54 -6
- ksadk/studio/framework_run.py +37 -4
- ksadk/studio/hosted_kernel.py +1 -1
- ksadk/studio/operations.py +12 -2
- ksadk/studio/plugin_composition.py +301 -0
- ksadk/studio/plugin_kernel_adapter.py +128 -0
- ksadk/studio/plugin_runtime.py +652 -0
- ksadk/studio/resource_catalog.py +6 -1
- ksadk/studio/run_service.py +713 -75
- ksadk/studio/runtime_source.py +9 -1
- ksadk/studio/scheduler_runtime.py +374 -0
- ksadk/studio/scheduler_service.py +211 -0
- ksadk/studio/service.py +672 -82
- ksadk/studio/shared_web.py +167 -28
- ksadk/studio/soul.py +52 -0
- ksadk/studio/static/assets/index-DXs1Eq4M.css +1 -0
- ksadk/studio/static/assets/index-DeDqwTsD.js +283 -0
- ksadk/studio/static/index.html +2 -2
- ksadk/studio/validator.py +1 -1
- ksadk/studio/workspace.py +1 -0
- ksadk/version.py +1 -1
- ksadk_runtime_common/workspace_files/path_utils.py +41 -0
- ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +0 -1
- ksadk/server/static/assets/channel-4cQHKtx1.js +0 -1
- ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +0 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +0 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +0 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +0 -1
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +0 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +0 -1
- ksadk/server/static/assets/index-8ipRcQ-M.js +0 -240
- ksadk/server/static/assets/index-ByFCqjlh.css +0 -2
- ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +0 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +0 -1
- ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +0 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +0 -1
- ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +0 -1
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +0 -1
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +0 -1
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +0 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +0 -8
- ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +0 -1
- ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +0 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +0 -1
- ksadk/studio/static/assets/index-BLpcdrgW.css +0 -1
- ksadk/studio/static/assets/index-CYekR2Xv.js +0 -260
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
"""Local Studio execution for immutable PluginHost AgentBundle v2 builds."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, cast
|
|
11
|
+
|
|
12
|
+
from ksadk.harness.reasoner import (
|
|
13
|
+
HarnessReasoner,
|
|
14
|
+
HarnessReasoningTurn,
|
|
15
|
+
HarnessToolCall,
|
|
16
|
+
)
|
|
17
|
+
from ksadk.plugins.builtins import (
|
|
18
|
+
builtin_capability_factories,
|
|
19
|
+
builtin_capability_manifests,
|
|
20
|
+
)
|
|
21
|
+
from ksadk.plugins.bundle import PluginBundleError, PluginBundleResolver, ResolvedPluginBundle
|
|
22
|
+
from ksadk.plugins.contracts import CompositionProfile, PluginManifest
|
|
23
|
+
from ksadk.plugins.host import PluginHost, PluginHostError
|
|
24
|
+
from ksadk.plugins.providers.harness import (
|
|
25
|
+
HarnessTurnResult,
|
|
26
|
+
KsADKHarnessProviderFactory,
|
|
27
|
+
)
|
|
28
|
+
from ksadk.plugins.providers.legacy import (
|
|
29
|
+
LegacyBundleAdapter,
|
|
30
|
+
LegacyBundleCompatibilityError,
|
|
31
|
+
LegacyHarnessSource,
|
|
32
|
+
)
|
|
33
|
+
from ksadk.plugins.providers.legacy_catalog import (
|
|
34
|
+
KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID,
|
|
35
|
+
builtin_agent_provider_manifests,
|
|
36
|
+
legacy_harness_agent_provider_manifest,
|
|
37
|
+
)
|
|
38
|
+
from ksadk.plugins.resolver import PluginRegistry
|
|
39
|
+
from ksadk.runtime import RuntimeLaunchContext
|
|
40
|
+
from ksadk.sessions.base import BaseSessionService
|
|
41
|
+
from ksadk.studio.contracts import (
|
|
42
|
+
BuildRecord,
|
|
43
|
+
NetworkPolicy,
|
|
44
|
+
ResolvedModel,
|
|
45
|
+
)
|
|
46
|
+
from ksadk.studio.errors import StudioError
|
|
47
|
+
from ksadk.studio.model_client import OpenAICompatibleModelClient
|
|
48
|
+
from ksadk.studio.plugin_kernel_adapter import StudioPluginKernelAdapter
|
|
49
|
+
from ksadk.studio.repository import BuildRepository
|
|
50
|
+
from ksadk.studio.run_service import StudioRunSpec
|
|
51
|
+
from ksadk.studio.workspace import Workspace
|
|
52
|
+
|
|
53
|
+
_COMPOSED_RUNTIME_TYPES = frozenset({"harness", "plugin"})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class StudioPluginTurnResult:
|
|
58
|
+
"""Normalized result retained at the Studio/third-party boundary."""
|
|
59
|
+
|
|
60
|
+
output_text: str
|
|
61
|
+
session_id: str
|
|
62
|
+
usage: Mapping[str, Any]
|
|
63
|
+
metadata: Mapping[str, Any]
|
|
64
|
+
raw: Any
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class _HostEntry:
|
|
69
|
+
agent_id: str
|
|
70
|
+
bundle: ResolvedPluginBundle
|
|
71
|
+
host: PluginHost
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _StudioHarnessReasoner:
|
|
75
|
+
"""Bind the Harness loop to the immutable Studio model/network policy."""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
client: OpenAICompatibleModelClient,
|
|
80
|
+
*,
|
|
81
|
+
model: ResolvedModel,
|
|
82
|
+
network_policy: NetworkPolicy,
|
|
83
|
+
timeout_seconds: int,
|
|
84
|
+
max_attempts: int,
|
|
85
|
+
backoff_seconds: float,
|
|
86
|
+
) -> None:
|
|
87
|
+
self._client = client
|
|
88
|
+
self._model = model
|
|
89
|
+
self._network_policy = network_policy
|
|
90
|
+
self._timeout_seconds = timeout_seconds
|
|
91
|
+
self._max_attempts = max_attempts
|
|
92
|
+
self._backoff_seconds = backoff_seconds
|
|
93
|
+
|
|
94
|
+
async def complete(
|
|
95
|
+
self,
|
|
96
|
+
*,
|
|
97
|
+
model: str,
|
|
98
|
+
prompt: str,
|
|
99
|
+
messages: Sequence[dict[str, Any]],
|
|
100
|
+
tools: Sequence[Any],
|
|
101
|
+
) -> HarnessReasoningTurn:
|
|
102
|
+
del prompt
|
|
103
|
+
if model != self._model.model:
|
|
104
|
+
raise PluginHostError(
|
|
105
|
+
"harness_model_not_bound",
|
|
106
|
+
f"Harness requested unbound model {model!r}",
|
|
107
|
+
)
|
|
108
|
+
response = await self._client.complete(
|
|
109
|
+
self._model,
|
|
110
|
+
messages=[dict(message) for message in messages],
|
|
111
|
+
network_policy=self._network_policy,
|
|
112
|
+
timeout_seconds=self._timeout_seconds,
|
|
113
|
+
max_attempts=self._max_attempts,
|
|
114
|
+
backoff_seconds=self._backoff_seconds,
|
|
115
|
+
tools=[dict(tool.openai_schema) for tool in tools],
|
|
116
|
+
allow_empty=bool(tools),
|
|
117
|
+
)
|
|
118
|
+
calls: list[HarnessToolCall] = []
|
|
119
|
+
for call in response.tool_calls:
|
|
120
|
+
try:
|
|
121
|
+
arguments = json.loads(call.arguments or "{}")
|
|
122
|
+
except json.JSONDecodeError as error:
|
|
123
|
+
raise PluginHostError(
|
|
124
|
+
"harness_tool_arguments_invalid",
|
|
125
|
+
f"model emitted invalid arguments for tool {call.name!r}",
|
|
126
|
+
) from error
|
|
127
|
+
if not isinstance(arguments, dict):
|
|
128
|
+
raise PluginHostError(
|
|
129
|
+
"harness_tool_arguments_invalid",
|
|
130
|
+
f"model emitted non-object arguments for tool {call.name!r}",
|
|
131
|
+
)
|
|
132
|
+
calls.append(
|
|
133
|
+
HarnessToolCall(
|
|
134
|
+
call_id=call.id,
|
|
135
|
+
name=call.name,
|
|
136
|
+
arguments=arguments,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
return HarnessReasoningTurn(
|
|
140
|
+
final_text=response.content or None,
|
|
141
|
+
tool_calls=tuple(calls),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class StudioPluginRuntime:
|
|
146
|
+
"""Resolve, activate, and retain composed providers for Studio sessions."""
|
|
147
|
+
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
workspace: Workspace,
|
|
151
|
+
*,
|
|
152
|
+
build_repository: BuildRepository,
|
|
153
|
+
session_service: BaseSessionService,
|
|
154
|
+
model_client: OpenAICompatibleModelClient,
|
|
155
|
+
secret_resolver: Any,
|
|
156
|
+
harness_reasoner: HarnessReasoner | None = None,
|
|
157
|
+
provider_manifests: Mapping[str, PluginManifest] | None = None,
|
|
158
|
+
provider_factories: Mapping[str, Any] | None = None,
|
|
159
|
+
legacy_harness_sources: Sequence[LegacyHarnessSource] = (),
|
|
160
|
+
) -> None:
|
|
161
|
+
self.workspace = workspace
|
|
162
|
+
self.builds = build_repository
|
|
163
|
+
self._session_service = session_service
|
|
164
|
+
self._model_client = model_client
|
|
165
|
+
self._secret_resolver = secret_resolver
|
|
166
|
+
self._harness_reasoner = harness_reasoner
|
|
167
|
+
self._provider_manifests = dict(provider_manifests or {})
|
|
168
|
+
self._provider_factories = dict(provider_factories or {})
|
|
169
|
+
self._legacy_bundles = LegacyBundleAdapter(legacy_harness_sources)
|
|
170
|
+
self._lock = asyncio.Lock()
|
|
171
|
+
self._hosts: dict[str, _HostEntry] = {}
|
|
172
|
+
|
|
173
|
+
def replace_provider_registrations(
|
|
174
|
+
self,
|
|
175
|
+
provider_manifests: Mapping[str, PluginManifest],
|
|
176
|
+
provider_factories: Mapping[str, Any],
|
|
177
|
+
) -> None:
|
|
178
|
+
"""Bind one startup snapshot before any provider activation exists."""
|
|
179
|
+
|
|
180
|
+
manifests = dict(provider_manifests)
|
|
181
|
+
factories = dict(provider_factories)
|
|
182
|
+
if manifests.keys() != factories.keys():
|
|
183
|
+
raise ValueError(
|
|
184
|
+
"plugin provider manifests and factories must use the same exact references"
|
|
185
|
+
)
|
|
186
|
+
if self._hosts:
|
|
187
|
+
raise RuntimeError("cannot replace provider registrations after activation")
|
|
188
|
+
self._provider_manifests = manifests
|
|
189
|
+
self._provider_factories = factories
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def active_activation_count(self) -> int:
|
|
193
|
+
return sum(entry.host.activation_count for entry in self._hosts.values())
|
|
194
|
+
|
|
195
|
+
def resolve(self, build_id: str, *, model: str | None = None) -> StudioRunSpec:
|
|
196
|
+
build = self.builds.get(build_id)
|
|
197
|
+
runtime_type = build.runtime_type.strip().lower()
|
|
198
|
+
if runtime_type not in _COMPOSED_RUNTIME_TYPES:
|
|
199
|
+
raise StudioError(
|
|
200
|
+
"BUILD_RUNTIME_UNSUPPORTED",
|
|
201
|
+
"Build 不是 PluginHost Harness/Provider Runtime",
|
|
202
|
+
status_code=422,
|
|
203
|
+
details={"buildId": build_id, "runtimeType": runtime_type},
|
|
204
|
+
)
|
|
205
|
+
bundle_root = self._bundle_root(build)
|
|
206
|
+
bundle = self._resolve_bundle(bundle_root)
|
|
207
|
+
self._preflight_bundle(bundle)
|
|
208
|
+
selected_model = self._select_model(build, model)
|
|
209
|
+
resolved = bundle.resolved_agent_spec
|
|
210
|
+
instructions = resolved.get("instructions")
|
|
211
|
+
instructions = instructions if isinstance(instructions, Mapping) else {}
|
|
212
|
+
return StudioRunSpec(
|
|
213
|
+
launch_context=RuntimeLaunchContext(
|
|
214
|
+
runtime_type=runtime_type,
|
|
215
|
+
project_dir=bundle_root,
|
|
216
|
+
config={"plugin_bundle_digest": bundle.bundle_digest},
|
|
217
|
+
),
|
|
218
|
+
build_id=build.id,
|
|
219
|
+
agent_id=build.agent_id,
|
|
220
|
+
model=selected_model,
|
|
221
|
+
request_config={
|
|
222
|
+
"agent_system": str(instructions.get("system") or ""),
|
|
223
|
+
"agent_task": str(instructions.get("task") or ""),
|
|
224
|
+
"plugin_bundle_digest": bundle.bundle_digest,
|
|
225
|
+
},
|
|
226
|
+
manifest_sha256=build.resolved_digest,
|
|
227
|
+
plugin_bundle_root=bundle_root,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
async def execute(
|
|
231
|
+
self,
|
|
232
|
+
spec: StudioRunSpec,
|
|
233
|
+
request: Mapping[str, Any],
|
|
234
|
+
*,
|
|
235
|
+
session_id: str,
|
|
236
|
+
) -> StudioPluginTurnResult:
|
|
237
|
+
if spec.plugin_bundle_root is None:
|
|
238
|
+
raise PluginHostError(
|
|
239
|
+
"plugin_bundle_unavailable", "Studio run has no PluginHost Bundle"
|
|
240
|
+
)
|
|
241
|
+
entry = await self._host_for(spec.plugin_bundle_root)
|
|
242
|
+
if entry.agent_id != spec.agent_id:
|
|
243
|
+
raise PluginHostError(
|
|
244
|
+
"plugin_bundle_agent_mismatch",
|
|
245
|
+
"Studio run Agent does not match its immutable PluginHost Bundle",
|
|
246
|
+
)
|
|
247
|
+
activation = await entry.host.open_activation(
|
|
248
|
+
entry.bundle,
|
|
249
|
+
activation_key=session_id,
|
|
250
|
+
)
|
|
251
|
+
raw = await activation.execute(dict(request))
|
|
252
|
+
return _normalize_result(raw, session_id=session_id)
|
|
253
|
+
|
|
254
|
+
def kernel_adapter_provider(self, spec: StudioRunSpec): # type: ignore[no-untyped-def]
|
|
255
|
+
"""Return a lazy, Build-pinned adapter factory for Scheduler Kernel."""
|
|
256
|
+
|
|
257
|
+
if spec.plugin_bundle_root is None:
|
|
258
|
+
raise StudioError(
|
|
259
|
+
"PLUGIN_RUNTIME_UNAVAILABLE",
|
|
260
|
+
"Studio Build 没有 PluginHost Bundle",
|
|
261
|
+
status_code=409,
|
|
262
|
+
)
|
|
263
|
+
return lambda: StudioPluginKernelAdapter(self, spec)
|
|
264
|
+
|
|
265
|
+
async def kernel_adapter(
|
|
266
|
+
self,
|
|
267
|
+
spec: StudioRunSpec,
|
|
268
|
+
*,
|
|
269
|
+
session_id: str,
|
|
270
|
+
) -> Any:
|
|
271
|
+
"""Bind a Scheduler run to the provider-owned activation adapter."""
|
|
272
|
+
|
|
273
|
+
if spec.plugin_bundle_root is None:
|
|
274
|
+
raise StudioError(
|
|
275
|
+
"PLUGIN_RUNTIME_UNAVAILABLE",
|
|
276
|
+
"Studio Build 没有 PluginHost Bundle",
|
|
277
|
+
status_code=409,
|
|
278
|
+
)
|
|
279
|
+
entry = await self._host_for(spec.plugin_bundle_root)
|
|
280
|
+
if entry.agent_id != spec.agent_id:
|
|
281
|
+
raise PluginHostError(
|
|
282
|
+
"plugin_bundle_agent_mismatch",
|
|
283
|
+
"Studio run Agent does not match its immutable PluginHost Bundle",
|
|
284
|
+
)
|
|
285
|
+
activation = await entry.host.open_activation(
|
|
286
|
+
entry.bundle,
|
|
287
|
+
activation_key=session_id,
|
|
288
|
+
)
|
|
289
|
+
return await activation.runtime_adapter()
|
|
290
|
+
|
|
291
|
+
async def close_session(self, session_id: str) -> None:
|
|
292
|
+
async with self._lock:
|
|
293
|
+
entries = tuple(self._hosts.values())
|
|
294
|
+
for entry in entries:
|
|
295
|
+
await entry.host.close_activation(session_id)
|
|
296
|
+
|
|
297
|
+
async def aclose(self) -> None:
|
|
298
|
+
async with self._lock:
|
|
299
|
+
entries = tuple(self._hosts.values())
|
|
300
|
+
self._hosts.clear()
|
|
301
|
+
for entry in entries:
|
|
302
|
+
await entry.host.dispose()
|
|
303
|
+
|
|
304
|
+
async def _host_for(self, bundle_root: Path) -> _HostEntry:
|
|
305
|
+
# Re-resolve every turn. This deliberately rechecks enabled receipts and
|
|
306
|
+
# package digests rather than trusting a previously healthy child.
|
|
307
|
+
bundle = self._resolve_bundle(bundle_root)
|
|
308
|
+
key = bundle.bundle_digest
|
|
309
|
+
async with self._lock:
|
|
310
|
+
existing = self._hosts.get(key)
|
|
311
|
+
if existing is not None:
|
|
312
|
+
return existing
|
|
313
|
+
|
|
314
|
+
registry, factories, permissions = self._runtime_components(bundle)
|
|
315
|
+
verified = (
|
|
316
|
+
bundle
|
|
317
|
+
if bundle.manifest.bundle_format == "agentkit.bundle/v1"
|
|
318
|
+
else PluginBundleResolver(registry).resolve(bundle_root)
|
|
319
|
+
)
|
|
320
|
+
services: dict[str, Any] = {
|
|
321
|
+
"session_service": self._session_service,
|
|
322
|
+
# Providers resolve credential *references* at activation time.
|
|
323
|
+
# The DSH discovery host never receives this service.
|
|
324
|
+
"credential_resolver": self._secret_resolver,
|
|
325
|
+
}
|
|
326
|
+
provider_id, _provider_version = _parse_plugin_ref(
|
|
327
|
+
verified.composition.profile.agent_provider.ref
|
|
328
|
+
)
|
|
329
|
+
if provider_id == KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID:
|
|
330
|
+
services["harness_reasoner"] = (
|
|
331
|
+
self._harness_reasoner or self._bound_harness_reasoner(verified)
|
|
332
|
+
)
|
|
333
|
+
host = PluginHost(
|
|
334
|
+
registry,
|
|
335
|
+
factories,
|
|
336
|
+
allowed_permissions=permissions,
|
|
337
|
+
services=services,
|
|
338
|
+
)
|
|
339
|
+
try:
|
|
340
|
+
await host.apply(verified.composition.profile)
|
|
341
|
+
except BaseException:
|
|
342
|
+
await host.dispose()
|
|
343
|
+
raise
|
|
344
|
+
candidate = _HostEntry(
|
|
345
|
+
agent_id=verified.manifest.agent_id,
|
|
346
|
+
bundle=verified,
|
|
347
|
+
host=host,
|
|
348
|
+
)
|
|
349
|
+
stale = [
|
|
350
|
+
(digest, entry)
|
|
351
|
+
for digest, entry in self._hosts.items()
|
|
352
|
+
if entry.agent_id == candidate.agent_id and digest != key
|
|
353
|
+
]
|
|
354
|
+
self._hosts[key] = candidate
|
|
355
|
+
for digest, entry in stale:
|
|
356
|
+
self._hosts.pop(digest, None)
|
|
357
|
+
await entry.host.dispose()
|
|
358
|
+
return candidate
|
|
359
|
+
|
|
360
|
+
def _resolve_bundle(self, bundle_root: Path) -> ResolvedPluginBundle:
|
|
361
|
+
registered_ids = {
|
|
362
|
+
manifest.metadata.id for manifest in self._provider_manifests.values()
|
|
363
|
+
}
|
|
364
|
+
try:
|
|
365
|
+
manifest, selection = self._legacy_bundles.select_from_bundle(
|
|
366
|
+
bundle_root,
|
|
367
|
+
registered_provider_ids=registered_ids,
|
|
368
|
+
)
|
|
369
|
+
except LegacyBundleCompatibilityError as error:
|
|
370
|
+
code = (
|
|
371
|
+
"AGENT_PROVIDER_NOT_REGISTERED"
|
|
372
|
+
if error.code == "agent_provider_not_registered"
|
|
373
|
+
else "PLUGIN_BUNDLE_INVALID"
|
|
374
|
+
)
|
|
375
|
+
raise StudioError(
|
|
376
|
+
code,
|
|
377
|
+
"Harness Bundle 兼容性校验失败",
|
|
378
|
+
status_code=409,
|
|
379
|
+
details={"reason": error.code},
|
|
380
|
+
) from error
|
|
381
|
+
if selection is not None and selection.route == "legacy":
|
|
382
|
+
assert selection.manifest is not None
|
|
383
|
+
profile = CompositionProfile.model_validate(
|
|
384
|
+
{
|
|
385
|
+
"agentProvider": {
|
|
386
|
+
"ref": (
|
|
387
|
+
f"plugin://{selection.manifest.metadata.id}"
|
|
388
|
+
f"@{selection.manifest.metadata.version}"
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
registry = PluginRegistry(
|
|
394
|
+
[selection.manifest, *builtin_capability_manifests()]
|
|
395
|
+
)
|
|
396
|
+
try:
|
|
397
|
+
resolved = json.loads(
|
|
398
|
+
(bundle_root / "resolved-agent-spec.json").read_text(encoding="utf-8")
|
|
399
|
+
)
|
|
400
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as error:
|
|
401
|
+
raise StudioError(
|
|
402
|
+
"PLUGIN_BUNDLE_INVALID",
|
|
403
|
+
"Legacy Harness Bundle 缺少 resolved Agent spec",
|
|
404
|
+
status_code=409,
|
|
405
|
+
details={"reason": "legacy_resolved_spec_invalid"},
|
|
406
|
+
) from error
|
|
407
|
+
if not isinstance(resolved, dict):
|
|
408
|
+
raise StudioError(
|
|
409
|
+
"PLUGIN_BUNDLE_INVALID",
|
|
410
|
+
"Legacy Harness resolved Agent spec 必须是对象",
|
|
411
|
+
status_code=409,
|
|
412
|
+
details={"reason": "legacy_resolved_spec_invalid"},
|
|
413
|
+
)
|
|
414
|
+
return ResolvedPluginBundle(
|
|
415
|
+
root=bundle_root,
|
|
416
|
+
manifest=manifest,
|
|
417
|
+
resolved_agent_spec=resolved,
|
|
418
|
+
composition=registry.resolve(profile),
|
|
419
|
+
)
|
|
420
|
+
profile = self._read_profile(bundle_root)
|
|
421
|
+
manifests = [
|
|
422
|
+
*builtin_agent_provider_manifests(),
|
|
423
|
+
*builtin_capability_manifests(),
|
|
424
|
+
]
|
|
425
|
+
external = self._external_manifest(profile)
|
|
426
|
+
if external is not None:
|
|
427
|
+
manifests.append(external)
|
|
428
|
+
try:
|
|
429
|
+
return PluginBundleResolver(PluginRegistry(manifests)).resolve(bundle_root)
|
|
430
|
+
except PluginBundleError as error:
|
|
431
|
+
raise StudioError(
|
|
432
|
+
"PLUGIN_BUNDLE_INVALID",
|
|
433
|
+
"PluginHost Bundle 校验失败",
|
|
434
|
+
status_code=409,
|
|
435
|
+
details={"reason": error.code},
|
|
436
|
+
) from error
|
|
437
|
+
|
|
438
|
+
def _runtime_components(
|
|
439
|
+
self,
|
|
440
|
+
bundle: ResolvedPluginBundle,
|
|
441
|
+
) -> tuple[PluginRegistry, dict[str, Any], frozenset[str]]:
|
|
442
|
+
manifests: list[PluginManifest] = [
|
|
443
|
+
*builtin_agent_provider_manifests(),
|
|
444
|
+
*builtin_capability_manifests(),
|
|
445
|
+
]
|
|
446
|
+
factories = builtin_capability_factories(
|
|
447
|
+
state_root=self.workspace.resolve(".agentkit/plugin-runtime/state"),
|
|
448
|
+
secret_resolver=self._secret_resolver.resolve,
|
|
449
|
+
)
|
|
450
|
+
provider_id, provider_version = _parse_plugin_ref(
|
|
451
|
+
bundle.composition.profile.agent_provider.ref
|
|
452
|
+
)
|
|
453
|
+
provider_ref = f"plugin://{provider_id}@{provider_version}"
|
|
454
|
+
if bundle.manifest.bundle_format == "agentkit.bundle/v1":
|
|
455
|
+
manifest = legacy_harness_agent_provider_manifest()
|
|
456
|
+
factory = KsADKHarnessProviderFactory(session_service=self._session_service)
|
|
457
|
+
else:
|
|
458
|
+
manifest = self._provider_manifests.get(provider_ref)
|
|
459
|
+
factory = self._provider_factories.get(provider_ref)
|
|
460
|
+
if manifest is None or factory is None:
|
|
461
|
+
raise StudioError(
|
|
462
|
+
"AGENT_PROVIDER_NOT_REGISTERED",
|
|
463
|
+
"AgentProvider 尚未由当前 DSH Profile 完成预检与注册",
|
|
464
|
+
status_code=409,
|
|
465
|
+
details={"provider": provider_ref},
|
|
466
|
+
)
|
|
467
|
+
manifests.append(manifest)
|
|
468
|
+
factories[provider_id] = factory
|
|
469
|
+
|
|
470
|
+
builtin_ids = {
|
|
471
|
+
manifest.metadata.id
|
|
472
|
+
for manifest in (
|
|
473
|
+
*builtin_agent_provider_manifests(),
|
|
474
|
+
*builtin_capability_manifests(),
|
|
475
|
+
)
|
|
476
|
+
}
|
|
477
|
+
allowed = {
|
|
478
|
+
permission
|
|
479
|
+
for manifest in manifests
|
|
480
|
+
if manifest.metadata.id in builtin_ids
|
|
481
|
+
for permission in manifest.spec.permissions
|
|
482
|
+
}
|
|
483
|
+
security = bundle.resolved_agent_spec.get("security")
|
|
484
|
+
if isinstance(security, Mapping):
|
|
485
|
+
raw = security.get("allowedPermissions") or security.get("allowed_permissions") or []
|
|
486
|
+
if isinstance(raw, Sequence) and not isinstance(raw, (str, bytes)):
|
|
487
|
+
allowed.update(str(item) for item in raw)
|
|
488
|
+
return PluginRegistry(manifests), factories, frozenset(allowed)
|
|
489
|
+
|
|
490
|
+
def _preflight_bundle(self, bundle: ResolvedPluginBundle) -> None:
|
|
491
|
+
"""Reject an unusable composition before a Run or Schedule is admitted.
|
|
492
|
+
|
|
493
|
+
Provider activation is intentionally lazy, but permission and graph
|
|
494
|
+
admission are deterministic from the immutable Bundle. Delaying this
|
|
495
|
+
check until ``RuntimeAdapter.start`` lets an accepted Inbox message
|
|
496
|
+
repeatedly create PENDING Runs that can never become runnable.
|
|
497
|
+
"""
|
|
498
|
+
|
|
499
|
+
registry, factories, permissions = self._runtime_components(bundle)
|
|
500
|
+
host = PluginHost(
|
|
501
|
+
registry,
|
|
502
|
+
factories,
|
|
503
|
+
allowed_permissions=permissions,
|
|
504
|
+
)
|
|
505
|
+
try:
|
|
506
|
+
host.preflight(bundle.composition.profile)
|
|
507
|
+
except PluginHostError as error:
|
|
508
|
+
code = (
|
|
509
|
+
"PLUGIN_PERMISSION_DENIED"
|
|
510
|
+
if error.code == "plugin_permission_denied"
|
|
511
|
+
else "PLUGIN_RUNTIME_PREFLIGHT_FAILED"
|
|
512
|
+
)
|
|
513
|
+
raise StudioError(
|
|
514
|
+
code,
|
|
515
|
+
"Agent 插件组合未通过运行前检查",
|
|
516
|
+
status_code=409,
|
|
517
|
+
details={"reason": error.code},
|
|
518
|
+
) from error
|
|
519
|
+
|
|
520
|
+
def _bound_harness_reasoner(self, bundle: ResolvedPluginBundle) -> HarnessReasoner:
|
|
521
|
+
spec = bundle.resolved_agent_spec
|
|
522
|
+
model = ResolvedModel.model_validate(spec.get("model"))
|
|
523
|
+
security = spec.get("security")
|
|
524
|
+
security = security if isinstance(security, Mapping) else {}
|
|
525
|
+
network = NetworkPolicy.model_validate(security.get("network") or {})
|
|
526
|
+
execution = spec.get("execution")
|
|
527
|
+
execution = execution if isinstance(execution, Mapping) else {}
|
|
528
|
+
retry = execution.get("retry")
|
|
529
|
+
retry = retry if isinstance(retry, Mapping) else {}
|
|
530
|
+
return _StudioHarnessReasoner(
|
|
531
|
+
self._model_client,
|
|
532
|
+
model=model,
|
|
533
|
+
network_policy=network,
|
|
534
|
+
timeout_seconds=int(
|
|
535
|
+
execution.get("timeoutSeconds") or execution.get("timeout_seconds") or 120
|
|
536
|
+
),
|
|
537
|
+
max_attempts=int(
|
|
538
|
+
retry.get("maxAttempts") or retry.get("max_attempts") or 2
|
|
539
|
+
),
|
|
540
|
+
backoff_seconds=float(
|
|
541
|
+
retry.get("backoffSeconds") or retry.get("backoff_seconds") or 1
|
|
542
|
+
),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def _external_manifest(self, profile: CompositionProfile) -> PluginManifest | None:
|
|
546
|
+
plugin_id, version = _parse_plugin_ref(profile.agent_provider.ref)
|
|
547
|
+
builtin_ids = {
|
|
548
|
+
manifest.metadata.id for manifest in builtin_agent_provider_manifests()
|
|
549
|
+
}
|
|
550
|
+
if plugin_id in builtin_ids:
|
|
551
|
+
return None
|
|
552
|
+
provider_ref = f"plugin://{plugin_id}@{version}"
|
|
553
|
+
manifest = self._provider_manifests.get(provider_ref)
|
|
554
|
+
if manifest is None:
|
|
555
|
+
raise StudioError(
|
|
556
|
+
"AGENT_PROVIDER_NOT_REGISTERED",
|
|
557
|
+
"AgentProvider 尚未由当前 DSH Profile 完成预检与注册",
|
|
558
|
+
status_code=409,
|
|
559
|
+
details={"provider": provider_ref},
|
|
560
|
+
)
|
|
561
|
+
return manifest
|
|
562
|
+
|
|
563
|
+
def _read_profile(self, bundle_root: Path) -> CompositionProfile:
|
|
564
|
+
try:
|
|
565
|
+
return cast(
|
|
566
|
+
CompositionProfile,
|
|
567
|
+
CompositionProfile.model_validate_json(
|
|
568
|
+
(bundle_root / "composition-profile.json").read_text(
|
|
569
|
+
encoding="utf-8"
|
|
570
|
+
)
|
|
571
|
+
),
|
|
572
|
+
)
|
|
573
|
+
except (OSError, UnicodeError, ValueError) as error:
|
|
574
|
+
raise StudioError(
|
|
575
|
+
"PLUGIN_BUNDLE_INVALID",
|
|
576
|
+
"PluginHost Bundle 缺少有效的 Composition Profile",
|
|
577
|
+
status_code=409,
|
|
578
|
+
) from error
|
|
579
|
+
|
|
580
|
+
def _bundle_root(self, build: BuildRecord) -> Path:
|
|
581
|
+
if not build.artifact_path:
|
|
582
|
+
raise StudioError("BUILD_NOT_READY", "Build 尚未生成制品", status_code=409)
|
|
583
|
+
archive = cast(
|
|
584
|
+
Path,
|
|
585
|
+
self.workspace.resolve(build.artifact_path, must_exist=True),
|
|
586
|
+
)
|
|
587
|
+
bundle_root = archive.parent / "agent-bundle"
|
|
588
|
+
if not bundle_root.is_dir():
|
|
589
|
+
raise StudioError(
|
|
590
|
+
"PLUGIN_BUNDLE_UNAVAILABLE",
|
|
591
|
+
"Build 缺少可运行的 PluginHost Bundle",
|
|
592
|
+
status_code=409,
|
|
593
|
+
)
|
|
594
|
+
return bundle_root
|
|
595
|
+
|
|
596
|
+
@staticmethod
|
|
597
|
+
def _select_model(build: BuildRecord, requested: str | None) -> str:
|
|
598
|
+
allowed = [
|
|
599
|
+
str(item) for item in build.runtime_lock.get("models") or [] if str(item)
|
|
600
|
+
]
|
|
601
|
+
default = str(build.runtime_lock.get("model") or "").strip()
|
|
602
|
+
if default and default not in allowed:
|
|
603
|
+
allowed.insert(0, default)
|
|
604
|
+
selected = str(requested or default).strip()
|
|
605
|
+
if not selected:
|
|
606
|
+
raise StudioError(
|
|
607
|
+
"AGENT_MODEL_REQUIRED", "Build 没有绑定可运行模型", status_code=422
|
|
608
|
+
)
|
|
609
|
+
if allowed and selected not in allowed:
|
|
610
|
+
raise StudioError(
|
|
611
|
+
"MODEL_NOT_BOUND",
|
|
612
|
+
"请求模型未绑定到当前 Agent Build",
|
|
613
|
+
status_code=422,
|
|
614
|
+
details={"model": selected, "allowedModels": allowed},
|
|
615
|
+
)
|
|
616
|
+
return selected
|
|
617
|
+
|
|
618
|
+
def _parse_plugin_ref(value: str) -> tuple[str, str]:
|
|
619
|
+
plugin_id, version = value.removeprefix("plugin://").rsplit("@", 1)
|
|
620
|
+
return plugin_id, version
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def _normalize_result(raw: Any, *, session_id: str) -> StudioPluginTurnResult:
|
|
624
|
+
if isinstance(raw, HarnessTurnResult):
|
|
625
|
+
return StudioPluginTurnResult(
|
|
626
|
+
output_text=raw.output_text,
|
|
627
|
+
session_id=raw.session_id,
|
|
628
|
+
usage=dict(raw.usage),
|
|
629
|
+
metadata=dict(raw.metadata),
|
|
630
|
+
raw=raw,
|
|
631
|
+
)
|
|
632
|
+
if not isinstance(raw, Mapping):
|
|
633
|
+
raise PluginHostError(
|
|
634
|
+
"provider_result_invalid", "AgentProvider result must be an object"
|
|
635
|
+
)
|
|
636
|
+
output_text = str(raw.get("outputText") or raw.get("output_text") or raw.get("output") or "")
|
|
637
|
+
if not output_text:
|
|
638
|
+
raise PluginHostError(
|
|
639
|
+
"provider_result_invalid", "AgentProvider result must contain outputText"
|
|
640
|
+
)
|
|
641
|
+
usage = raw.get("usage") if isinstance(raw.get("usage"), Mapping) else {}
|
|
642
|
+
metadata = raw.get("metadata") if isinstance(raw.get("metadata"), Mapping) else {}
|
|
643
|
+
return StudioPluginTurnResult(
|
|
644
|
+
output_text=output_text,
|
|
645
|
+
session_id=str(raw.get("sessionId") or raw.get("session_id") or session_id),
|
|
646
|
+
usage=dict(usage),
|
|
647
|
+
metadata=dict(metadata),
|
|
648
|
+
raw=raw,
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
__all__ = ["StudioPluginRuntime", "StudioPluginTurnResult"]
|
ksadk/studio/resource_catalog.py
CHANGED
|
@@ -338,7 +338,12 @@ class LocalResourceCatalog:
|
|
|
338
338
|
if not catalog:
|
|
339
339
|
if cached is not None:
|
|
340
340
|
return cached[1], cached[2]
|
|
341
|
-
|
|
341
|
+
# No provider response and no cache: return an empty catalog so the
|
|
342
|
+
# UI can guide the user to configure a provider instead of showing
|
|
343
|
+
# a phantom default model.
|
|
344
|
+
self._provider_models = {}
|
|
345
|
+
self._provider_catalog_cache[cache_key] = (now, [], source)
|
|
346
|
+
return [], source
|
|
342
347
|
|
|
343
348
|
descriptors: list[ResourceDescriptor] = []
|
|
344
349
|
for item in catalog:
|