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
ksadk/plugins/bundle.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Resolve the plugin composition embedded in an AgentBundle v2 directory.
|
|
2
|
+
|
|
3
|
+
``BundleManifest`` remains the single wire contract owned by Studio. This
|
|
4
|
+
module only creates a runtime view after the existing manifest, file digests,
|
|
5
|
+
composition profile, and plugin lock have been checked against one another and
|
|
6
|
+
against the selected :class:`PluginRegistry`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path, PurePosixPath
|
|
15
|
+
from types import MappingProxyType
|
|
16
|
+
from typing import Any, Mapping
|
|
17
|
+
|
|
18
|
+
from pydantic import ValidationError
|
|
19
|
+
|
|
20
|
+
from ksadk.plugins.bundle_security import BundleSecurityError, assert_bundle_security
|
|
21
|
+
from ksadk.plugins.contracts import CompositionProfile, PluginLock
|
|
22
|
+
from ksadk.plugins.resolver import PluginRegistry, PluginResolutionError, ResolvedComposition
|
|
23
|
+
from ksadk.studio.contracts import BundleManifest
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PluginBundleError(ValueError):
|
|
27
|
+
"""Stable rejection while loading the plugin portion of a Bundle v2."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, code: str, message: str) -> None:
|
|
30
|
+
super().__init__(message)
|
|
31
|
+
self.code = code
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class ResolvedPluginBundle:
|
|
36
|
+
"""Validated runtime view of existing AgentBundle v2 artifacts.
|
|
37
|
+
|
|
38
|
+
This deliberately is not another serializable bundle contract. Providers
|
|
39
|
+
receive the already-resolved composition and the immutable resolved Agent
|
|
40
|
+
spec; they do not re-read mutable Studio state.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
root: Path
|
|
44
|
+
manifest: BundleManifest
|
|
45
|
+
resolved_agent_spec: Mapping[str, Any]
|
|
46
|
+
composition: ResolvedComposition
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def bundle_digest(self) -> str:
|
|
50
|
+
return str(self.manifest.bundle_digest)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class PluginBundleResolver:
|
|
54
|
+
"""Load and cross-check the composition artifacts of an AgentBundle v2."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, registry: PluginRegistry) -> None:
|
|
57
|
+
self._registry = registry
|
|
58
|
+
|
|
59
|
+
def resolve(self, root: str | Path) -> ResolvedPluginBundle:
|
|
60
|
+
bundle_root = Path(root).resolve()
|
|
61
|
+
if not bundle_root.is_dir():
|
|
62
|
+
raise PluginBundleError(
|
|
63
|
+
"plugin_bundle_unavailable",
|
|
64
|
+
f"AgentBundle directory does not exist: {bundle_root}",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
manifest_payload = self._read_json(bundle_root, "manifest.json")
|
|
68
|
+
try:
|
|
69
|
+
manifest = BundleManifest.model_validate(manifest_payload)
|
|
70
|
+
except ValidationError as error:
|
|
71
|
+
raise PluginBundleError("plugin_bundle_manifest_invalid", str(error)) from error
|
|
72
|
+
if manifest.bundle_format != "agentkit.bundle/v2":
|
|
73
|
+
raise PluginBundleError(
|
|
74
|
+
"plugin_bundle_version_unsupported",
|
|
75
|
+
"PluginHost execution requires agentkit.bundle/v2",
|
|
76
|
+
)
|
|
77
|
+
if manifest.execution_profile != "composed":
|
|
78
|
+
raise PluginBundleError(
|
|
79
|
+
"plugin_bundle_legacy_execution",
|
|
80
|
+
"Bundle v2 selects legacy execution and cannot enter PluginHost",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
self._verify_manifest_digest(manifest)
|
|
84
|
+
self._verify_declared_files(bundle_root, manifest)
|
|
85
|
+
try:
|
|
86
|
+
assert_bundle_security(bundle_root)
|
|
87
|
+
except BundleSecurityError as error:
|
|
88
|
+
raise PluginBundleError(
|
|
89
|
+
error.code,
|
|
90
|
+
"Plugin Bundle contains literal secret material",
|
|
91
|
+
) from error
|
|
92
|
+
|
|
93
|
+
profile_payload = self._read_json(bundle_root, "composition-profile.json")
|
|
94
|
+
lock_payload = self._read_json(bundle_root, "plugin-lock.json")
|
|
95
|
+
agent_spec = self._read_json(bundle_root, "resolved-agent-spec.json")
|
|
96
|
+
try:
|
|
97
|
+
profile = CompositionProfile.model_validate(profile_payload)
|
|
98
|
+
embedded_lock = PluginLock.model_validate(lock_payload)
|
|
99
|
+
except ValidationError as error:
|
|
100
|
+
raise PluginBundleError("plugin_bundle_composition_invalid", str(error)) from error
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
composition = self._registry.resolve(profile)
|
|
104
|
+
except PluginResolutionError as error:
|
|
105
|
+
raise PluginBundleError(error.code, str(error)) from error
|
|
106
|
+
if composition.profile_digest != manifest.composition_profile_digest:
|
|
107
|
+
raise PluginBundleError(
|
|
108
|
+
"plugin_bundle_profile_digest_mismatch",
|
|
109
|
+
"composition-profile.json does not match manifest compositionProfileDigest",
|
|
110
|
+
)
|
|
111
|
+
if embedded_lock != composition.plugin_lock:
|
|
112
|
+
raise PluginBundleError(
|
|
113
|
+
"plugin_bundle_lock_mismatch",
|
|
114
|
+
"plugin-lock.json does not match deterministic profile resolution",
|
|
115
|
+
)
|
|
116
|
+
if composition.plugin_lock_digest != manifest.plugin_lock_digest:
|
|
117
|
+
raise PluginBundleError(
|
|
118
|
+
"plugin_bundle_lock_digest_mismatch",
|
|
119
|
+
"plugin-lock.json does not match manifest pluginLockDigest",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return ResolvedPluginBundle(
|
|
123
|
+
root=bundle_root,
|
|
124
|
+
manifest=manifest,
|
|
125
|
+
resolved_agent_spec=_deep_freeze(agent_spec),
|
|
126
|
+
composition=composition,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def _read_json(root: Path, relative: str) -> dict[str, Any]:
|
|
131
|
+
path = root / relative
|
|
132
|
+
try:
|
|
133
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
134
|
+
except FileNotFoundError as error:
|
|
135
|
+
raise PluginBundleError(
|
|
136
|
+
"plugin_bundle_file_missing", f"Bundle file is missing: {relative}"
|
|
137
|
+
) from error
|
|
138
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as error:
|
|
139
|
+
raise PluginBundleError(
|
|
140
|
+
"plugin_bundle_file_invalid", f"Bundle file is invalid: {relative}"
|
|
141
|
+
) from error
|
|
142
|
+
if not isinstance(payload, dict):
|
|
143
|
+
raise PluginBundleError(
|
|
144
|
+
"plugin_bundle_file_invalid", f"Bundle file must be an object: {relative}"
|
|
145
|
+
)
|
|
146
|
+
return payload
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def _verify_manifest_digest(manifest: BundleManifest) -> None:
|
|
150
|
+
payload = manifest.model_dump(
|
|
151
|
+
by_alias=True,
|
|
152
|
+
exclude={"bundle_digest"},
|
|
153
|
+
exclude_none=True,
|
|
154
|
+
mode="json",
|
|
155
|
+
)
|
|
156
|
+
actual = _sha256(_canonical_json(payload))
|
|
157
|
+
if manifest.bundle_digest != actual:
|
|
158
|
+
raise PluginBundleError(
|
|
159
|
+
"plugin_bundle_digest_mismatch",
|
|
160
|
+
"manifest.json does not match its declared bundleDigest",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _verify_declared_files(root: Path, manifest: BundleManifest) -> None:
|
|
165
|
+
declared: set[str] = set()
|
|
166
|
+
for entry in manifest.files:
|
|
167
|
+
relative = PurePosixPath(entry.path)
|
|
168
|
+
if relative.is_absolute() or ".." in relative.parts:
|
|
169
|
+
raise PluginBundleError(
|
|
170
|
+
"plugin_bundle_path_invalid",
|
|
171
|
+
f"Bundle manifest contains an unsafe path: {entry.path}",
|
|
172
|
+
)
|
|
173
|
+
path = (root / Path(*relative.parts)).resolve()
|
|
174
|
+
try:
|
|
175
|
+
path.relative_to(root)
|
|
176
|
+
except ValueError as error:
|
|
177
|
+
raise PluginBundleError(
|
|
178
|
+
"plugin_bundle_path_invalid",
|
|
179
|
+
f"Bundle manifest path escapes its root: {entry.path}",
|
|
180
|
+
) from error
|
|
181
|
+
try:
|
|
182
|
+
content = path.read_bytes()
|
|
183
|
+
except OSError as error:
|
|
184
|
+
raise PluginBundleError(
|
|
185
|
+
"plugin_bundle_file_missing",
|
|
186
|
+
f"Declared Bundle file is missing: {entry.path}",
|
|
187
|
+
) from error
|
|
188
|
+
if len(content) != entry.size or _sha256(content) != entry.sha256:
|
|
189
|
+
raise PluginBundleError(
|
|
190
|
+
"plugin_bundle_file_digest_mismatch",
|
|
191
|
+
f"Declared Bundle file failed integrity check: {entry.path}",
|
|
192
|
+
)
|
|
193
|
+
declared.add(entry.path)
|
|
194
|
+
|
|
195
|
+
required = {
|
|
196
|
+
"composition-profile.json",
|
|
197
|
+
"plugin-lock.json",
|
|
198
|
+
"resolved-agent-spec.json",
|
|
199
|
+
}
|
|
200
|
+
missing = sorted(required - declared)
|
|
201
|
+
if missing:
|
|
202
|
+
raise PluginBundleError(
|
|
203
|
+
"plugin_bundle_file_missing",
|
|
204
|
+
"Bundle manifest does not declare required plugin artifacts: " + ", ".join(missing),
|
|
205
|
+
)
|
|
206
|
+
actual = {
|
|
207
|
+
path.relative_to(root).as_posix()
|
|
208
|
+
for path in root.rglob("*")
|
|
209
|
+
if path.is_file() and path.name != "manifest.json"
|
|
210
|
+
}
|
|
211
|
+
undeclared = sorted(actual - declared)
|
|
212
|
+
if undeclared:
|
|
213
|
+
raise PluginBundleError(
|
|
214
|
+
"plugin_bundle_file_undeclared",
|
|
215
|
+
"Bundle contains files outside its integrity manifest: " + ", ".join(undeclared),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _canonical_json(payload: Any) -> bytes:
|
|
220
|
+
return json.dumps(
|
|
221
|
+
payload,
|
|
222
|
+
ensure_ascii=False,
|
|
223
|
+
sort_keys=True,
|
|
224
|
+
separators=(",", ":"),
|
|
225
|
+
).encode("utf-8")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _sha256(payload: bytes) -> str:
|
|
229
|
+
return f"sha256:{hashlib.sha256(payload).hexdigest()}"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _deep_freeze(value: Any) -> Any:
|
|
233
|
+
"""Prevent a Provider from mutating any nested resolved-spec value."""
|
|
234
|
+
|
|
235
|
+
if isinstance(value, dict):
|
|
236
|
+
return MappingProxyType({key: _deep_freeze(child) for key, child in value.items()})
|
|
237
|
+
if isinstance(value, list):
|
|
238
|
+
return tuple(_deep_freeze(child) for child in value)
|
|
239
|
+
return value
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
__all__ = [
|
|
243
|
+
"PluginBundleError",
|
|
244
|
+
"PluginBundleResolver",
|
|
245
|
+
"ResolvedPluginBundle",
|
|
246
|
+
]
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Secret and local-path admission for immutable Agent Bundles.
|
|
2
|
+
|
|
3
|
+
This is deliberately narrower than a generic source-code linter. A Bundle
|
|
4
|
+
may legitimately contain documentation and examples, so we inspect all
|
|
5
|
+
structured deployment inputs and only use high-confidence token signatures in
|
|
6
|
+
runtime source files. The result is suitable for a build/deployment boundary:
|
|
7
|
+
it prevents a resolved Bundle from becoming a durable copy of a credential
|
|
8
|
+
without treating every mention of the word ``token`` as a secret.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
from urllib.parse import urlsplit
|
|
19
|
+
|
|
20
|
+
_SENSITIVE_KEY = re.compile(
|
|
21
|
+
r"(?:^|[_-])(?:password|passwd|api[_-]?key|access[_-]?key|secret[_-]?key|"
|
|
22
|
+
r"access[_-]?token|refresh[_-]?token|credential|private[_-]?key)(?:[_-]?ref)?$",
|
|
23
|
+
re.IGNORECASE,
|
|
24
|
+
)
|
|
25
|
+
_REFERENCE_PREFIXES = ("env://", "secret://", "keyring://", "vault://")
|
|
26
|
+
_HIGH_CONFIDENCE_TOKENS: tuple[tuple[str, re.Pattern[str]], ...] = (
|
|
27
|
+
("private-key", re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----")),
|
|
28
|
+
("aws-access-key", re.compile(r"\b(?:AKIA|ASIA)[A-Z0-9]{16}\b")),
|
|
29
|
+
("openai-style-key", re.compile(r"\bsk-[A-Za-z0-9_-]{20,}\b")),
|
|
30
|
+
("bearer-token", re.compile(r"\bBearer\s+[A-Za-z0-9._~+/=-]{24,}")),
|
|
31
|
+
)
|
|
32
|
+
_STRUCTURED_SUFFIXES = frozenset({".json", ".yaml", ".yml"})
|
|
33
|
+
_RUNTIME_SOURCE_SUFFIXES = frozenset({".py", ".js", ".mjs", ".cjs", ".ts", ".tsx"})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class BundleSecurityFinding:
|
|
38
|
+
"""One deterministic admission finding without leaking the matched value."""
|
|
39
|
+
|
|
40
|
+
path: str
|
|
41
|
+
kind: str
|
|
42
|
+
field: str | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BundleSecurityError(ValueError):
|
|
46
|
+
"""Raised when a Bundle would persist a literal credential or host path."""
|
|
47
|
+
|
|
48
|
+
code = "bundle_secret_detected"
|
|
49
|
+
|
|
50
|
+
def __init__(self, findings: tuple[BundleSecurityFinding, ...]) -> None:
|
|
51
|
+
self.findings = findings
|
|
52
|
+
locations = ", ".join(
|
|
53
|
+
f"{finding.path}{f' ({finding.field})' if finding.field else ''}"
|
|
54
|
+
for finding in findings[:3]
|
|
55
|
+
)
|
|
56
|
+
suffix = " …" if len(findings) > 3 else ""
|
|
57
|
+
super().__init__(f"Agent Bundle contains literal secret material: {locations}{suffix}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def scan_bundle_security(root: str | Path) -> tuple[BundleSecurityFinding, ...]:
|
|
61
|
+
"""Return high-confidence security findings for an already materialized Bundle.
|
|
62
|
+
|
|
63
|
+
Structured files receive semantic key/value inspection. Runtime source is
|
|
64
|
+
only searched for credential signatures; user Markdown and arbitrary
|
|
65
|
+
package assets are intentionally outside this detector to avoid false
|
|
66
|
+
positive build failures.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
bundle_root = Path(root).resolve()
|
|
70
|
+
findings: list[BundleSecurityFinding] = []
|
|
71
|
+
for path in sorted(
|
|
72
|
+
(candidate for candidate in bundle_root.rglob("*") if candidate.is_file()),
|
|
73
|
+
key=lambda candidate: candidate.relative_to(bundle_root).as_posix(),
|
|
74
|
+
):
|
|
75
|
+
relative = path.relative_to(bundle_root).as_posix()
|
|
76
|
+
suffix = path.suffix.lower()
|
|
77
|
+
if suffix not in _STRUCTURED_SUFFIXES and not (
|
|
78
|
+
relative.startswith("runtime/") and suffix in _RUNTIME_SOURCE_SUFFIXES
|
|
79
|
+
):
|
|
80
|
+
continue
|
|
81
|
+
try:
|
|
82
|
+
text = path.read_text(encoding="utf-8")
|
|
83
|
+
except (OSError, UnicodeDecodeError):
|
|
84
|
+
continue
|
|
85
|
+
if suffix == ".json":
|
|
86
|
+
try:
|
|
87
|
+
payload = json.loads(text)
|
|
88
|
+
except json.JSONDecodeError:
|
|
89
|
+
findings.append(BundleSecurityFinding(relative, "invalid-structured-input"))
|
|
90
|
+
continue
|
|
91
|
+
_scan_value(payload, relative, "$", findings)
|
|
92
|
+
elif suffix in {".yaml", ".yml"}:
|
|
93
|
+
# Bundle-owned YAML is a narrow runtime launch declaration. It
|
|
94
|
+
# has no credential-bearing fields today, but token patterns still
|
|
95
|
+
# catch accidental inline credentials without introducing a second
|
|
96
|
+
# YAML parser/security grammar.
|
|
97
|
+
_scan_text(text, relative, findings)
|
|
98
|
+
else:
|
|
99
|
+
_scan_text(text, relative, findings)
|
|
100
|
+
return tuple(findings)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def assert_bundle_security(root: str | Path) -> None:
|
|
104
|
+
findings = scan_bundle_security(root)
|
|
105
|
+
if findings:
|
|
106
|
+
raise BundleSecurityError(findings)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _scan_value(
|
|
110
|
+
value: Any,
|
|
111
|
+
relative: str,
|
|
112
|
+
field: str,
|
|
113
|
+
findings: list[BundleSecurityFinding],
|
|
114
|
+
) -> None:
|
|
115
|
+
if isinstance(value, dict):
|
|
116
|
+
for key, child in value.items():
|
|
117
|
+
key_text = str(key)
|
|
118
|
+
child_field = f"{field}.{key_text}"
|
|
119
|
+
normalized = key_text.replace("_", "").replace("-", "").lower()
|
|
120
|
+
# Metadata that *names* secret fields is not a secret value.
|
|
121
|
+
if normalized in {"secretfields", "credentialfields"}:
|
|
122
|
+
continue
|
|
123
|
+
if _SENSITIVE_KEY.search(key_text) and _has_literal_secret(child):
|
|
124
|
+
findings.append(
|
|
125
|
+
BundleSecurityFinding(relative, "literal-secret-field", child_field)
|
|
126
|
+
)
|
|
127
|
+
_scan_value(child, relative, child_field, findings)
|
|
128
|
+
return
|
|
129
|
+
if isinstance(value, list):
|
|
130
|
+
for index, child in enumerate(value):
|
|
131
|
+
_scan_value(child, relative, f"{field}[{index}]", findings)
|
|
132
|
+
return
|
|
133
|
+
if isinstance(value, str):
|
|
134
|
+
_scan_text(value, relative, findings, field=field)
|
|
135
|
+
split = urlsplit(value)
|
|
136
|
+
# ``plugin://name@version`` is a pinned package reference, not URL
|
|
137
|
+
# userinfo. Only network endpoint schemes can carry credentials here.
|
|
138
|
+
if split.scheme in {"http", "https"} and (
|
|
139
|
+
split.username is not None or split.password is not None
|
|
140
|
+
):
|
|
141
|
+
findings.append(BundleSecurityFinding(relative, "url-credentials", field))
|
|
142
|
+
if value.startswith(("/Users/", "/home/", "C:\\Users\\")):
|
|
143
|
+
findings.append(BundleSecurityFinding(relative, "local-home-path", field))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _has_literal_secret(value: Any) -> bool:
|
|
147
|
+
return isinstance(value, str) and bool(value) and not value.startswith(_REFERENCE_PREFIXES)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _scan_text(
|
|
151
|
+
text: str,
|
|
152
|
+
relative: str,
|
|
153
|
+
findings: list[BundleSecurityFinding],
|
|
154
|
+
*,
|
|
155
|
+
field: str | None = None,
|
|
156
|
+
) -> None:
|
|
157
|
+
for kind, pattern in _HIGH_CONFIDENCE_TOKENS:
|
|
158
|
+
if pattern.search(text):
|
|
159
|
+
findings.append(BundleSecurityFinding(relative, kind, field))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
__all__ = [
|
|
163
|
+
"BundleSecurityError",
|
|
164
|
+
"BundleSecurityFinding",
|
|
165
|
+
"assert_bundle_security",
|
|
166
|
+
"scan_bundle_security",
|
|
167
|
+
]
|