agentengine-sdk-python 0.8.2__py3-none-any.whl → 0.8.4__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.4.dist-info}/METADATA +22 -28
- agentengine_sdk_python-0.8.4.dist-info/RECORD +1070 -0
- ksadk/_build_provenance.json +1 -0
- ksadk/agui/_agent_helpers.py +4 -0
- ksadk/api/client.py +108 -91
- ksadk/builders/code_builder.py +36 -9
- ksadk/builders/framework_requirements.py +2 -1
- ksadk/builders/ks3_uploader.py +36 -7
- ksadk/builders/managed_runtime_builder.py +4 -0
- ksadk/cli/__init__.py +38 -3
- ksadk/cli/cmd_managed_runtime.py +13 -1
- ksadk/cli/cmd_plugin.py +662 -0
- ksadk/cli/cmd_web.py +2 -5
- ksadk/cli/runtime_bootstrap.py +23 -1
- ksadk/codex/client.py +424 -96
- ksadk/codex/projection.py +24 -0
- ksadk/codex/runtime.py +83 -283
- ksadk/common/auth.py +16 -10
- ksadk/configs/env_registry.py +59 -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 +154 -15
- ksadk/conversations/projector.py +614 -0
- ksadk/conversations/reducer.py +98 -0
- ksadk/conversations/runtime_invocation.py +5 -0
- ksadk/conversations/runtime_payloads.py +1 -0
- ksadk/conversations/runtime_preparation.py +30 -27
- ksadk/conversations/runtime_resume.py +50 -2
- ksadk/conversations/runtime_stream_events.py +5 -0
- ksadk/conversations/runtime_streaming.py +25 -7
- ksadk/deployment/env_forward.py +2 -2
- ksadk/deployment/providers/serverless.py +1 -1
- ksadk/events/_v1_compat/projection.py +2 -0
- ksadk/events/adapters/_codex_validators.py +18 -7
- ksadk/events/adapters/codex.py +190 -0
- ksadk/events/adapters/langgraph.py +15 -1
- ksadk/events/canonical_store.py +3 -1
- ksadk/events/projections.py +1 -1
- ksadk/harness/__init__.py +45 -35
- ksadk/harness/config.py +2 -2
- ksadk/harness/runtime.py +75 -16
- ksadk/harness/tools.py +38 -13
- ksadk/interaction/provider.py +1 -0
- ksadk/interaction/providers/langgraph.py +1 -0
- ksadk/kernel/bootstrap.py +28 -4
- ksadk/kernel/contract_fingerprints.py +5 -4
- ksadk/kernel/contracts.py +8 -0
- ksadk/kernel/control.py +1 -0
- ksadk/kernel/ingress.py +36 -2
- ksadk/kernel/memory_store.py +1 -0
- ksadk/kernel/postgres_store.py +1 -0
- ksadk/kernel/sqlite_store.py +28 -0
- ksadk/kernel/store.py +3 -0
- ksadk/kernel/worker.py +76 -10
- ksadk/knowledge_base/client.py +57 -27
- ksadk/mcp_runtime/__init__.py +12 -4
- ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
- ksadk/memory/adk/resilient_session_service.py +25 -17
- ksadk/memory/adk/short_term_memory.py +26 -9
- ksadk/memory/models.py +7 -4
- ksadk/memory/provider_resolver.py +3 -0
- ksadk/memory/providers/local_sqlite.py +7 -0
- ksadk/model_proxy/server.py +10 -5
- ksadk/plugins/__init__.py +171 -0
- ksadk/plugins/artifacts.py +275 -0
- ksadk/plugins/bridges/__init__.py +47 -0
- ksadk/plugins/bridges/codex.py +659 -0
- ksadk/plugins/bridges/dsh.py +1300 -0
- ksadk/plugins/builtins.py +746 -0
- ksadk/plugins/bundle.py +246 -0
- ksadk/plugins/bundle_security.py +191 -0
- ksadk/plugins/codex_manifest.py +983 -0
- ksadk/plugins/composition.py +537 -0
- ksadk/plugins/context_contributor.py +437 -0
- ksadk/plugins/contracts.py +569 -0
- ksadk/plugins/delivery.py +153 -0
- ksadk/plugins/dsh_installation_digest.py +71 -0
- ksadk/plugins/dsh_toolchain.py +1024 -0
- ksadk/plugins/ecosystem_bridge.py +1105 -0
- ksadk/plugins/ecosystem_probe.py +191 -0
- ksadk/plugins/host.py +787 -0
- ksadk/plugins/kernel_adapter.py +205 -0
- ksadk/plugins/providers/__init__.py +108 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
- ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -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-dsh-capability-host/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +870 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +92 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +15 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -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 +988 -0
- ksadk/plugins/providers/codex_dsh.py +83 -0
- ksadk/plugins/providers/codex_native.py +368 -0
- ksadk/plugins/providers/codex_turn.py +301 -0
- ksadk/plugins/providers/dsh.py +951 -0
- ksadk/plugins/providers/dsh_capabilities.py +1389 -0
- ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
- ksadk/plugins/providers/dsh_mcp.py +467 -0
- ksadk/plugins/providers/harness.py +632 -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/mcp_projection.py +150 -0
- ksadk/plugins/providers/platform_resources.py +657 -0
- ksadk/plugins/providers/shipped_dsh.py +370 -0
- ksadk/plugins/resolver.py +338 -0
- ksadk/plugins/subagent_providers/__init__.py +12 -0
- ksadk/plugins/subagent_providers/codex.py +443 -0
- ksadk/plugins/subagents.py +333 -0
- ksadk/resource_runtime/__init__.py +4 -0
- ksadk/resource_runtime/broker.py +616 -0
- ksadk/resource_runtime/build_artifacts.py +165 -0
- ksadk/resource_runtime/contracts.py +138 -0
- ksadk/resource_runtime/discovery_receipts.py +100 -0
- ksadk/resource_runtime/discovery_skills.py +172 -0
- ksadk/resource_runtime/errors.py +6 -0
- ksadk/resource_runtime/ipc.py +142 -0
- ksadk/resource_runtime/kernel_approval.py +165 -0
- ksadk/resource_runtime/knowledge.py +114 -0
- ksadk/resource_runtime/langgraph.py +94 -0
- ksadk/resource_runtime/leases.py +191 -0
- ksadk/resource_runtime/managed_projection.py +186 -0
- ksadk/resource_runtime/mcp_server.py +138 -0
- ksadk/resource_runtime/memory_provider.py +318 -0
- ksadk/resource_runtime/memory_receipts.py +34 -0
- ksadk/resource_runtime/operation_ledger.py +211 -0
- ksadk/resource_runtime/plugin_config.py +48 -0
- ksadk/resource_runtime/policy_authorization.py +54 -0
- ksadk/resource_runtime/process.py +110 -0
- ksadk/resource_runtime/skill_artifacts.py +153 -0
- ksadk/resource_runtime/skill_runtime.py +42 -0
- ksadk/resource_runtime/skill_workspace.py +118 -0
- ksadk/resource_runtime/skills.py +215 -0
- ksadk/resource_runtime/snapshots.py +180 -0
- ksadk/resource_runtime/socket_server.py +103 -0
- ksadk/resource_runtime/supervisor.py +380 -0
- ksadk/resource_runtime/worker.py +504 -0
- ksadk/runners/_langgraph_runner_streams.py +74 -6
- ksadk/runners/adk_runner.py +149 -11
- ksadk/runners/base_runner.py +29 -1
- ksadk/runners/langgraph_runner.py +475 -145
- ksadk/runtime/_runner_adapter/stream_mapping.py +193 -60
- ksadk/runtime/adapter.py +3 -0
- ksadk/runtime/conversation_execution.py +92 -16
- ksadk/runtime/executor.py +78 -2
- ksadk/runtime/factory.py +30 -200
- ksadk/runtime/framework_adapters.py +41 -0
- ksadk/runtime/preprocessing.py +10 -2
- ksadk/runtime/runner_adapter.py +78 -14
- ksadk/runtime_context.py +40 -2
- ksadk/sandbox/__init__.py +2 -1
- ksadk/sandbox/backends/e2b.py +52 -11
- ksadk/sandbox/backends/local_process.py +9 -0
- ksadk/sandbox/base.py +2 -0
- ksadk/sandbox/e2b_connection.py +66 -0
- 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 +581 -0
- ksadk/server/composition.py +2 -0
- ksadk/server/factory.py +5 -0
- ksadk/server/persistence_capability.py +516 -0
- ksadk/server/routes/control.py +210 -46
- ksadk/server/routes/dependencies.py +15 -0
- ksadk/server/routes/models.py +47 -4
- ksadk/server/routes/projection.py +252 -12
- ksadk/server/routes/sessions.py +284 -107
- ksadk/server/routes/streaming.py +43 -9
- ksadk/server/static/assets/{ArtifactsPanel-DSU1sWD_.js → ArtifactsPanel-CGbWlMNh.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-DOH6MVcn.js → CodeBlock-b4C7upmO.js} +3 -3
- ksadk/server/static/assets/{MathMessageMarkdown-BL8my1R2.js → MathMessageMarkdown-DbdW5A4_.js} +4 -4
- ksadk/server/static/assets/{MermaidBlock-Dz4IP-Tx.js → MermaidBlock-BEBN4Ksz.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-DUrK0JpZ.js → NativeTerminalPanel-zvL-WpAn.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-g20pFzNV.js → abnfDiagram-VCTEODGH-BYafW7Za.js} +1 -1
- ksadk/server/static/assets/{arc-C4FzinUA.js → arc-bTohInsS.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-BW5UtSnx.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-DpIqL5h4.js → architectureDiagram-5GKGNRK7-Ch5Lbzsi.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-BjkljTNz.js → blockDiagram-NRAW4CY4-kqF4MDOG.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-Dk_ieq2X.js → c4Diagram-UCG6FXSJ-8uSRIODv.js} +1 -1
- ksadk/server/static/assets/channel-CF5Up3hQ.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-KkXX47WI.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-BVHAchFb.js → chunk-5VM5RSS4-DkhehZDr.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-C-ipQzuS.js → chunk-F27PBJKO-BdXhtPe9.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-C2EyJbIK.js → chunk-G27WJ6UU-DPTpvpwi.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-vYvVJb_M.js → chunk-JWPE2WC7-Pqoh38el.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-BcI6ysPT.js → chunk-LCL6LL3I-DeU00qC-.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-C030x_Z1.js → chunk-POPQ4Y6H-CzxScFkr.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-yyGpsz4n.js → chunk-RHFEMEQ7-DpqMx-ux.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-BLTlmMU7.js → chunk-SVP7TREG-C6t0SypQ.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-C_32ArgP.js → chunk-XXDRQBXY-DVaeafZk.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Cz81acRQ.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Cz81acRQ.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-9YQYwdl0.js → cose-bilkent-JH36ORCC-Bf9D3tzJ.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-DVWs8a2t.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-ErLF5N13.js → cynefinDiagram-5FMLGOSQ-B9cY3iTz.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-DFiQF-6f.js → dagre-3AP2YEHR-CSwlx3CB.js} +1 -1
- ksadk/server/static/assets/{dagre-BuhGnRI1.js → dagre-UBpgon5q.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-Bt1v8-GC.js → diagram-S7CK7UJ4-C9akhN9a.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-DSCxJdBK.js → diagram-UQ7AKVKN-CgedhTPI.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-DHfYwp_9.js → diagram-VSXAHHWV-Cjucz5BY.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-DdzD-4Le.js → diagram-VX7I27RA-Dd4Vleyn.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-Du-nAJ9F.js → diagram-Z3DM3KII-CZ2jGtYW.js} +1 -1
- ksadk/server/static/assets/{dist-B7seoj3d.js → dist-B2_CVgKv.js} +1 -1
- ksadk/server/static/assets/{dist-CSh_DE14.js → dist-B3FR1SKK.js} +1 -1
- ksadk/server/static/assets/{dist-DUX-id_F.js → dist-BIr4DSRq.js} +1 -1
- ksadk/server/static/assets/{dist-C_wsv-Qd.js → dist-BgV_owcB.js} +1 -1
- ksadk/server/static/assets/{dist-W-TIVntx.js → dist-Bj730Ib4.js} +1 -1
- ksadk/server/static/assets/{dist-BytlxIDT.js → dist-BnaTiyzN.js} +1 -1
- ksadk/server/static/assets/{dist-CXYYBw3_.js → dist-C1NMAY5a.js} +1 -1
- ksadk/server/static/assets/{dist-dDdADKFA.js → dist-C5_ZAZsG.js} +1 -1
- ksadk/server/static/assets/{dist-BVDffZ0U.js → dist-CB9iJrvg.js} +1 -1
- ksadk/server/static/assets/{dist-Bf2M8m3N.js → dist-CFzsn6BG.js} +1 -1
- ksadk/server/static/assets/{dist-ngtN0DJB.js → dist-Cf125P_m.js} +1 -1
- ksadk/server/static/assets/{dist-BE7bu-DL.js → dist-ClYUxfeK.js} +1 -1
- ksadk/server/static/assets/{dist-DNp6rLRA.js → dist-DFD6Qa6T.js} +1 -1
- ksadk/server/static/assets/{dist-BX8z72IC.js → dist-DVCOWtha.js} +1 -1
- ksadk/server/static/assets/{dist-dydu68Fl.js → dist-D_ctSS2i.js} +1 -1
- ksadk/server/static/assets/{dist-ClxGviKw.js → dist-DiixqL0J.js} +1 -1
- ksadk/server/static/assets/{dist-BjU6y-A2.js → dist-DoVSwoDb.js} +1 -1
- ksadk/server/static/assets/{dist-Dq9gLD7L.js → dist-DvFK7p5S.js} +1 -1
- ksadk/server/static/assets/{dist-8hqcx0sU.js → dist-QfkEHOlb.js} +1 -1
- ksadk/server/static/assets/{dist-CttYUqzS.js → dist-btbmmX5O.js} +1 -1
- ksadk/server/static/assets/{dist-DmldrHd_.js → dist-fBO2v5Kt.js} +1 -1
- ksadk/server/static/assets/{dist-DnfXs8Vn.js → dist-ufY3F5p6.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-Da9C9NO1.js → ebnfDiagram-PWID7BFC-Ci8NqqZu.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-EAGqqR79.js → erDiagram-SSCWMZ5O-CCIOhLZG.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-CQqR2hg3.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-DDRRbGPm.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DqJsKb59.js → ganttDiagram-EL5Y4UJY-Cp_r9Hb6.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-fgWEf7UD.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-B6g4dtDi.js → gitGraphDiagram-WWUBYQGX-Dlhc8Wr8.js} +1 -1
- ksadk/server/static/assets/index-DcTML1Up.css +2 -0
- ksadk/server/static/assets/index-Dh2XHqPK.js +244 -0
- ksadk/server/static/assets/info-A6RAGUB7-BUecLTdt.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-Djm_nbd5.js → infoDiagram-RXCK75RN-CO0rRTRA.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-DWya9SG2.js → ishikawaDiagram-5VMMS53U-CHiaedSS.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DutxhSNI.js → journeyDiagram-EYS64GPL-CHcCIB09.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-DJdOF8Fm.js → kanban-definition-3QL26DDD-CJ_ebBZf.js} +1 -1
- ksadk/server/static/assets/{katex-vFWytM5c.js → katex-B4weu383.js} +1 -1
- ksadk/server/static/assets/{linear-BHjG8b-J.js → linear-V-MgYHW1.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-KGSy4jWT.js → mermaid-parser.core-CBijEX_c.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-9-HC88D4.js → mindmap-definition-FBJOCRG2-Bc-pQvpp.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BeW-btFv.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-DrEZd4fD.js → pegDiagram-XKGWAZYB-D7genGLl.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-X0xh4SaT.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-DpjqOFFp.js → pieDiagram-E7YTZNPT-Bxj32zwD.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-BiNhgOXX.js → quadrantDiagram-AXDQQJYC-BURv2mGh.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-DzRMSqjd.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-Cc44icX-.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-D6S4BdV-.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-vD3aVE2o.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-CYbW_b4T.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-_Y3Ojg6G.js → railroadDiagram-O6MQD6OU-CCsh3avW.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-DeCFdg9K.js → requirementDiagram-EFPCY7ZU-_FRRBKEd.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-CPH75rhw.js → sankeyDiagram-P5KCCOFB-C-gr2o9M.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-OEpQQdr4.js → sequenceDiagram-WJ2MYXX4-D97NmfKg.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-heUErzhI.js → sizeCapture-X5ZJPWSS-DjrPC5FX.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-DvA3jSMB.js → stateDiagram-HBIQ2CUA-DzMNg1hh.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BIcjKFhF.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-CR-dfN_t.js → swimlanes-XN3QIQJK-Cssf_CsU.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-tq3K3Ybl.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-DAVJf1TX.js → timeline-definition-24CTP7MA-BWTHzyS1.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-DBXt3GdU.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-DrCy4E_l.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-DwM8eDkh.js → vennDiagram-4TSXK5OY-BSKgJsmR.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-CefhOliM.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DZL-Zz2t.js → wardleyDiagram-VM6X3IG4-eflHNoaZ.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js → xychartDiagram-S5SC5T6Z-eYqrQ-v4.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/session_context.py +66 -0
- ksadk/sessions/__init__.py +11 -21
- ksadk/sessions/_local_service_sync.py +263 -1
- ksadk/sessions/base.py +176 -0
- ksadk/sessions/errors.py +4 -0
- ksadk/sessions/in_memory.py +309 -19
- ksadk/sessions/local_service.py +83 -1
- ksadk/sessions/persistence.py +275 -0
- ksadk/sessions/postgres_service.py +358 -0
- ksadk/sessions/resilience.py +8 -0
- ksadk/sessions/resilient.py +362 -4
- ksadk/sessions/topology.py +135 -0
- ksadk/skills/models.py +26 -4
- ksadk/skills/package_store.py +186 -38
- ksadk/skills/runtime/agent.py +31 -6
- ksadk/skills/runtime/artifact_delivery.py +139 -0
- ksadk/skills/runtime/backends/e2b.py +98 -6
- ksadk/skills/runtime/backends/local.py +73 -8
- ksadk/skills/runtime/base.py +3 -0
- ksadk/skills/runtime/executor.py +2 -0
- ksadk/skills/runtime/loader.py +10 -2
- ksadk/skills/runtime/pinned.py +114 -0
- ksadk/skills/runtime/request.py +31 -0
- ksadk/skills/service_client.py +211 -24
- ksadk/studio/agent_lifecycle.py +26 -20
- ksadk/studio/api.py +714 -90
- ksadk/studio/api_catalog_routes.py +23 -0
- ksadk/studio/api_contracts.py +34 -1
- ksadk/studio/api_plugin_routes.py +911 -0
- ksadk/studio/api_resource_connections.py +183 -0
- ksadk/studio/attachment_store.py +187 -0
- ksadk/studio/authoring.py +10 -4
- ksadk/studio/authoring_coordinator.py +31 -16
- ksadk/studio/builder.py +199 -43
- ksadk/studio/cloud.py +346 -104
- ksadk/studio/cloud_shared_web.py +565 -0
- ksadk/studio/codex_agent_service.py +140 -27
- ksadk/studio/codex_builder.py +294 -16
- ksadk/studio/codex_credentials.py +90 -0
- ksadk/studio/codex_manifest.py +51 -4
- ksadk/studio/codex_plugin_store.py +746 -0
- ksadk/studio/codex_provider_build.py +231 -0
- ksadk/studio/codex_run.py +200 -29
- ksadk/studio/compatibility_report.py +456 -0
- ksadk/studio/compiler.py +84 -15
- ksadk/studio/contracts.py +249 -4
- ksadk/studio/dsh_application.py +137 -0
- ksadk/studio/dsh_capability_service.py +1107 -0
- ksadk/studio/dsh_models.py +59 -0
- ksadk/studio/dsh_provider_registration.py +1075 -0
- ksadk/studio/event_store.py +54 -6
- ksadk/studio/framework_run.py +37 -4
- ksadk/studio/hosted_kernel.py +89 -14
- ksadk/studio/mcp_runtime.py +11 -3
- ksadk/studio/model_client.py +2 -2
- ksadk/studio/operations.py +12 -2
- ksadk/studio/plugin_composition.py +468 -0
- ksadk/studio/plugin_kernel_adapter.py +28 -0
- ksadk/studio/plugin_runtime.py +831 -0
- ksadk/studio/resource_authority.py +637 -0
- ksadk/studio/resource_binding_validation.py +77 -0
- ksadk/studio/resource_build_admission.py +196 -0
- ksadk/studio/resource_build_materializer.py +149 -0
- ksadk/studio/resource_catalog.py +149 -3
- ksadk/studio/resource_connections.py +220 -0
- ksadk/studio/run_service.py +1144 -96
- ksadk/studio/runtime_source.py +24 -8
- ksadk/studio/scheduler_assistant.py +317 -0
- ksadk/studio/scheduler_runtime.py +383 -0
- ksadk/studio/scheduler_service.py +214 -0
- ksadk/studio/service.py +1131 -95
- ksadk/studio/shared_web.py +610 -87
- ksadk/studio/soul.py +52 -0
- ksadk/studio/static/assets/CodeBlock-DCUUY0PL-BLjA7kbS.js +41 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--BEOyPC3w.css +1 -0
- ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--CUYIXwgI.js +8 -0
- ksadk/studio/static/assets/MermaidBlock-DBFTztfQ-BTYKWEbD.js +44 -0
- ksadk/studio/static/assets/PrismRenderer-wiXRHURw.js +3 -0
- ksadk/studio/static/assets/abnfDiagram-VCTEODGH-CLETGjKq.js +1 -0
- ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
- ksadk/studio/static/assets/architecture-7GRP2DOG-hXMiO_AU.js +1 -0
- ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-B5EQysk6.js +36 -0
- ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
- ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CFmPl1de.js +129 -0
- ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BRrGu8qt.js +38 -0
- ksadk/studio/static/assets/channel-BqO-gtEw.js +1 -0
- ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- ksadk/studio/static/assets/chunk-4HAMMTFA-D220vUtq.js +62 -0
- ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- ksadk/studio/static/assets/chunk-75Z2AOVW-C8lLv_qc.js +2 -0
- ksadk/studio/static/assets/chunk-DU6HZSFF-C4DtVpCu.js +128 -0
- ksadk/studio/static/assets/chunk-F27PBJKO-Cpw14SNG.js +1 -0
- ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
- ksadk/studio/static/assets/chunk-GMAD6QVW-DiQA-u2N.js +72 -0
- ksadk/studio/static/assets/chunk-GVQU2GXP-DUdNBUCi.js +1 -0
- ksadk/studio/static/assets/chunk-IMKFNOWR-BSlbjT_b.js +231 -0
- ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- ksadk/studio/static/assets/chunk-L3NEJ4N5-Db8Ab22W.js +1 -0
- ksadk/studio/static/assets/chunk-OSK3NFVY-M2m6G3j4.js +10 -0
- ksadk/studio/static/assets/chunk-P2QGCYS3-DezaQxiK.js +1 -0
- ksadk/studio/static/assets/chunk-POPQ4Y6H-DOpufJ3t.js +1 -0
- ksadk/studio/static/assets/chunk-PWAF6VOD-CfLb1gSc.js +1 -0
- ksadk/studio/static/assets/chunk-SHT3W25Y-DAyIVp5H.js +168 -0
- ksadk/studio/static/assets/chunk-SVP7TREG-DulomOrS.js +88 -0
- ksadk/studio/static/assets/chunk-TICWLB2K-bHJYoflj.js +206 -0
- ksadk/studio/static/assets/chunk-XXDRQBXY-7PcdR2B1.js +1 -0
- ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- ksadk/studio/static/assets/classDiagram-ZZMXUADV-D_bW8JjA.js +1 -0
- ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-D_bW8JjA.js +1 -0
- ksadk/studio/static/assets/cose-bilkent-JH36ORCC-snL88wkX.js +1 -0
- ksadk/studio/static/assets/cynefin-OW5HDTMX-Bd1YhRo1.js +1 -0
- ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-BBrIG_mI.js +62 -0
- ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
- ksadk/studio/static/assets/dagre-GXQ25YYZ-DtF1nGdY.js +4 -0
- ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
- ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
- ksadk/studio/static/assets/diagram-S7CK7UJ4-0BZ98mya.js +30 -0
- ksadk/studio/static/assets/diagram-UQ7AKVKN-6mLKEo6L.js +41 -0
- ksadk/studio/static/assets/diagram-VSXAHHWV-CJBIaiZ2.js +3 -0
- ksadk/studio/static/assets/diagram-VX7I27RA-BohLEE1C.js +24 -0
- ksadk/studio/static/assets/diagram-Z3DM3KII-D8KIGonG.js +24 -0
- ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
- ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-C99HU61t.js +1 -0
- ksadk/studio/static/assets/erDiagram-RLTQ6QDP-DLGry2Ap.js +99 -0
- ksadk/studio/static/assets/eventmodeling-NTZA5JFV-B1_5Ac8j.js +1 -0
- ksadk/studio/static/assets/flowDiagram-HODETNUW-DD5hmicn.js +1 -0
- ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BPJrVLSG.js +292 -0
- ksadk/studio/static/assets/gitGraph-4MIJSDKK-Cy_ZQBGi.js +1 -0
- ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-CZCUz4MT.js +106 -0
- ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
- ksadk/studio/static/assets/index-CeHL9ARV.css +2 -0
- ksadk/studio/static/assets/index-D9QEcVpL.js +320 -0
- ksadk/studio/static/assets/info-A6RAGUB7-DBzDQB_I.js +1 -0
- ksadk/studio/static/assets/infoDiagram-27XIBGKW-B-sq07Hi.js +2 -0
- ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
- ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-CljI2wyf.js +70 -0
- ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-D50Fo2sL.js +139 -0
- ksadk/studio/static/assets/kanban-definition-UXKFOSKX-DimfOTam.js +89 -0
- ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
- ksadk/studio/static/assets/line-CX5BOOUP.js +1 -0
- ksadk/studio/static/assets/linear-BCxN9JLv.js +1 -0
- ksadk/studio/static/assets/mermaid-parser.core-BdCz8fkS.js +7 -0
- ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-Bizzqy5R.js +96 -0
- ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
- ksadk/studio/static/assets/packet-AYTQ26CC-gPln-_kG.js +1 -0
- ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
- ksadk/studio/static/assets/pegDiagram-XKGWAZYB-Dy3fB-gW.js +1 -0
- ksadk/studio/static/assets/pie-WAS4IAKB-qZdHMr7B.js +1 -0
- ksadk/studio/static/assets/pieDiagram-E7YTZNPT-B9mrFSDF.js +39 -0
- ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-NzUfCbMR.js +7 -0
- ksadk/studio/static/assets/radar-RG4KPBEZ-BZlnaea9.js +1 -0
- ksadk/studio/static/assets/railroad-74A4TZTK-Ce3__H9c.js +1 -0
- ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-DqQn7EzD.js +1 -0
- ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-B55kCA8F.js +1 -0
- ksadk/studio/static/assets/railroad-peg-WCYAUIDC-DfQnvkHd.js +1 -0
- ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-DnhMjZCk.js +1 -0
- ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-BF3d103U.js +84 -0
- ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
- ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
- ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-Bo-JTwt0.js +40 -0
- ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-Dh4wZtUm.js +162 -0
- ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- ksadk/studio/static/assets/src-SFn9St9H.js +1 -0
- ksadk/studio/static/assets/stateDiagram-D77RDMKH-DH774G6d.js +1 -0
- ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-DZvVWPBo.js +1 -0
- ksadk/studio/static/assets/swimlanes-42K2YHIH-Bl13OvTX.js +1 -0
- ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-BnFOTALJ.js +8 -0
- ksadk/studio/static/assets/timeline-definition-24CTP7MA-DxmSSjRB.js +120 -0
- ksadk/studio/static/assets/treeView-Q6P3EWNA-D5Nn9Swz.js +1 -0
- ksadk/studio/static/assets/treemap-WGGIJYW6-CgHgnQhE.js +1 -0
- ksadk/studio/static/assets/vennDiagram-4TSXK5OY-B9QEXcJq.js +34 -0
- ksadk/studio/static/assets/wardley-WFR3VGLG-DXrK2RRY.js +1 -0
- ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-Ba1AOWoe.js +78 -0
- ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-Cr5pt5Y-.js +7 -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
- agentengine_sdk_python-0.8.2.dist-info/RECORD +0 -778
- 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/PrismRenderer-IYN-ffww.js +0 -3
- 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.4.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
"""Parse and snapshot the file-based data plane of a Codex plugin.
|
|
2
|
+
|
|
3
|
+
Codex App Server remains the lifecycle authority for marketplaces and plugin
|
|
4
|
+
installation. This module starts at the installed plugin root: it validates
|
|
5
|
+
the official ``.codex-plugin/plugin.json`` contract, resolves only contained
|
|
6
|
+
component paths, and records deterministic content digests for admission into
|
|
7
|
+
KsADK's immutable build contracts.
|
|
8
|
+
|
|
9
|
+
The upstream manifest is allowed to grow new fields. Fields understood here
|
|
10
|
+
remain strictly typed, and spelling aliases are normalized only for the
|
|
11
|
+
documented camelCase form and KsADK's legacy snake_case form. Supplying both
|
|
12
|
+
spellings is rejected instead of silently choosing one.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import re
|
|
20
|
+
import stat
|
|
21
|
+
from collections.abc import Iterable, Mapping
|
|
22
|
+
from pathlib import Path, PurePosixPath
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
24
|
+
from urllib.parse import urlsplit
|
|
25
|
+
|
|
26
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from ksadk.plugins.contracts import LockedPluginComponent, PluginSourceSnapshot
|
|
30
|
+
|
|
31
|
+
_MANIFEST_RELATIVE_PATH = PurePosixPath(".codex-plugin/plugin.json")
|
|
32
|
+
_MAX_JSON_BYTES = 4 * 1024 * 1024
|
|
33
|
+
_MAX_ARTIFACT_BYTES = 256 * 1024 * 1024
|
|
34
|
+
_MAX_ARTIFACT_FILES = 20_000
|
|
35
|
+
_SEMVER = re.compile(
|
|
36
|
+
r"^(0|[1-9]\d*)\."
|
|
37
|
+
r"(0|[1-9]\d*)\."
|
|
38
|
+
r"(0|[1-9]\d*)"
|
|
39
|
+
r"(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\."
|
|
40
|
+
r"(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?"
|
|
41
|
+
r"(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$"
|
|
42
|
+
)
|
|
43
|
+
_PLUGIN_NAME = re.compile(r"^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$")
|
|
44
|
+
_DIGEST = re.compile(r"^sha256:[0-9a-f]{64}$")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CodexPluginManifestError(ValueError):
|
|
48
|
+
"""The installed Codex plugin data plane is malformed or unsafe."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _to_camel(value: str) -> str:
|
|
52
|
+
head, *tail = value.split("_")
|
|
53
|
+
return head + "".join(part.capitalize() for part in tail)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _CodexSnapshotModel(BaseModel):
|
|
57
|
+
model_config = ConfigDict(
|
|
58
|
+
alias_generator=_to_camel,
|
|
59
|
+
populate_by_name=True,
|
|
60
|
+
extra="forbid",
|
|
61
|
+
frozen=True,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _CodexUpstreamModel(_CodexSnapshotModel):
|
|
66
|
+
# Native manifests and companion objects are versioned by Codex, not by
|
|
67
|
+
# KsADK. Preserve additive fields while validating every field we consume.
|
|
68
|
+
model_config = ConfigDict(
|
|
69
|
+
alias_generator=_to_camel,
|
|
70
|
+
populate_by_name=True,
|
|
71
|
+
extra="allow",
|
|
72
|
+
frozen=True,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _normalize_aliases(
|
|
77
|
+
value: Any,
|
|
78
|
+
aliases: Mapping[str, str],
|
|
79
|
+
*,
|
|
80
|
+
label: str,
|
|
81
|
+
) -> Any:
|
|
82
|
+
if not isinstance(value, Mapping):
|
|
83
|
+
return value
|
|
84
|
+
normalized = dict(value)
|
|
85
|
+
for snake_name, camel_name in aliases.items():
|
|
86
|
+
if snake_name in normalized and camel_name in normalized:
|
|
87
|
+
raise ValueError(f"{label} cannot contain both {camel_name!r} and {snake_name!r}")
|
|
88
|
+
# Pydantic accepts the declared alias and field name. Leave the
|
|
89
|
+
# caller's spelling intact so validation errors point at the source
|
|
90
|
+
# document rather than at a rewritten key.
|
|
91
|
+
return normalized
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _non_empty(value: str, *, field: str) -> str:
|
|
95
|
+
if not value or value != value.strip() or any(ord(character) < 32 for character in value):
|
|
96
|
+
raise ValueError(f"{field} must be trimmed, non-empty printable text")
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _relative_reference(
|
|
101
|
+
value: str,
|
|
102
|
+
*,
|
|
103
|
+
field: str,
|
|
104
|
+
require_dot_prefix: bool = False,
|
|
105
|
+
) -> str:
|
|
106
|
+
_non_empty(value, field=field)
|
|
107
|
+
if "\\" in value or "\x00" in value:
|
|
108
|
+
raise ValueError(f"{field} must use a relative POSIX path")
|
|
109
|
+
if require_dot_prefix and not value.startswith("./"):
|
|
110
|
+
raise ValueError(f"{field} must start with './' relative to the plugin root")
|
|
111
|
+
path = PurePosixPath(value)
|
|
112
|
+
if path.is_absolute() or not path.parts or any(part == ".." for part in path.parts):
|
|
113
|
+
raise ValueError(f"{field} must stay relative to the plugin root")
|
|
114
|
+
normalized = path.as_posix()
|
|
115
|
+
if normalized in {"", "."}:
|
|
116
|
+
raise ValueError(f"{field} must name a component inside the plugin root")
|
|
117
|
+
return value
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class CodexPluginAuthor(_CodexUpstreamModel):
|
|
121
|
+
name: str
|
|
122
|
+
email: str | None = None
|
|
123
|
+
url: str | None = None
|
|
124
|
+
|
|
125
|
+
@field_validator("name")
|
|
126
|
+
@classmethod
|
|
127
|
+
def validate_name(cls, value: str) -> str:
|
|
128
|
+
return _non_empty(value, field="author.name")
|
|
129
|
+
|
|
130
|
+
@field_validator("email", "url")
|
|
131
|
+
@classmethod
|
|
132
|
+
def validate_optional_text(cls, value: str | None) -> str | None:
|
|
133
|
+
return None if value is None else _non_empty(value, field="author metadata")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class CodexPluginInterface(_CodexUpstreamModel):
|
|
137
|
+
display_name: str | None = Field(default=None, alias="displayName")
|
|
138
|
+
short_description: str | None = Field(default=None, alias="shortDescription")
|
|
139
|
+
long_description: str | None = Field(default=None, alias="longDescription")
|
|
140
|
+
developer_name: str | None = Field(default=None, alias="developerName")
|
|
141
|
+
category: str | None = None
|
|
142
|
+
capabilities: tuple[str, ...] = ()
|
|
143
|
+
website_url: str | None = Field(default=None, alias="websiteURL")
|
|
144
|
+
privacy_policy_url: str | None = Field(default=None, alias="privacyPolicyURL")
|
|
145
|
+
terms_of_service_url: str | None = Field(default=None, alias="termsOfServiceURL")
|
|
146
|
+
default_prompt: str | tuple[str, ...] | None = Field(default=None, alias="defaultPrompt")
|
|
147
|
+
brand_color: str | None = Field(default=None, alias="brandColor")
|
|
148
|
+
composer_icon: str | None = Field(default=None, alias="composerIcon")
|
|
149
|
+
logo: str | None = None
|
|
150
|
+
logo_dark: str | None = Field(default=None, alias="logoDark")
|
|
151
|
+
screenshots: tuple[str, ...] = ()
|
|
152
|
+
|
|
153
|
+
@model_validator(mode="before")
|
|
154
|
+
@classmethod
|
|
155
|
+
def normalize_spelling(cls, value: Any) -> Any:
|
|
156
|
+
return _normalize_aliases(
|
|
157
|
+
value,
|
|
158
|
+
{
|
|
159
|
+
"display_name": "displayName",
|
|
160
|
+
"short_description": "shortDescription",
|
|
161
|
+
"long_description": "longDescription",
|
|
162
|
+
"developer_name": "developerName",
|
|
163
|
+
"website_url": "websiteURL",
|
|
164
|
+
"privacy_policy_url": "privacyPolicyURL",
|
|
165
|
+
"terms_of_service_url": "termsOfServiceURL",
|
|
166
|
+
"default_prompt": "defaultPrompt",
|
|
167
|
+
"brand_color": "brandColor",
|
|
168
|
+
"composer_icon": "composerIcon",
|
|
169
|
+
"logo_dark": "logoDark",
|
|
170
|
+
},
|
|
171
|
+
label="plugin.json interface",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@field_validator(
|
|
175
|
+
"display_name",
|
|
176
|
+
"short_description",
|
|
177
|
+
"long_description",
|
|
178
|
+
"developer_name",
|
|
179
|
+
"category",
|
|
180
|
+
"website_url",
|
|
181
|
+
"privacy_policy_url",
|
|
182
|
+
"terms_of_service_url",
|
|
183
|
+
"brand_color",
|
|
184
|
+
)
|
|
185
|
+
@classmethod
|
|
186
|
+
def validate_optional_text(cls, value: str | None) -> str | None:
|
|
187
|
+
return None if value is None else _non_empty(value, field="interface field")
|
|
188
|
+
|
|
189
|
+
@field_validator("capabilities", "screenshots")
|
|
190
|
+
@classmethod
|
|
191
|
+
def validate_text_tuple(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
192
|
+
for item in value:
|
|
193
|
+
_non_empty(item, field="interface list entry")
|
|
194
|
+
return value
|
|
195
|
+
|
|
196
|
+
@field_validator("default_prompt")
|
|
197
|
+
@classmethod
|
|
198
|
+
def validate_prompt(cls, value: str | tuple[str, ...] | None) -> str | tuple[str, ...] | None:
|
|
199
|
+
if isinstance(value, str):
|
|
200
|
+
return _non_empty(value, field="interface.defaultPrompt")
|
|
201
|
+
if isinstance(value, tuple):
|
|
202
|
+
for item in value:
|
|
203
|
+
_non_empty(item, field="interface.defaultPrompt entry")
|
|
204
|
+
return value
|
|
205
|
+
|
|
206
|
+
@field_validator("composer_icon", "logo", "logo_dark")
|
|
207
|
+
@classmethod
|
|
208
|
+
def validate_optional_asset_path(cls, value: str | None) -> str | None:
|
|
209
|
+
return (
|
|
210
|
+
None
|
|
211
|
+
if value is None
|
|
212
|
+
else _relative_reference(
|
|
213
|
+
value,
|
|
214
|
+
field="interface asset",
|
|
215
|
+
require_dot_prefix=True,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
@field_validator("screenshots")
|
|
220
|
+
@classmethod
|
|
221
|
+
def validate_screenshot_paths(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
222
|
+
for item in value:
|
|
223
|
+
_relative_reference(
|
|
224
|
+
item,
|
|
225
|
+
field="interface screenshot",
|
|
226
|
+
require_dot_prefix=True,
|
|
227
|
+
)
|
|
228
|
+
return value
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class CodexPluginManifest(_CodexUpstreamModel):
|
|
232
|
+
"""Normalized official manifest.
|
|
233
|
+
|
|
234
|
+
Only ``name`` is universally required by the native format. Installed
|
|
235
|
+
official plugins exist with deliberately minimal metadata, so optional
|
|
236
|
+
presentation fields cannot be promoted to KsADK-only requirements.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
name: str
|
|
240
|
+
version: str | None = None
|
|
241
|
+
id: str | None = None
|
|
242
|
+
description: str | None = None
|
|
243
|
+
author: CodexPluginAuthor | None = None
|
|
244
|
+
homepage: str | None = None
|
|
245
|
+
repository: str | None = None
|
|
246
|
+
license: str | None = None
|
|
247
|
+
keywords: tuple[str, ...] = ()
|
|
248
|
+
skills: str | tuple[str, ...] | None = None
|
|
249
|
+
hooks: str | tuple[str, ...] | dict[str, Any] | tuple[dict[str, Any], ...] | None = None
|
|
250
|
+
mcp_servers: str | dict[str, dict[str, Any]] | None = Field(
|
|
251
|
+
default=None,
|
|
252
|
+
alias="mcpServers",
|
|
253
|
+
)
|
|
254
|
+
apps: str | None = None
|
|
255
|
+
interface: CodexPluginInterface | None = None
|
|
256
|
+
bundled_content_variant: str | None = Field(
|
|
257
|
+
default=None,
|
|
258
|
+
alias="bundledContentVariant",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
@model_validator(mode="before")
|
|
262
|
+
@classmethod
|
|
263
|
+
def normalize_spelling(cls, value: Any) -> Any:
|
|
264
|
+
return _normalize_aliases(
|
|
265
|
+
value,
|
|
266
|
+
{
|
|
267
|
+
"mcp_servers": "mcpServers",
|
|
268
|
+
"bundled_content_variant": "bundledContentVariant",
|
|
269
|
+
},
|
|
270
|
+
label="plugin.json",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
@field_validator("name")
|
|
274
|
+
@classmethod
|
|
275
|
+
def validate_name(cls, value: str) -> str:
|
|
276
|
+
_non_empty(value, field="plugin.json name")
|
|
277
|
+
if len(value) > 128 or _PLUGIN_NAME.fullmatch(value) is None:
|
|
278
|
+
raise ValueError("plugin.json name is not a valid Codex plugin identifier")
|
|
279
|
+
return value
|
|
280
|
+
|
|
281
|
+
@field_validator("version")
|
|
282
|
+
@classmethod
|
|
283
|
+
def validate_version(cls, value: str | None) -> str | None:
|
|
284
|
+
if value is not None and _SEMVER.fullmatch(value) is None:
|
|
285
|
+
raise ValueError("plugin.json version must be exact semantic version text")
|
|
286
|
+
return value
|
|
287
|
+
|
|
288
|
+
@field_validator(
|
|
289
|
+
"id",
|
|
290
|
+
"description",
|
|
291
|
+
"homepage",
|
|
292
|
+
"repository",
|
|
293
|
+
"license",
|
|
294
|
+
"bundled_content_variant",
|
|
295
|
+
)
|
|
296
|
+
@classmethod
|
|
297
|
+
def validate_optional_text(cls, value: str | None) -> str | None:
|
|
298
|
+
return None if value is None else _non_empty(value, field="plugin.json field")
|
|
299
|
+
|
|
300
|
+
@field_validator("keywords")
|
|
301
|
+
@classmethod
|
|
302
|
+
def validate_keywords(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
303
|
+
for item in value:
|
|
304
|
+
_non_empty(item, field="plugin.json keyword")
|
|
305
|
+
if len(value) != len(set(value)):
|
|
306
|
+
raise ValueError("plugin.json keywords must be unique")
|
|
307
|
+
return value
|
|
308
|
+
|
|
309
|
+
@field_validator("apps")
|
|
310
|
+
@classmethod
|
|
311
|
+
def validate_component_path(cls, value: str | None) -> str | None:
|
|
312
|
+
return (
|
|
313
|
+
None
|
|
314
|
+
if value is None
|
|
315
|
+
else _relative_reference(
|
|
316
|
+
value,
|
|
317
|
+
field="component path",
|
|
318
|
+
require_dot_prefix=True,
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
@field_validator("skills", mode="before")
|
|
323
|
+
@classmethod
|
|
324
|
+
def validate_skill_paths(cls, value: Any) -> Any:
|
|
325
|
+
if value is None:
|
|
326
|
+
return None
|
|
327
|
+
paths = [value] if isinstance(value, str) else value
|
|
328
|
+
if (
|
|
329
|
+
not isinstance(paths, (list, tuple))
|
|
330
|
+
or not paths
|
|
331
|
+
or not all(isinstance(item, str) for item in paths)
|
|
332
|
+
):
|
|
333
|
+
raise ValueError("skills must be a path or non-empty array of paths")
|
|
334
|
+
normalized = tuple(
|
|
335
|
+
_relative_reference(
|
|
336
|
+
item,
|
|
337
|
+
field="skills",
|
|
338
|
+
require_dot_prefix=True,
|
|
339
|
+
)
|
|
340
|
+
for item in paths
|
|
341
|
+
)
|
|
342
|
+
return normalized[0] if isinstance(value, str) else normalized
|
|
343
|
+
|
|
344
|
+
@field_validator("hooks", mode="before")
|
|
345
|
+
@classmethod
|
|
346
|
+
def validate_hook_declaration(cls, value: Any) -> Any:
|
|
347
|
+
if value is None or isinstance(value, dict):
|
|
348
|
+
return value
|
|
349
|
+
if isinstance(value, str):
|
|
350
|
+
return _relative_reference(
|
|
351
|
+
value,
|
|
352
|
+
field="hooks",
|
|
353
|
+
require_dot_prefix=True,
|
|
354
|
+
)
|
|
355
|
+
if not isinstance(value, (list, tuple)) or not value:
|
|
356
|
+
raise ValueError(
|
|
357
|
+
"hooks must be a path, path array, inline object, or inline object array"
|
|
358
|
+
)
|
|
359
|
+
if all(isinstance(item, str) for item in value):
|
|
360
|
+
return tuple(
|
|
361
|
+
_relative_reference(
|
|
362
|
+
item,
|
|
363
|
+
field="hooks",
|
|
364
|
+
require_dot_prefix=True,
|
|
365
|
+
)
|
|
366
|
+
for item in value
|
|
367
|
+
)
|
|
368
|
+
if all(isinstance(item, dict) for item in value):
|
|
369
|
+
return tuple(dict(item) for item in value)
|
|
370
|
+
raise ValueError("hooks arrays cannot mix paths and inline hook objects")
|
|
371
|
+
|
|
372
|
+
@field_validator("mcp_servers")
|
|
373
|
+
@classmethod
|
|
374
|
+
def validate_mcp_declaration(
|
|
375
|
+
cls, value: str | dict[str, dict[str, Any]] | None
|
|
376
|
+
) -> str | dict[str, dict[str, Any]] | None:
|
|
377
|
+
if isinstance(value, str):
|
|
378
|
+
return _relative_reference(
|
|
379
|
+
value,
|
|
380
|
+
field="mcpServers",
|
|
381
|
+
require_dot_prefix=True,
|
|
382
|
+
)
|
|
383
|
+
if isinstance(value, dict):
|
|
384
|
+
_validate_named_object_map(value, label="plugin.json mcpServers")
|
|
385
|
+
return value
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class CodexPluginSourceCoordinate(_CodexSnapshotModel):
|
|
389
|
+
"""Credential-free requested and resolved native source coordinate."""
|
|
390
|
+
|
|
391
|
+
coordinate_format: Literal["codex.plugin-source/v1"] = "codex.plugin-source/v1"
|
|
392
|
+
type: Literal["local", "git", "npm", "remote"]
|
|
393
|
+
requested: str = Field(min_length=1, max_length=2048)
|
|
394
|
+
resolved: str = Field(min_length=1, max_length=2048)
|
|
395
|
+
marketplace_name: str | None = Field(default=None, max_length=256)
|
|
396
|
+
registry: str | None = Field(default=None, max_length=2048)
|
|
397
|
+
integrity: str | None = Field(default=None, max_length=512)
|
|
398
|
+
|
|
399
|
+
@field_validator("requested", "resolved", "marketplace_name", "registry", "integrity")
|
|
400
|
+
@classmethod
|
|
401
|
+
def validate_coordinate_text(cls, value: str | None) -> str | None:
|
|
402
|
+
if value is None:
|
|
403
|
+
return None
|
|
404
|
+
_non_empty(value, field="source coordinate")
|
|
405
|
+
if "://" in value:
|
|
406
|
+
parsed = urlsplit(value)
|
|
407
|
+
if parsed.username or parsed.password or parsed.query or parsed.fragment:
|
|
408
|
+
raise ValueError("source coordinates must not embed credentials or query data")
|
|
409
|
+
return value
|
|
410
|
+
|
|
411
|
+
def to_plugin_source_snapshot(self) -> PluginSourceSnapshot:
|
|
412
|
+
"""Project into the generic lock model without coupling the parser to it."""
|
|
413
|
+
|
|
414
|
+
from ksadk.plugins.contracts import PluginSourceSnapshot
|
|
415
|
+
|
|
416
|
+
source_type: Literal["local", "git", "npm", "market"]
|
|
417
|
+
source_type = "market" if self.type == "remote" else self.type
|
|
418
|
+
return PluginSourceSnapshot(
|
|
419
|
+
ecosystem="codex",
|
|
420
|
+
type=source_type,
|
|
421
|
+
requested=self.requested,
|
|
422
|
+
resolved=self.resolved,
|
|
423
|
+
integrity=self.integrity,
|
|
424
|
+
marketplace=self.marketplace_name,
|
|
425
|
+
registry=self.registry,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class CodexPluginComponentSnapshot(_CodexSnapshotModel):
|
|
430
|
+
component_format: Literal["codex.plugin-component/v1"] = "codex.plugin-component/v1"
|
|
431
|
+
kind: Literal["skill", "mcp", "hook", "app"]
|
|
432
|
+
name: str = Field(min_length=1, max_length=256)
|
|
433
|
+
path: str | None = Field(default=None, max_length=1024)
|
|
434
|
+
content_digest: str
|
|
435
|
+
|
|
436
|
+
@field_validator("name")
|
|
437
|
+
@classmethod
|
|
438
|
+
def validate_component_name(cls, value: str) -> str:
|
|
439
|
+
return _non_empty(value, field="component name")
|
|
440
|
+
|
|
441
|
+
@field_validator("path")
|
|
442
|
+
@classmethod
|
|
443
|
+
def validate_component_path(cls, value: str | None) -> str | None:
|
|
444
|
+
return None if value is None else _relative_reference(value, field="component path")
|
|
445
|
+
|
|
446
|
+
@field_validator("content_digest")
|
|
447
|
+
@classmethod
|
|
448
|
+
def validate_digest(cls, value: str) -> str:
|
|
449
|
+
if _DIGEST.fullmatch(value) is None:
|
|
450
|
+
raise ValueError("component contentDigest must be a lowercase sha256 digest")
|
|
451
|
+
return value
|
|
452
|
+
|
|
453
|
+
def to_locked_component(self) -> LockedPluginComponent:
|
|
454
|
+
from ksadk.plugins.contracts import LockedPluginComponent
|
|
455
|
+
|
|
456
|
+
return LockedPluginComponent(
|
|
457
|
+
id=self.name,
|
|
458
|
+
kind=self.kind,
|
|
459
|
+
digest=self.content_digest,
|
|
460
|
+
path=self.path,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class CodexInstalledPluginSnapshot(_CodexSnapshotModel):
|
|
465
|
+
snapshot_format: Literal["codex.installed-plugin-snapshot/v1"] = (
|
|
466
|
+
"codex.installed-plugin-snapshot/v1"
|
|
467
|
+
)
|
|
468
|
+
installed_root: str = Field(min_length=1, max_length=4096)
|
|
469
|
+
source: CodexPluginSourceCoordinate
|
|
470
|
+
manifest: CodexPluginManifest
|
|
471
|
+
manifest_digest: str
|
|
472
|
+
artifact_digest: str
|
|
473
|
+
components: tuple[CodexPluginComponentSnapshot, ...] = ()
|
|
474
|
+
|
|
475
|
+
@field_validator("manifest_digest", "artifact_digest")
|
|
476
|
+
@classmethod
|
|
477
|
+
def validate_digest(cls, value: str) -> str:
|
|
478
|
+
if _DIGEST.fullmatch(value) is None:
|
|
479
|
+
raise ValueError("snapshot digests must be lowercase sha256 digests")
|
|
480
|
+
return value
|
|
481
|
+
|
|
482
|
+
@field_validator("components")
|
|
483
|
+
@classmethod
|
|
484
|
+
def validate_components(
|
|
485
|
+
cls, value: tuple[CodexPluginComponentSnapshot, ...]
|
|
486
|
+
) -> tuple[CodexPluginComponentSnapshot, ...]:
|
|
487
|
+
identities = [(component.kind, component.name) for component in value]
|
|
488
|
+
if len(identities) != len(set(identities)):
|
|
489
|
+
raise ValueError("component kind/name identities must be unique")
|
|
490
|
+
return tuple(sorted(value, key=lambda item: (item.kind, item.name, item.path or "")))
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _sha256(raw: bytes) -> str:
|
|
494
|
+
return "sha256:" + hashlib.sha256(raw).hexdigest()
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _canonical_json_digest(value: Any) -> str:
|
|
498
|
+
return _sha256(
|
|
499
|
+
json.dumps(
|
|
500
|
+
value,
|
|
501
|
+
sort_keys=True,
|
|
502
|
+
separators=(",", ":"),
|
|
503
|
+
ensure_ascii=False,
|
|
504
|
+
).encode("utf-8")
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _validate_root(plugin_root: Path) -> Path:
|
|
509
|
+
root = Path(plugin_root).expanduser()
|
|
510
|
+
if root.is_symlink():
|
|
511
|
+
raise CodexPluginManifestError("installed plugin root must not be a symlink")
|
|
512
|
+
if not root.is_dir():
|
|
513
|
+
raise CodexPluginManifestError("installed plugin root must be an existing directory")
|
|
514
|
+
return root.resolve()
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _contained_path(
|
|
518
|
+
root: Path,
|
|
519
|
+
raw_path: str | PurePosixPath,
|
|
520
|
+
*,
|
|
521
|
+
label: str,
|
|
522
|
+
expected: Literal["file", "directory", "either"] = "either",
|
|
523
|
+
) -> Path:
|
|
524
|
+
relative_text = raw_path.as_posix() if isinstance(raw_path, PurePosixPath) else raw_path
|
|
525
|
+
try:
|
|
526
|
+
_relative_reference(relative_text, field=label)
|
|
527
|
+
except ValueError as exc:
|
|
528
|
+
raise CodexPluginManifestError(str(exc)) from exc
|
|
529
|
+
relative = PurePosixPath(relative_text)
|
|
530
|
+
cursor = root
|
|
531
|
+
for part in relative.parts:
|
|
532
|
+
if part == ".":
|
|
533
|
+
continue
|
|
534
|
+
cursor /= part
|
|
535
|
+
if cursor.is_symlink():
|
|
536
|
+
raise CodexPluginManifestError(f"{label} must not traverse a symlink: {relative}")
|
|
537
|
+
try:
|
|
538
|
+
resolved = cursor.resolve(strict=True)
|
|
539
|
+
resolved.relative_to(root)
|
|
540
|
+
except (OSError, ValueError) as exc:
|
|
541
|
+
raise CodexPluginManifestError(
|
|
542
|
+
f"{label} does not resolve to an existing path inside the plugin root: {relative}"
|
|
543
|
+
) from exc
|
|
544
|
+
if expected == "file" and not resolved.is_file():
|
|
545
|
+
raise CodexPluginManifestError(f"{label} must resolve to a regular file: {relative}")
|
|
546
|
+
if expected == "directory" and not resolved.is_dir():
|
|
547
|
+
raise CodexPluginManifestError(f"{label} must resolve to a directory: {relative}")
|
|
548
|
+
if expected == "either" and not (resolved.is_file() or resolved.is_dir()):
|
|
549
|
+
raise CodexPluginManifestError(f"{label} must resolve to a regular file or directory")
|
|
550
|
+
return resolved
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _read_json_object(path: Path, *, root: Path, label: str) -> tuple[dict[str, Any], bytes]:
|
|
554
|
+
relative = path.relative_to(root).as_posix()
|
|
555
|
+
path = _contained_path(root, relative, label=label, expected="file")
|
|
556
|
+
try:
|
|
557
|
+
stat = path.stat()
|
|
558
|
+
if stat.st_size > _MAX_JSON_BYTES:
|
|
559
|
+
raise CodexPluginManifestError(f"{label} exceeds {_MAX_JSON_BYTES} bytes")
|
|
560
|
+
raw = path.read_bytes()
|
|
561
|
+
value = json.loads(raw)
|
|
562
|
+
except CodexPluginManifestError:
|
|
563
|
+
raise
|
|
564
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
565
|
+
raise CodexPluginManifestError(f"{label} must contain valid UTF-8 JSON") from exc
|
|
566
|
+
if not isinstance(value, dict):
|
|
567
|
+
raise CodexPluginManifestError(f"{label} must contain one JSON object")
|
|
568
|
+
return value, raw
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _validate_named_object_map(value: Any, *, label: str) -> dict[str, dict[str, Any]]:
|
|
572
|
+
if not isinstance(value, dict):
|
|
573
|
+
raise CodexPluginManifestError(f"{label} must be an object")
|
|
574
|
+
normalized: dict[str, dict[str, Any]] = {}
|
|
575
|
+
for name, config in value.items():
|
|
576
|
+
if not isinstance(name, str):
|
|
577
|
+
raise CodexPluginManifestError(f"{label} names must be strings")
|
|
578
|
+
try:
|
|
579
|
+
_non_empty(name, field=f"{label} name")
|
|
580
|
+
except ValueError as exc:
|
|
581
|
+
raise CodexPluginManifestError(str(exc)) from exc
|
|
582
|
+
if not isinstance(config, dict):
|
|
583
|
+
raise CodexPluginManifestError(f"{label} entry {name!r} must be an object")
|
|
584
|
+
normalized[name] = config
|
|
585
|
+
return normalized
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _component_paths(root: Path, manifest: CodexPluginManifest) -> dict[str, tuple[Path, ...]]:
|
|
589
|
+
paths: dict[str, list[Path]] = {"skills": [], "mcp": [], "hooks": [], "apps": []}
|
|
590
|
+
|
|
591
|
+
def add(
|
|
592
|
+
kind: str,
|
|
593
|
+
declarations: Iterable[str],
|
|
594
|
+
defaults: Iterable[str],
|
|
595
|
+
*,
|
|
596
|
+
expected: Literal["file", "directory", "either"],
|
|
597
|
+
) -> None:
|
|
598
|
+
default_paths = tuple(defaults)
|
|
599
|
+
raw_paths = (*default_paths, *declarations)
|
|
600
|
+
seen: set[Path] = set()
|
|
601
|
+
for raw_path in raw_paths:
|
|
602
|
+
candidate = root / PurePosixPath(raw_path)
|
|
603
|
+
if raw_path in default_paths and not candidate.exists():
|
|
604
|
+
continue
|
|
605
|
+
resolved = _contained_path(
|
|
606
|
+
root,
|
|
607
|
+
raw_path,
|
|
608
|
+
label=f"{kind} component path",
|
|
609
|
+
expected=expected,
|
|
610
|
+
)
|
|
611
|
+
if resolved not in seen:
|
|
612
|
+
paths[kind].append(resolved)
|
|
613
|
+
seen.add(resolved)
|
|
614
|
+
|
|
615
|
+
skill_paths = (
|
|
616
|
+
(manifest.skills,) if isinstance(manifest.skills, str) else tuple(manifest.skills or ())
|
|
617
|
+
)
|
|
618
|
+
add("skills", skill_paths, (), expected="either")
|
|
619
|
+
mcp_path = manifest.mcp_servers if isinstance(manifest.mcp_servers, str) else None
|
|
620
|
+
add("mcp", (mcp_path,) if mcp_path else (), (), expected="file")
|
|
621
|
+
hook_paths: tuple[str, ...]
|
|
622
|
+
if isinstance(manifest.hooks, str):
|
|
623
|
+
hook_paths = (manifest.hooks,)
|
|
624
|
+
elif isinstance(manifest.hooks, tuple) and all(
|
|
625
|
+
isinstance(item, str) for item in manifest.hooks
|
|
626
|
+
):
|
|
627
|
+
hook_paths = manifest.hooks
|
|
628
|
+
else:
|
|
629
|
+
hook_paths = ()
|
|
630
|
+
hook_defaults = ("./hooks/hooks.json",) if manifest.hooks is None else ()
|
|
631
|
+
add("hooks", hook_paths, hook_defaults, expected="file")
|
|
632
|
+
add("apps", (manifest.apps,) if manifest.apps else (), (), expected="file")
|
|
633
|
+
return {kind: tuple(value) for kind, value in paths.items()}
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _load_mcp_servers(
|
|
637
|
+
root: Path,
|
|
638
|
+
manifest: CodexPluginManifest,
|
|
639
|
+
paths: tuple[Path, ...],
|
|
640
|
+
) -> list[tuple[str, dict[str, Any], str | None]]:
|
|
641
|
+
entries: list[tuple[str, dict[str, Any], str | None]] = []
|
|
642
|
+
if isinstance(manifest.mcp_servers, dict):
|
|
643
|
+
for name, config in _validate_named_object_map(
|
|
644
|
+
manifest.mcp_servers, label="plugin.json mcpServers"
|
|
645
|
+
).items():
|
|
646
|
+
entries.append((name, config, None))
|
|
647
|
+
for path in paths:
|
|
648
|
+
payload, _raw = _read_json_object(path, root=root, label="Codex MCP manifest")
|
|
649
|
+
if "mcpServers" in payload and "mcp_servers" in payload:
|
|
650
|
+
raise CodexPluginManifestError(
|
|
651
|
+
"Codex MCP manifest cannot contain both 'mcpServers' and 'mcp_servers'"
|
|
652
|
+
)
|
|
653
|
+
if "mcpServers" in payload:
|
|
654
|
+
servers = payload["mcpServers"]
|
|
655
|
+
unexpected = set(payload) - {"mcpServers"}
|
|
656
|
+
elif "mcp_servers" in payload:
|
|
657
|
+
servers = payload["mcp_servers"]
|
|
658
|
+
unexpected = set(payload) - {"mcp_servers"}
|
|
659
|
+
else:
|
|
660
|
+
servers = payload
|
|
661
|
+
unexpected = set()
|
|
662
|
+
if unexpected:
|
|
663
|
+
raise CodexPluginManifestError(
|
|
664
|
+
"Codex MCP manifest wrapper cannot contain unrelated fields: "
|
|
665
|
+
+ ", ".join(sorted(unexpected))
|
|
666
|
+
)
|
|
667
|
+
relative = path.relative_to(root).as_posix()
|
|
668
|
+
for name, config in _validate_named_object_map(
|
|
669
|
+
servers, label="Codex MCP manifest servers"
|
|
670
|
+
).items():
|
|
671
|
+
entries.append((name, config, relative))
|
|
672
|
+
_reject_duplicate_names(entries, label="MCP server")
|
|
673
|
+
return entries
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _load_named_companion(
|
|
677
|
+
root: Path,
|
|
678
|
+
paths: tuple[Path, ...],
|
|
679
|
+
*,
|
|
680
|
+
key: str,
|
|
681
|
+
label: str,
|
|
682
|
+
) -> list[tuple[str, dict[str, Any], str]]:
|
|
683
|
+
entries: list[tuple[str, dict[str, Any], str]] = []
|
|
684
|
+
for path in paths:
|
|
685
|
+
payload, _raw = _read_json_object(path, root=root, label=label)
|
|
686
|
+
if key not in payload:
|
|
687
|
+
raise CodexPluginManifestError(f"{label} must contain a {key!r} object")
|
|
688
|
+
unexpected = set(payload) - {key}
|
|
689
|
+
if unexpected:
|
|
690
|
+
raise CodexPluginManifestError(
|
|
691
|
+
f"{label} cannot contain unrelated fields: " + ", ".join(sorted(unexpected))
|
|
692
|
+
)
|
|
693
|
+
relative = path.relative_to(root).as_posix()
|
|
694
|
+
named_entries = _validate_named_object_map(payload[key], label=f"{label} {key}")
|
|
695
|
+
for name, config in named_entries.items():
|
|
696
|
+
entries.append((name, config, relative))
|
|
697
|
+
_reject_duplicate_names(entries, label=key.rstrip("s"))
|
|
698
|
+
return entries
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _load_hooks(
|
|
702
|
+
root: Path,
|
|
703
|
+
manifest: CodexPluginManifest,
|
|
704
|
+
paths: tuple[Path, ...],
|
|
705
|
+
) -> list[tuple[str, dict[str, Any], str | None]]:
|
|
706
|
+
grouped: dict[str, list[dict[str, Any]]] = {}
|
|
707
|
+
sources: dict[str, set[str | None]] = {}
|
|
708
|
+
|
|
709
|
+
def append_payload(payload: dict[str, Any], *, relative: str | None) -> None:
|
|
710
|
+
unexpected = set(payload) - {"description", "hooks"}
|
|
711
|
+
if unexpected:
|
|
712
|
+
raise CodexPluginManifestError(
|
|
713
|
+
"Codex hooks manifest cannot contain unrelated fields: "
|
|
714
|
+
+ ", ".join(sorted(unexpected))
|
|
715
|
+
)
|
|
716
|
+
description = payload.get("description")
|
|
717
|
+
if description is not None and (
|
|
718
|
+
not isinstance(description, str) or not description.strip()
|
|
719
|
+
):
|
|
720
|
+
raise CodexPluginManifestError(
|
|
721
|
+
"Codex hooks manifest description must be a non-empty string"
|
|
722
|
+
)
|
|
723
|
+
if "hooks" not in payload or not isinstance(payload["hooks"], dict):
|
|
724
|
+
raise CodexPluginManifestError("Codex hooks manifest must contain a 'hooks' object")
|
|
725
|
+
for event, hooks in payload["hooks"].items():
|
|
726
|
+
if not isinstance(event, str) or not event.strip():
|
|
727
|
+
raise CodexPluginManifestError("Codex hook event names must be non-empty strings")
|
|
728
|
+
if not isinstance(hooks, list) or not all(isinstance(item, dict) for item in hooks):
|
|
729
|
+
raise CodexPluginManifestError(
|
|
730
|
+
f"Codex hooks for event {event!r} must be an array of objects"
|
|
731
|
+
)
|
|
732
|
+
grouped.setdefault(event, []).extend(hooks)
|
|
733
|
+
sources.setdefault(event, set()).add(relative)
|
|
734
|
+
|
|
735
|
+
inline: tuple[dict[str, Any], ...] = ()
|
|
736
|
+
if isinstance(manifest.hooks, dict):
|
|
737
|
+
inline = (manifest.hooks,)
|
|
738
|
+
elif isinstance(manifest.hooks, tuple) and all(
|
|
739
|
+
isinstance(item, dict) for item in manifest.hooks
|
|
740
|
+
):
|
|
741
|
+
inline = manifest.hooks
|
|
742
|
+
for payload in inline:
|
|
743
|
+
append_payload(payload, relative=None)
|
|
744
|
+
for path in paths:
|
|
745
|
+
payload, _raw = _read_json_object(path, root=root, label="Codex hooks manifest")
|
|
746
|
+
relative = path.relative_to(root).as_posix()
|
|
747
|
+
append_payload(payload, relative=relative)
|
|
748
|
+
return [
|
|
749
|
+
(
|
|
750
|
+
event,
|
|
751
|
+
{"hooks": grouped[event]},
|
|
752
|
+
next(iter(sources[event]))
|
|
753
|
+
if len(sources[event]) == 1 and None not in sources[event]
|
|
754
|
+
else None,
|
|
755
|
+
)
|
|
756
|
+
for event in sorted(grouped)
|
|
757
|
+
]
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _reject_duplicate_names(
|
|
761
|
+
entries: Iterable[tuple[str, dict[str, Any], str | None]], *, label: str
|
|
762
|
+
) -> None:
|
|
763
|
+
seen: set[str] = set()
|
|
764
|
+
for name, _config, _path in entries:
|
|
765
|
+
if name in seen:
|
|
766
|
+
raise CodexPluginManifestError(f"duplicate {label} name: {name!r}")
|
|
767
|
+
seen.add(name)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def _iter_skill_roots(root: Path, paths: tuple[Path, ...]) -> list[tuple[str, Path]]:
|
|
771
|
+
skills: list[tuple[str, Path]] = []
|
|
772
|
+
names: set[str] = set()
|
|
773
|
+
for path in paths:
|
|
774
|
+
candidates: list[Path]
|
|
775
|
+
if path.is_file():
|
|
776
|
+
if path.name != "SKILL.md":
|
|
777
|
+
raise CodexPluginManifestError("a file-valued skills path must name SKILL.md")
|
|
778
|
+
candidates = [path.parent]
|
|
779
|
+
else:
|
|
780
|
+
direct_manifest = path / "SKILL.md"
|
|
781
|
+
if direct_manifest.is_file():
|
|
782
|
+
candidates = [path]
|
|
783
|
+
else:
|
|
784
|
+
candidates = [
|
|
785
|
+
child
|
|
786
|
+
for child in sorted(path.iterdir(), key=lambda item: item.name)
|
|
787
|
+
if not child.name.startswith(".") and child.is_dir()
|
|
788
|
+
]
|
|
789
|
+
for skill_root in candidates:
|
|
790
|
+
relative = skill_root.relative_to(root).as_posix()
|
|
791
|
+
_contained_path(
|
|
792
|
+
root,
|
|
793
|
+
relative,
|
|
794
|
+
label="skill directory",
|
|
795
|
+
expected="directory",
|
|
796
|
+
)
|
|
797
|
+
skill_manifest = skill_root / "SKILL.md"
|
|
798
|
+
if not skill_manifest.is_file() or skill_manifest.is_symlink():
|
|
799
|
+
raise CodexPluginManifestError(f"skill {relative!r} is missing a regular SKILL.md")
|
|
800
|
+
name = skill_root.name
|
|
801
|
+
if name in names:
|
|
802
|
+
raise CodexPluginManifestError(f"duplicate skill directory name: {name!r}")
|
|
803
|
+
names.add(name)
|
|
804
|
+
skills.append((name, skill_root))
|
|
805
|
+
return skills
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _validate_interface_assets(root: Path, interface: CodexPluginInterface | None) -> None:
|
|
809
|
+
if interface is None:
|
|
810
|
+
return
|
|
811
|
+
references = [
|
|
812
|
+
interface.composer_icon,
|
|
813
|
+
interface.logo,
|
|
814
|
+
interface.logo_dark,
|
|
815
|
+
*interface.screenshots,
|
|
816
|
+
]
|
|
817
|
+
for reference in references:
|
|
818
|
+
if reference is not None:
|
|
819
|
+
_contained_path(root, reference, label="interface asset", expected="file")
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def _load_installed_plugin(
|
|
823
|
+
plugin_root: Path,
|
|
824
|
+
) -> tuple[
|
|
825
|
+
Path,
|
|
826
|
+
CodexPluginManifest,
|
|
827
|
+
bytes,
|
|
828
|
+
list[tuple[str, Path]],
|
|
829
|
+
list[tuple[str, dict[str, Any], str | None]],
|
|
830
|
+
list[tuple[str, dict[str, Any], str | None]],
|
|
831
|
+
list[tuple[str, dict[str, Any], str]],
|
|
832
|
+
]:
|
|
833
|
+
root = _validate_root(plugin_root)
|
|
834
|
+
manifest_path = root / _MANIFEST_RELATIVE_PATH
|
|
835
|
+
payload, raw = _read_json_object(
|
|
836
|
+
manifest_path,
|
|
837
|
+
root=root,
|
|
838
|
+
label=".codex-plugin/plugin.json",
|
|
839
|
+
)
|
|
840
|
+
try:
|
|
841
|
+
manifest = CodexPluginManifest.model_validate(payload)
|
|
842
|
+
except (TypeError, ValueError) as exc:
|
|
843
|
+
raise CodexPluginManifestError(f"invalid .codex-plugin/plugin.json: {exc}") from exc
|
|
844
|
+
|
|
845
|
+
paths = _component_paths(root, manifest)
|
|
846
|
+
skills = _iter_skill_roots(root, paths["skills"])
|
|
847
|
+
mcp_servers = _load_mcp_servers(root, manifest, paths["mcp"])
|
|
848
|
+
hooks = _load_hooks(root, manifest, paths["hooks"])
|
|
849
|
+
apps = _load_named_companion(
|
|
850
|
+
root,
|
|
851
|
+
paths["apps"],
|
|
852
|
+
key="apps",
|
|
853
|
+
label="Codex app manifest",
|
|
854
|
+
)
|
|
855
|
+
_validate_interface_assets(root, manifest.interface)
|
|
856
|
+
return root, manifest, raw, skills, mcp_servers, hooks, apps
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def load_codex_plugin_manifest(plugin_root: Path) -> CodexPluginManifest:
|
|
860
|
+
"""Load one installed Codex manifest and validate all referenced components."""
|
|
861
|
+
|
|
862
|
+
_root, manifest, _raw, _skills, _mcp, _hooks, _apps = _load_installed_plugin(plugin_root)
|
|
863
|
+
return manifest
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _tree_digest(path: Path) -> str:
|
|
867
|
+
digest = hashlib.sha256()
|
|
868
|
+
files: list[Path]
|
|
869
|
+
if path.is_file():
|
|
870
|
+
files = [path]
|
|
871
|
+
base = path.parent
|
|
872
|
+
else:
|
|
873
|
+
files = []
|
|
874
|
+
base = path
|
|
875
|
+
for candidate in sorted(
|
|
876
|
+
path.rglob("*"),
|
|
877
|
+
key=lambda item: item.relative_to(path).as_posix(),
|
|
878
|
+
):
|
|
879
|
+
relative = candidate.relative_to(path).as_posix()
|
|
880
|
+
if candidate.is_symlink():
|
|
881
|
+
raise CodexPluginManifestError(
|
|
882
|
+
f"plugin snapshots must not include symlinks: {relative}"
|
|
883
|
+
)
|
|
884
|
+
if candidate.is_dir():
|
|
885
|
+
continue
|
|
886
|
+
if not candidate.is_file():
|
|
887
|
+
raise CodexPluginManifestError(
|
|
888
|
+
f"plugin snapshots may contain regular files only: {relative}"
|
|
889
|
+
)
|
|
890
|
+
files.append(candidate)
|
|
891
|
+
if len(files) > _MAX_ARTIFACT_FILES:
|
|
892
|
+
raise CodexPluginManifestError(
|
|
893
|
+
f"plugin snapshot exceeds {_MAX_ARTIFACT_FILES} regular files"
|
|
894
|
+
)
|
|
895
|
+
total_bytes = 0
|
|
896
|
+
for candidate in files:
|
|
897
|
+
relative = candidate.relative_to(base).as_posix()
|
|
898
|
+
try:
|
|
899
|
+
raw = candidate.read_bytes()
|
|
900
|
+
except OSError as exc:
|
|
901
|
+
raise CodexPluginManifestError(f"plugin file cannot be read: {relative}") from exc
|
|
902
|
+
total_bytes += len(raw)
|
|
903
|
+
if total_bytes > _MAX_ARTIFACT_BYTES:
|
|
904
|
+
raise CodexPluginManifestError(
|
|
905
|
+
f"plugin snapshot exceeds {_MAX_ARTIFACT_BYTES} content bytes"
|
|
906
|
+
)
|
|
907
|
+
digest.update(relative.encode("utf-8"))
|
|
908
|
+
digest.update(b"\0")
|
|
909
|
+
digest.update(f"{stat.S_IMODE(candidate.stat().st_mode) & 0o111:03o}".encode("ascii"))
|
|
910
|
+
digest.update(b"\0")
|
|
911
|
+
digest.update(str(len(raw)).encode("ascii"))
|
|
912
|
+
digest.update(b"\0")
|
|
913
|
+
digest.update(raw)
|
|
914
|
+
digest.update(b"\0")
|
|
915
|
+
return "sha256:" + digest.hexdigest()
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
def snapshot_installed_codex_plugin(
|
|
919
|
+
plugin_root: Path,
|
|
920
|
+
*,
|
|
921
|
+
source: CodexPluginSourceCoordinate,
|
|
922
|
+
) -> CodexInstalledPluginSnapshot:
|
|
923
|
+
"""Create a deterministic installed-root and per-component snapshot."""
|
|
924
|
+
|
|
925
|
+
root, manifest, raw, skills, mcp_servers, hooks, apps = _load_installed_plugin(plugin_root)
|
|
926
|
+
components: list[CodexPluginComponentSnapshot] = []
|
|
927
|
+
for name, skill_root in skills:
|
|
928
|
+
components.append(
|
|
929
|
+
CodexPluginComponentSnapshot(
|
|
930
|
+
kind="skill",
|
|
931
|
+
name=name,
|
|
932
|
+
path=skill_root.relative_to(root).as_posix(),
|
|
933
|
+
content_digest=_tree_digest(skill_root),
|
|
934
|
+
)
|
|
935
|
+
)
|
|
936
|
+
for name, config, path in mcp_servers:
|
|
937
|
+
components.append(
|
|
938
|
+
CodexPluginComponentSnapshot(
|
|
939
|
+
kind="mcp",
|
|
940
|
+
name=name,
|
|
941
|
+
path=path,
|
|
942
|
+
content_digest=_canonical_json_digest({"name": name, "config": config}),
|
|
943
|
+
)
|
|
944
|
+
)
|
|
945
|
+
for name, config, path in hooks:
|
|
946
|
+
components.append(
|
|
947
|
+
CodexPluginComponentSnapshot(
|
|
948
|
+
kind="hook",
|
|
949
|
+
name=name,
|
|
950
|
+
path=path,
|
|
951
|
+
content_digest=_canonical_json_digest({"name": name, "config": config}),
|
|
952
|
+
)
|
|
953
|
+
)
|
|
954
|
+
for name, config, path in apps:
|
|
955
|
+
components.append(
|
|
956
|
+
CodexPluginComponentSnapshot(
|
|
957
|
+
kind="app",
|
|
958
|
+
name=name,
|
|
959
|
+
path=path,
|
|
960
|
+
content_digest=_canonical_json_digest({"name": name, "config": config}),
|
|
961
|
+
)
|
|
962
|
+
)
|
|
963
|
+
return CodexInstalledPluginSnapshot(
|
|
964
|
+
installed_root=str(root),
|
|
965
|
+
source=source,
|
|
966
|
+
manifest=manifest,
|
|
967
|
+
manifest_digest=_sha256(raw),
|
|
968
|
+
artifact_digest=_tree_digest(root),
|
|
969
|
+
components=tuple(components),
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
__all__ = [
|
|
974
|
+
"CodexInstalledPluginSnapshot",
|
|
975
|
+
"CodexPluginAuthor",
|
|
976
|
+
"CodexPluginComponentSnapshot",
|
|
977
|
+
"CodexPluginInterface",
|
|
978
|
+
"CodexPluginManifest",
|
|
979
|
+
"CodexPluginManifestError",
|
|
980
|
+
"CodexPluginSourceCoordinate",
|
|
981
|
+
"load_codex_plugin_manifest",
|
|
982
|
+
"snapshot_installed_codex_plugin",
|
|
983
|
+
]
|