@skaile/workspaces 0.29.1 → 0.30.1
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.
- package/CHANGELOG.md +40 -0
- package/dist/{asset-feeds-2V7LZIG7.js → asset-feeds-IWOBYROH.js} +14 -13
- package/dist/asset-feeds-IWOBYROH.js.map +1 -0
- package/dist/asset-manager/index.js +12 -12
- package/dist/asset-manager/installer.js +11 -11
- package/dist/asset-manager/renderers.js +2 -2
- package/dist/asset-manager/src/index.d.ts +27 -0
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/asset-manager/src/install-agent.d.ts.map +1 -1
- package/dist/asset-manager/src/installer.d.ts +1 -2
- package/dist/asset-manager/src/installer.d.ts.map +1 -1
- package/dist/asset-manager/src/renderers.d.ts.map +1 -1
- package/dist/bridge/drivers/claude-sdk.js +324 -226
- package/dist/bridge/drivers/claude-sdk.js.map +1 -1
- package/dist/bridge/drivers/codex.js +3 -3
- package/dist/bridge/drivers/codex.js.map +1 -1
- package/dist/bridge/drivers/continue.js +4 -4
- package/dist/bridge/drivers/echo.js +2 -2
- package/dist/bridge/drivers/echo.js.map +1 -1
- package/dist/bridge/drivers/gemini.js +5 -5
- package/dist/bridge/drivers/goose.js +4 -4
- package/dist/bridge/drivers/omp.js +3 -3
- package/dist/bridge/drivers/opencode.js +4 -4
- package/dist/bridge/drivers/qwen.js +5 -5
- package/dist/bridge/index.js +4 -4
- package/dist/bridge/src/drivers/claude-sdk.d.ts +64 -0
- package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
- package/dist/bridge/src/drivers/echo.d.ts +1 -1
- package/dist/bridge/src/drivers/echo.d.ts.map +1 -1
- package/dist/bridge/src/error-classifier.d.ts.map +1 -1
- package/dist/bridge/src/normalizer.d.ts +5 -0
- package/dist/bridge/src/normalizer.d.ts.map +1 -1
- package/dist/{chunk-LDUNTZB6.js → chunk-26L2B6P6.js} +2 -2
- package/dist/{chunk-LDUNTZB6.js.map → chunk-26L2B6P6.js.map} +1 -1
- package/dist/{chunk-FWHOU3CX.js → chunk-2VKVWN5E.js} +6 -6
- package/dist/{chunk-FWHOU3CX.js.map → chunk-2VKVWN5E.js.map} +1 -1
- package/dist/{chunk-2FS4FPZ7.js → chunk-4AGIEU7V.js} +44 -62
- package/dist/chunk-4AGIEU7V.js.map +1 -0
- package/dist/{chunk-ZONCCO5N.js → chunk-4R6DROGN.js} +30 -32
- package/dist/chunk-4R6DROGN.js.map +1 -0
- package/dist/{chunk-3QQSSRYN.js → chunk-5DQOZFWJ.js} +416 -345
- package/dist/chunk-5DQOZFWJ.js.map +1 -0
- package/dist/{chunk-LD4QIVBY.js → chunk-5MJJYIL4.js} +3 -3
- package/dist/{chunk-LD4QIVBY.js.map → chunk-5MJJYIL4.js.map} +1 -1
- package/dist/{chunk-AKSR434V.js → chunk-5XAQDOMU.js} +79 -56
- package/dist/chunk-5XAQDOMU.js.map +1 -0
- package/dist/{chunk-MBOR7D65.js → chunk-67MFR5LS.js} +3 -3
- package/dist/{chunk-MBOR7D65.js.map → chunk-67MFR5LS.js.map} +1 -1
- package/dist/{chunk-7GWYRVAX.js → chunk-6LW7Q53Z.js} +162 -165
- package/dist/chunk-6LW7Q53Z.js.map +1 -0
- package/dist/{chunk-SF4GWVR5.js → chunk-6TCOYFVJ.js} +86 -55
- package/dist/chunk-6TCOYFVJ.js.map +1 -0
- package/dist/{chunk-QMONOHXT.js → chunk-74UKG2LA.js} +3 -3
- package/dist/{chunk-QMONOHXT.js.map → chunk-74UKG2LA.js.map} +1 -1
- package/dist/{chunk-JIQLLZ65.js → chunk-7MANNJ3U.js} +534 -235
- package/dist/chunk-7MANNJ3U.js.map +1 -0
- package/dist/{chunk-7R6W5EQR.js → chunk-ACXLKNIM.js} +2 -2
- package/dist/{chunk-7R6W5EQR.js.map → chunk-ACXLKNIM.js.map} +1 -1
- package/dist/{chunk-72IA6BTP.js → chunk-AKYEI3D6.js} +9 -9
- package/dist/{chunk-72IA6BTP.js.map → chunk-AKYEI3D6.js.map} +1 -1
- package/dist/{chunk-CMG3GJ6B.js → chunk-DAI754GK.js} +4 -4
- package/dist/{chunk-CMG3GJ6B.js.map → chunk-DAI754GK.js.map} +1 -1
- package/dist/{chunk-OTFG5Q7H.js → chunk-FRRTUVUM.js} +4594 -4480
- package/dist/chunk-FRRTUVUM.js.map +1 -0
- package/dist/{chunk-TV72ZJYN.js → chunk-GXC3TYMQ.js} +60 -47
- package/dist/chunk-GXC3TYMQ.js.map +1 -0
- package/dist/{chunk-IERB3UXJ.js → chunk-H5YXL4KI.js} +83 -69
- package/dist/chunk-H5YXL4KI.js.map +1 -0
- package/dist/{chunk-QDJKE557.js → chunk-HRKB2ATT.js} +64 -64
- package/dist/chunk-HRKB2ATT.js.map +1 -0
- package/dist/{chunk-ZEKWHJBB.js → chunk-IZIFMNTI.js} +3 -3
- package/dist/{chunk-ZEKWHJBB.js.map → chunk-IZIFMNTI.js.map} +1 -1
- package/dist/{chunk-24UIWON4.js → chunk-JB35MVBN.js} +167 -156
- package/dist/chunk-JB35MVBN.js.map +1 -0
- package/dist/{chunk-UIZKIUC6.js → chunk-KHZI44SS.js} +3 -3
- package/dist/chunk-KHZI44SS.js.map +1 -0
- package/dist/{chunk-5YO3H5Q4.js → chunk-KY5NY7LB.js} +3 -3
- package/dist/chunk-KY5NY7LB.js.map +1 -0
- package/dist/{chunk-MIH45XPY.js → chunk-LA2YSXEE.js} +1974 -1919
- package/dist/chunk-LA2YSXEE.js.map +1 -0
- package/dist/{chunk-IEJ5IOC3.js → chunk-LFDZYCEO.js} +4 -2
- package/dist/chunk-LFDZYCEO.js.map +1 -0
- package/dist/{chunk-2NOE4AGD.js → chunk-LIK75OJ5.js} +3 -3
- package/dist/{chunk-2NOE4AGD.js.map → chunk-LIK75OJ5.js.map} +1 -1
- package/dist/{chunk-PAYAFNXG.js → chunk-NGJ6E344.js} +104 -74
- package/dist/chunk-NGJ6E344.js.map +1 -0
- package/dist/{chunk-5V5KZQIC.js → chunk-PVMAIVNL.js} +4 -4
- package/dist/{chunk-5V5KZQIC.js.map → chunk-PVMAIVNL.js.map} +1 -1
- package/dist/{chunk-HK5FRWG6.js → chunk-R6O6YWU7.js} +2 -2
- package/dist/{chunk-HK5FRWG6.js.map → chunk-R6O6YWU7.js.map} +1 -1
- package/dist/{chunk-PRLD7EXG.js → chunk-SBGHNXJD.js} +2 -2
- package/dist/{chunk-PRLD7EXG.js.map → chunk-SBGHNXJD.js.map} +1 -1
- package/dist/{chunk-5RBPPBEK.js → chunk-SCJD3FMO.js} +4 -4
- package/dist/{chunk-5RBPPBEK.js.map → chunk-SCJD3FMO.js.map} +1 -1
- package/dist/{chunk-BZ2JU7IP.js → chunk-TU4VZULO.js} +5 -5
- package/dist/{chunk-BZ2JU7IP.js.map → chunk-TU4VZULO.js.map} +1 -1
- package/dist/{chunk-DVMSGGCM.js → chunk-VGFYUD3T.js} +158 -108
- package/dist/chunk-VGFYUD3T.js.map +1 -0
- package/dist/{chunk-Z3XXKR5E.js → chunk-WDMH4UMD.js} +4 -6
- package/dist/chunk-WDMH4UMD.js.map +1 -0
- package/dist/{chunk-W7W66MDZ.js → chunk-X46FY45B.js} +44 -42
- package/dist/chunk-X46FY45B.js.map +1 -0
- package/dist/{chunk-JKNWJ64A.js → chunk-XGCXFX7X.js} +2 -2
- package/dist/{chunk-JKNWJ64A.js.map → chunk-XGCXFX7X.js.map} +1 -1
- package/dist/{chunk-MLJKA3S6.js → chunk-XOAF5QAE.js} +2094 -1901
- package/dist/chunk-XOAF5QAE.js.map +1 -0
- package/dist/{chunk-FQHHCICT.js → chunk-XVDXGQ55.js} +226 -226
- package/dist/chunk-XVDXGQ55.js.map +1 -0
- package/dist/{chunk-M4KLIEIJ.js → chunk-YBJLYT4S.js} +3 -3
- package/dist/chunk-YBJLYT4S.js.map +1 -0
- package/dist/{chunk-OKRUTSG7.js → chunk-ZDKJHQY2.js} +2 -2
- package/dist/{chunk-OKRUTSG7.js.map → chunk-ZDKJHQY2.js.map} +1 -1
- package/dist/{chunk-UBCZVONU.js → chunk-ZME3JDA6.js} +3 -3
- package/dist/{chunk-UBCZVONU.js.map → chunk-ZME3JDA6.js.map} +1 -1
- package/dist/cli/index.js +2811 -2662
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/asset-feeds.d.ts +0 -9
- package/dist/cli/src/asset-feeds.d.ts.map +1 -1
- package/dist/cli/src/commands/catalog-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/catalog.d.ts.map +1 -1
- package/dist/cli/src/commands/config.d.ts.map +1 -1
- package/dist/cli/src/commands/deploy.d.ts.map +1 -1
- package/dist/cli/src/commands/integration.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/manage.d.ts.map +1 -1
- package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/project.d.ts +1 -1
- package/dist/cli/src/commands/project.d.ts.map +1 -1
- package/dist/cli/src/commands/show.d.ts.map +1 -1
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/validate.d.ts.map +1 -1
- package/dist/cli/src/ensure-sources.d.ts.map +1 -1
- package/dist/cli/src/flow-discovery.d.ts.map +1 -1
- package/dist/cli/src/open-registry.d.ts.map +1 -1
- package/dist/cli/src/plugin-store/index.d.ts +4 -6
- package/dist/cli/src/plugin-store/index.d.ts.map +1 -1
- package/dist/cli/src/plugin-store/paths.d.ts +0 -2
- package/dist/cli/src/plugin-store/paths.d.ts.map +1 -1
- package/dist/cli/src/setup.d.ts.map +1 -1
- package/dist/connectors/config.js +9 -9
- package/dist/connectors/index.js +12 -12
- package/dist/connectors/rclone.js +2 -2
- package/dist/connectors/src/connector-prompt.d.ts.map +1 -1
- package/dist/connectors/src/connector-registry.d.ts.map +1 -1
- package/dist/connectors/src/connector-types.d.ts +1 -1
- package/dist/connectors/src/connector-types.d.ts.map +1 -1
- package/dist/connectors/src/index.d.ts +5 -5
- package/dist/connectors/src/index.d.ts.map +1 -1
- package/dist/connectors/src/managed-gitconfig.d.ts.map +1 -1
- package/dist/{connectors-PTCSHCHZ.js → connectors-DY7UBBV5.js} +3 -3
- package/dist/{connectors-PTCSHCHZ.js.map → connectors-DY7UBBV5.js.map} +1 -1
- package/dist/connectors-shared/src/index.d.ts +1 -1
- package/dist/connectors-shared/src/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -8
- package/dist/core/logging.js +1 -1
- package/dist/core/manifest.js +2 -2
- package/dist/core/models.js +1 -1
- package/dist/core/publish-manifest.js +2 -2
- package/dist/core/runtime-assets.js +5 -5
- package/dist/core/src/lock.d.ts +0 -9
- package/dist/core/src/lock.d.ts.map +1 -1
- package/dist/core/src/logging/create-logger.d.ts.map +1 -1
- package/dist/core/src/logging/index.d.ts +11 -11
- package/dist/core/src/logging/index.d.ts.map +1 -1
- package/dist/core/src/logging/log-store.d.ts.map +1 -1
- package/dist/core/src/logging/sinks/no-op-sink.d.ts.map +1 -1
- package/dist/core/src/logging/sinks/sqlite-sink.d.ts.map +1 -1
- package/dist/core/src/manifest.d.ts.map +1 -1
- package/dist/core/src/models.d.ts +6 -0
- package/dist/core/src/models.d.ts.map +1 -1
- package/dist/core/src/publish-manifest.d.ts.map +1 -1
- package/dist/core/src/repo-manager.d.ts +0 -12
- package/dist/core/src/repo-manager.d.ts.map +1 -1
- package/dist/core/src/runtime-assets.d.ts.map +1 -1
- package/dist/core/src/store.d.ts +1 -1
- package/dist/core/src/store.d.ts.map +1 -1
- package/dist/core/src/walker.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +0 -27
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/store.js +1 -1
- package/dist/core/workspace-config.js +4 -4
- package/dist/deploy/index.js +8 -8
- package/dist/discovery/index.js +6 -6
- package/dist/discovery/src/asset-kind-registry.d.ts +1 -1
- package/dist/discovery/src/asset-kind-registry.d.ts.map +1 -1
- package/dist/discovery/src/builtin-providers.d.ts.map +1 -1
- package/dist/discovery/src/discover-manifest.d.ts +1 -1
- package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
- package/dist/discovery/src/discover.d.ts +30 -3
- package/dist/discovery/src/discover.d.ts.map +1 -1
- package/dist/discovery/src/domains.d.ts +144 -0
- package/dist/discovery/src/domains.d.ts.map +1 -0
- package/dist/discovery/src/index.d.ts +9 -7
- package/dist/discovery/src/index.d.ts.map +1 -1
- package/dist/discovery/src/source-config.d.ts +1 -1
- package/dist/discovery/src/source-config.d.ts.map +1 -1
- package/dist/discovery/src/tree-entries.d.ts +13 -1
- package/dist/discovery/src/tree-entries.d.ts.map +1 -1
- package/dist/ensure-sources-6GW6UBXY.js +84 -0
- package/dist/ensure-sources-6GW6UBXY.js.map +1 -0
- package/dist/factory-assets/connectors/deploy/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/deploy/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/deploy.js +12 -12
- package/dist/factory-assets/connectors/devserver/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/devserver/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/devserver.js +12 -12
- package/dist/factory-assets/connectors/flow/adapter.d.ts +16 -2
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +12 -12
- package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +3 -3
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +9 -9
- package/dist/factory-assets/connectors/flow/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/run-flow.js +15 -15
- package/dist/factory-assets/connectors/flow.js +12 -12
- package/dist/factory-assets/connectors/git/driver.d.ts +32 -0
- package/dist/factory-assets/connectors/git/driver.d.ts.map +1 -1
- package/dist/factory-assets/connectors/git.js +12 -12
- package/dist/factory-assets/connectors/gmail/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/gmail/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/gmail.js +12 -12
- package/dist/factory-assets/connectors/googledrive.js +12 -12
- package/dist/factory-assets/connectors/local/driver.d.ts +1 -2
- package/dist/factory-assets/connectors/local/driver.d.ts.map +1 -1
- package/dist/factory-assets/connectors/local.js +12 -12
- package/dist/factory-assets/connectors/mattermost/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/mattermost/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/mattermost.js +12 -12
- package/dist/factory-assets/connectors/memory/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/memory.js +12 -12
- package/dist/factory-assets/connectors/minio/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/minio/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/minio.js +12 -12
- package/dist/factory-assets/connectors/postgres/adapter.d.ts +6 -1
- package/dist/factory-assets/connectors/postgres/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/postgres.js +12 -12
- package/dist/factory-assets/connectors/s3/driver.d.ts.map +1 -1
- package/dist/factory-assets/connectors/s3.js +12 -12
- package/dist/factory-assets/connectors/sharepoint.js +12 -12
- package/dist/factory-assets/connectors/sqlite/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/sqlite/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/sqlite.js +12 -12
- package/dist/factory-assets/connectors/static-server/adapter.d.ts +6 -1
- package/dist/factory-assets/connectors/static-server/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/static-server.js +12 -12
- package/dist/factory-assets/connectors/tunnel/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/tunnel/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/tunnel.js +12 -12
- package/dist/factory-assets/connectors/webdav.js +12 -12
- package/dist/factory-assets/connectors/xstate/adapter.d.ts +1 -1
- package/dist/factory-assets/connectors/xstate/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/xstate-store/adapter.d.ts +7 -0
- package/dist/factory-assets/connectors/xstate-store/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/xstate-store.js +12 -12
- package/dist/factory-assets/connectors/xstate.js +12 -12
- package/dist/flows-JXO3KGC2.js +7 -0
- package/dist/{flows-L3GHQD7F.js.map → flows-JXO3KGC2.js.map} +1 -1
- package/dist/library/index.js +13 -13
- package/dist/library/install.js +9 -9
- package/dist/library/src/config.d.ts.map +1 -1
- package/dist/library/src/index.d.ts +27 -27
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/install/index.d.ts +2 -2
- package/dist/library/src/install/index.d.ts.map +1 -1
- package/dist/library/src/install/install-from-manifest.d.ts +0 -21
- package/dist/library/src/install/install-from-manifest.d.ts.map +1 -1
- package/dist/library/src/knowledge/index.d.ts +2 -2
- package/dist/library/src/knowledge/index.d.ts.map +1 -1
- package/dist/library/src/knowledge/knowledge-kind-provider.d.ts.map +1 -1
- package/dist/library/src/local/library.d.ts +2 -2
- package/dist/library/src/local/library.d.ts.map +1 -1
- package/dist/library/src/local/store-paths.d.ts.map +1 -1
- package/dist/library/src/pin-resolver.d.ts.map +1 -1
- package/dist/library/src/preset/apply.d.ts +2 -2
- package/dist/library/src/preset/apply.d.ts.map +1 -1
- package/dist/library/src/preset/index.d.ts +10 -10
- package/dist/library/src/preset/index.d.ts.map +1 -1
- package/dist/library/src/preset/parse.d.ts.map +1 -1
- package/dist/library/src/preset/resolve-item.d.ts +1 -1
- package/dist/library/src/preset/resolve-item.d.ts.map +1 -1
- package/dist/library/src/preset/upgrade.d.ts +1 -1
- package/dist/library/src/preset/upgrade.d.ts.map +1 -1
- package/dist/library/src/remote/index.d.ts +5 -5
- package/dist/library/src/remote/index.d.ts.map +1 -1
- package/dist/library/src/remote/remote-catalog-source.d.ts +1 -1
- package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
- package/dist/library/src/remote/rest-catalog-source.d.ts +1 -1
- package/dist/library/src/remote/rest-catalog-source.d.ts.map +1 -1
- package/dist/library/src/sync/driver.d.ts +1 -1
- package/dist/library/src/sync/driver.d.ts.map +1 -1
- package/dist/library/src/sync/git-driver.d.ts +1 -1
- package/dist/library/src/sync/git-driver.d.ts.map +1 -1
- package/dist/library/src/sync/local-driver.d.ts +1 -1
- package/dist/library/src/sync/local-driver.d.ts.map +1 -1
- package/dist/library/src/workspace-config.d.ts +0 -9
- package/dist/library/src/workspace-config.d.ts.map +1 -1
- package/dist/open-library-SH6NCJM2.js +23 -0
- package/dist/{open-library-3VJY74LQ.js.map → open-library-SH6NCJM2.js.map} +1 -1
- package/dist/paths-YSXGJ7TO.js +15 -0
- package/dist/{paths-CE66L3AL.js.map → paths-YSXGJ7TO.js.map} +1 -1
- package/dist/plugin-registry/src/targets.d.ts +2 -3
- package/dist/plugin-registry/src/targets.d.ts.map +1 -1
- package/dist/{plugin-store-GL5VKYUA.js → plugin-store-2KMDUF7R.js} +11 -14
- package/dist/plugin-store-2KMDUF7R.js.map +1 -0
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/src/asset-kind-registry.d.ts +1 -1
- package/dist/plugins/src/asset-kind-registry.d.ts.map +1 -1
- package/dist/plugins/src/catalog-source.d.ts +5 -0
- package/dist/plugins/src/catalog-source.d.ts.map +1 -1
- package/dist/plugins/src/index.d.ts +5 -5
- package/dist/plugins/src/index.d.ts.map +1 -1
- package/dist/{provider-GAWKFQ3T.js → provider-ZPT443IX.js} +18 -16
- package/dist/provider-ZPT443IX.js.map +1 -0
- package/dist/resolver/index.js +1 -1
- package/dist/resolver/src/preamble.d.ts.map +1 -1
- package/dist/runner/index.js +17 -17
- package/dist/runner/src/builtin-capabilities.d.ts +12 -0
- package/dist/runner/src/builtin-capabilities.d.ts.map +1 -1
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/runner/src/compaction/index.d.ts +0 -4
- package/dist/runner/src/compaction/index.d.ts.map +1 -1
- package/dist/runner/src/compaction/orchestrator.d.ts +23 -2
- package/dist/runner/src/compaction/orchestrator.d.ts.map +1 -1
- package/dist/runner/src/composition/compile.d.ts.map +1 -1
- package/dist/runner/src/composition/index.d.ts +4 -4
- package/dist/runner/src/composition/index.d.ts.map +1 -1
- package/dist/runner/src/composition/resolve.d.ts.map +1 -1
- package/dist/runner/src/composition/types.d.ts +2 -2
- package/dist/runner/src/composition/types.d.ts.map +1 -1
- package/dist/runner/src/config.d.ts +2 -2
- package/dist/runner/src/config.d.ts.map +1 -1
- package/dist/runner/src/define-capability.d.ts +1 -1
- package/dist/runner/src/define-capability.d.ts.map +1 -1
- package/dist/runner/src/external-mcp.d.ts.map +1 -1
- package/dist/runner/src/index.d.ts +9 -9
- package/dist/runner/src/index.d.ts.map +1 -1
- package/dist/runner/src/logging-bootstrap.d.ts +1 -1
- package/dist/runner/src/logging-bootstrap.d.ts.map +1 -1
- package/dist/runner/src/manifest.d.ts.map +1 -1
- package/dist/runner/src/resource-handler.d.ts.map +1 -1
- package/dist/runner/src/resources.d.ts.map +1 -1
- package/dist/runner/src/runner-capabilities.d.ts +25 -0
- package/dist/runner/src/runner-capabilities.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/runner/src/session-builder.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +12 -12
- package/dist/sdk/bridge.js +4 -4
- package/dist/sdk/core.js +8 -8
- package/dist/sdk/flow.js +9 -9
- package/dist/sdk/index.js +92 -76
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +1 -1
- package/dist/sdk/runner.js +17 -17
- package/dist/sdk/session.js +1 -1
- package/dist/sdk/src/agent-session.d.ts +8 -0
- package/dist/sdk/src/agent-session.d.ts.map +1 -1
- package/dist/sdk/src/factory.d.ts.map +1 -1
- package/dist/sdk/src/index.d.ts +2 -2
- package/dist/sdk/src/index.d.ts.map +1 -1
- package/dist/sdk/store.js +1 -1
- package/dist/sdk/telemetry.js +1 -1
- package/dist/secrets/index.js +2 -2
- package/dist/secrets/src/index.d.ts +8 -8
- package/dist/secrets/src/index.d.ts.map +1 -1
- package/dist/secrets/src/lifecycle.d.ts +1 -2
- package/dist/secrets/src/lifecycle.d.ts.map +1 -1
- package/dist/secrets/src/providers/env.d.ts +1 -1
- package/dist/secrets/src/providers/env.d.ts.map +1 -1
- package/dist/secrets/src/providers/local.d.ts +1 -1
- package/dist/secrets/src/providers/local.d.ts.map +1 -1
- package/dist/secrets/src/router.d.ts +1 -1
- package/dist/secrets/src/router.d.ts.map +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/src/mentions.d.ts.map +1 -1
- package/dist/{sessions-2IUX4P3P.js → sessions-DICDEONJ.js} +3 -3
- package/dist/{sessions-2IUX4P3P.js.map → sessions-DICDEONJ.js.map} +1 -1
- package/dist/{setup-4YMN2BDU.js → setup-6MQ6QWML.js} +198 -175
- package/dist/setup-6MQ6QWML.js.map +1 -0
- package/dist/skills-BMI6H7HI.js +7 -0
- package/dist/{skills-6HJ3WINN.js.map → skills-BMI6H7HI.js.map} +1 -1
- package/dist/store/index.js +1 -1
- package/dist/store/src/index.d.ts +1 -1
- package/dist/store/src/index.d.ts.map +1 -1
- package/dist/store/src/resource-client.d.ts +1 -1
- package/dist/store/src/resource-client.d.ts.map +1 -1
- package/dist/store/src/vue.d.ts.map +1 -1
- package/dist/store/vue.js.map +1 -1
- package/dist/store-client-LDYF4TK4.js +15 -0
- package/dist/{store-client-BCP5LCAZ.js.map → store-client-LDYF4TK4.js.map} +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/src/context.d.ts.map +1 -1
- package/dist/telemetry/src/index.d.ts +3 -3
- package/dist/telemetry/src/index.d.ts.map +1 -1
- package/dist/telemetry/src/otel/gen-ai.d.ts +1 -1
- package/dist/telemetry/src/otel/gen-ai.d.ts.map +1 -1
- package/dist/telemetry/src/otel/provider.d.ts.map +1 -1
- package/dist/transport/src/index.d.ts +1 -1
- package/dist/transport/src/index.d.ts.map +1 -1
- package/dist/tui/index.js +251 -218
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/src/index.d.ts +2 -2
- package/dist/tui/src/index.d.ts.map +1 -1
- package/dist/tui/src/repl-renderer.d.ts.map +1 -1
- package/dist/tui/src/repl-session.d.ts +1 -1
- package/dist/tui/src/repl-session.d.ts.map +1 -1
- package/dist/tui/src/repl.d.ts.map +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/envelope.d.ts +1 -1
- package/dist/types/src/envelope.d.ts.map +1 -1
- package/dist/types/src/manifests/agent.d.ts.map +1 -1
- package/dist/types/src/manifests/connector.d.ts.map +1 -1
- package/dist/types/src/manifests/contract.d.ts.map +1 -1
- package/dist/types/src/manifests/index.d.ts +16 -16
- package/dist/types/src/manifests/index.d.ts.map +1 -1
- package/dist/types/src/manifests/mcp-server.d.ts.map +1 -1
- package/dist/types/src/manifests/mount.d.ts.map +1 -1
- package/dist/types/src/manifests/persona.d.ts.map +1 -1
- package/dist/types/src/manifests/preset.d.ts +0 -18
- package/dist/types/src/manifests/preset.d.ts.map +1 -1
- package/dist/types/src/manifests/prompt.d.ts.map +1 -1
- package/dist/types/src/manifests/ruleset.d.ts.map +1 -1
- package/dist/types/src/manifests/skill.d.ts.map +1 -1
- package/dist/validator-VF5M764T.js +5 -0
- package/dist/{validator-47SQUW3J.js.map → validator-VF5M764T.js.map} +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +6 -6
- package/dist/workspace-plugin/adapters/omp.js +76 -69
- package/dist/workspace-plugin/adapters/omp.js.map +1 -1
- package/dist/workspace-plugin/index.js +2 -2
- package/dist/workspace-plugin/src/adapters/omp.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/connectors.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/flows.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/sessions.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/skills.d.ts.map +1 -1
- package/dist/workspace-plugin/src/tools/validator.d.ts.map +1 -1
- package/package.json +1 -2
- package/dist/asset-feeds-2V7LZIG7.js.map +0 -1
- package/dist/chunk-24UIWON4.js.map +0 -1
- package/dist/chunk-2FS4FPZ7.js.map +0 -1
- package/dist/chunk-3QQSSRYN.js.map +0 -1
- package/dist/chunk-5YO3H5Q4.js.map +0 -1
- package/dist/chunk-7GWYRVAX.js.map +0 -1
- package/dist/chunk-AKSR434V.js.map +0 -1
- package/dist/chunk-DVMSGGCM.js.map +0 -1
- package/dist/chunk-FQHHCICT.js.map +0 -1
- package/dist/chunk-IEJ5IOC3.js.map +0 -1
- package/dist/chunk-IERB3UXJ.js.map +0 -1
- package/dist/chunk-JIQLLZ65.js.map +0 -1
- package/dist/chunk-M4KLIEIJ.js.map +0 -1
- package/dist/chunk-MIH45XPY.js.map +0 -1
- package/dist/chunk-MLJKA3S6.js.map +0 -1
- package/dist/chunk-OTFG5Q7H.js.map +0 -1
- package/dist/chunk-PAYAFNXG.js.map +0 -1
- package/dist/chunk-QDJKE557.js.map +0 -1
- package/dist/chunk-SF4GWVR5.js.map +0 -1
- package/dist/chunk-TV72ZJYN.js.map +0 -1
- package/dist/chunk-UIZKIUC6.js.map +0 -1
- package/dist/chunk-W7W66MDZ.js.map +0 -1
- package/dist/chunk-Z3XXKR5E.js.map +0 -1
- package/dist/chunk-ZONCCO5N.js.map +0 -1
- package/dist/ensure-sources-BNSRO5G7.js +0 -74
- package/dist/ensure-sources-BNSRO5G7.js.map +0 -1
- package/dist/flows-L3GHQD7F.js +0 -7
- package/dist/open-library-3VJY74LQ.js +0 -23
- package/dist/paths-CE66L3AL.js +0 -15
- package/dist/plugin-store-GL5VKYUA.js.map +0 -1
- package/dist/provider-GAWKFQ3T.js.map +0 -1
- package/dist/setup-4YMN2BDU.js.map +0 -1
- package/dist/skills-6HJ3WINN.js +0 -7
- package/dist/store-client-BCP5LCAZ.js +0 -15
- package/dist/validator-47SQUW3J.js +0 -5
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { WorkspacePlugin } from './chunk-
|
|
1
|
+
import { WorkspacePlugin } from './chunk-5MJJYIL4.js';
|
|
2
2
|
import { WebSocketServerTransport } from './chunk-WQ7DE5UC.js';
|
|
3
3
|
import { assembleSystemPrompt, buildCapabilitiesPromptSection } from './chunk-W3UDISS2.js';
|
|
4
4
|
import { PROTOCOL_VERSION } from './chunk-TDSRLMDB.js';
|
|
5
|
-
import { EventNormalizer } from './chunk-
|
|
6
|
-
import { classifyClaudeSdkError } from './chunk-
|
|
7
|
-
import { createDriver } from './chunk-
|
|
5
|
+
import { EventNormalizer } from './chunk-5XAQDOMU.js';
|
|
6
|
+
import { classifyClaudeSdkError } from './chunk-NGJ6E344.js';
|
|
7
|
+
import { createDriver } from './chunk-IZIFMNTI.js';
|
|
8
8
|
import { deployCatalogEntry, undeployCatalogEntry } from './chunk-LV2HPH3C.js';
|
|
9
|
-
import { registerBuiltinConnectors, findMissingPackages, installNpmPackages, ConnectorManager, ConnectorStartupError,
|
|
10
|
-
import { loadConnectorDeclarations, PreMintedSecretProvider, InMemorySecretProvider } from './chunk-
|
|
9
|
+
import { registerBuiltinConnectors, buildConnectorPromptSection, findMissingPackages, installNpmPackages, ConnectorManager, ConnectorStartupError, buildSdkConnectorTools } from './chunk-FRRTUVUM.js';
|
|
10
|
+
import { loadConnectorDeclarations, PreMintedSecretProvider, InMemorySecretProvider } from './chunk-DAI754GK.js';
|
|
11
11
|
import { renderStimulusPrompt, buildOrchestratorPrompt } from './chunk-DEYYRFQU.js';
|
|
12
|
-
import { resolveSettings, resolveApiKey, providerEnvKey } from './chunk-
|
|
13
|
-
import { resolveRuntimeAssets } from './chunk-
|
|
14
|
-
import { resolveSkWorkspaceConfig, resolveAgentDir, stageMaterializedSkills, loadMcpServerDeclarations, COMPACTION_DEFAULTS, validateAssetRecipeAttr } from './chunk-
|
|
15
|
-
import { parseAssetRef } from './chunk-
|
|
12
|
+
import { resolveSettings, resolveApiKey, providerEnvKey } from './chunk-YBJLYT4S.js';
|
|
13
|
+
import { resolveRuntimeAssets } from './chunk-2VKVWN5E.js';
|
|
14
|
+
import { resolveSkWorkspaceConfig, resolveAgentDir, stageMaterializedSkills, loadMcpServerDeclarations, COMPACTION_DEFAULTS, validateAssetRecipeAttr } from './chunk-5DQOZFWJ.js';
|
|
15
|
+
import { parseAssetRef } from './chunk-LFDZYCEO.js';
|
|
16
16
|
import { resolveDriverPaths } from './chunk-K5GBV4SA.js';
|
|
17
|
-
import { getLogStore, resolveLogStoreConfig, createLogStoreFromConfig, OnLogBridgeSink, WsLogSink, registerLogStore, resetLogStore, createLogger } from './chunk-
|
|
17
|
+
import { getLogStore, resolveLogStoreConfig, createLogStoreFromConfig, OnLogBridgeSink, WsLogSink, registerLogStore, resetLogStore, createLogger } from './chunk-JB35MVBN.js';
|
|
18
18
|
import { __require } from './chunk-NSBPE2FW.js';
|
|
19
19
|
import pc from 'picocolors';
|
|
20
20
|
import { Marked } from 'marked';
|
|
@@ -183,48 +183,6 @@ var MarkdownStreamer = class {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
};
|
|
186
|
-
|
|
187
|
-
// runner/src/context.ts
|
|
188
|
-
var PLATFORM_DESCRIPTIONS = {
|
|
189
|
-
skaile: "You are an AI assistant operating within the Skaile platform, an enterprise AI workspace. Users interact with you through a chat interface in their browser."
|
|
190
|
-
};
|
|
191
|
-
function buildContextSection(context) {
|
|
192
|
-
if (!context) return void 0;
|
|
193
|
-
const hasAny = context.platform !== void 0 || context.multi_user !== void 0 || context.session_model !== void 0;
|
|
194
|
-
if (!hasAny) return void 0;
|
|
195
|
-
const lines = ["## Platform", ""];
|
|
196
|
-
if (context.platform) {
|
|
197
|
-
const description = PLATFORM_DESCRIPTIONS[context.platform];
|
|
198
|
-
if (description) {
|
|
199
|
-
lines.push(description);
|
|
200
|
-
} else {
|
|
201
|
-
lines.push(`You are running inside the ${context.platform} platform.`);
|
|
202
|
-
}
|
|
203
|
-
lines.push("");
|
|
204
|
-
}
|
|
205
|
-
if (context.multi_user === true) {
|
|
206
|
-
lines.push(
|
|
207
|
-
"Multiple users may participate in the same session. Be aware that messages in this conversation may come from different users."
|
|
208
|
-
);
|
|
209
|
-
lines.push("");
|
|
210
|
-
} else if (context.multi_user === false) {
|
|
211
|
-
lines.push("This is a single-user session.");
|
|
212
|
-
lines.push("");
|
|
213
|
-
}
|
|
214
|
-
if (context.session_model === "persistent") {
|
|
215
|
-
lines.push(
|
|
216
|
-
"Your work persists across interactions. Files you create or modify in the workspace are saved and available in future sessions."
|
|
217
|
-
);
|
|
218
|
-
lines.push("");
|
|
219
|
-
} else if (context.session_model === "ephemeral") {
|
|
220
|
-
lines.push(
|
|
221
|
-
"This session is ephemeral. Work you do does not persist beyond the current interaction."
|
|
222
|
-
);
|
|
223
|
-
lines.push("");
|
|
224
|
-
}
|
|
225
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
226
|
-
return lines.join("\n");
|
|
227
|
-
}
|
|
228
186
|
function defineCapability(args) {
|
|
229
187
|
const inputSchema = z5.toJSONSchema(args.input, {
|
|
230
188
|
target: "draft-07",
|
|
@@ -629,6 +587,48 @@ async function openLocalIndexIfAvailable(log) {
|
|
|
629
587
|
return STUB_LIBRARY;
|
|
630
588
|
}
|
|
631
589
|
}
|
|
590
|
+
|
|
591
|
+
// runner/src/context.ts
|
|
592
|
+
var PLATFORM_DESCRIPTIONS = {
|
|
593
|
+
skaile: "You are an AI assistant operating within the Skaile platform, an enterprise AI workspace. Users interact with you through a chat interface in their browser."
|
|
594
|
+
};
|
|
595
|
+
function buildContextSection(context) {
|
|
596
|
+
if (!context) return void 0;
|
|
597
|
+
const hasAny = context.platform !== void 0 || context.multi_user !== void 0 || context.session_model !== void 0;
|
|
598
|
+
if (!hasAny) return void 0;
|
|
599
|
+
const lines = ["## Platform", ""];
|
|
600
|
+
if (context.platform) {
|
|
601
|
+
const description = PLATFORM_DESCRIPTIONS[context.platform];
|
|
602
|
+
if (description) {
|
|
603
|
+
lines.push(description);
|
|
604
|
+
} else {
|
|
605
|
+
lines.push(`You are running inside the ${context.platform} platform.`);
|
|
606
|
+
}
|
|
607
|
+
lines.push("");
|
|
608
|
+
}
|
|
609
|
+
if (context.multi_user === true) {
|
|
610
|
+
lines.push(
|
|
611
|
+
"Multiple users may participate in the same session. Be aware that messages in this conversation may come from different users."
|
|
612
|
+
);
|
|
613
|
+
lines.push("");
|
|
614
|
+
} else if (context.multi_user === false) {
|
|
615
|
+
lines.push("This is a single-user session.");
|
|
616
|
+
lines.push("");
|
|
617
|
+
}
|
|
618
|
+
if (context.session_model === "persistent") {
|
|
619
|
+
lines.push(
|
|
620
|
+
"Your work persists across interactions. Files you create or modify in the workspace are saved and available in future sessions."
|
|
621
|
+
);
|
|
622
|
+
lines.push("");
|
|
623
|
+
} else if (context.session_model === "ephemeral") {
|
|
624
|
+
lines.push(
|
|
625
|
+
"This session is ephemeral. Work you do does not persist beyond the current interaction."
|
|
626
|
+
);
|
|
627
|
+
lines.push("");
|
|
628
|
+
}
|
|
629
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
630
|
+
return lines.join("\n");
|
|
631
|
+
}
|
|
632
632
|
var execFileAsync = promisify(execFile);
|
|
633
633
|
var PROBE_TIMEOUT_MS = 1500;
|
|
634
634
|
var VERSION_PROBES = [
|
|
@@ -893,63 +893,72 @@ function resolveExternalMcpDeclarations(projectDir, sessionId) {
|
|
|
893
893
|
mcpLog.warn("MCP server skipped: reserved id", { id: decl.id });
|
|
894
894
|
continue;
|
|
895
895
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
try {
|
|
900
|
-
const recipeKey = decl.recipe.flake ? `mcps.${decl.id}` : decl.recipe.attr ?? `mcps.${decl.id}`;
|
|
901
|
-
const outPath = resolveRecipePath(nixFlakeRef, recipeKey, recipeCache);
|
|
902
|
-
const outPaths = /* @__PURE__ */ new Map([[decl.id, outPath]]);
|
|
903
|
-
const resolvedCommand = decl.command ? substituteRecipeTemplating(decl.command, outPaths) : decl.command;
|
|
904
|
-
const resolvedArgs = decl.args?.map((a) => substituteRecipeTemplating(a, outPaths));
|
|
905
|
-
const resolvedEnv = substituteRecipeMap(decl.env, outPaths);
|
|
906
|
-
resolvedDecl = {
|
|
907
|
-
...decl,
|
|
908
|
-
command: resolvedCommand,
|
|
909
|
-
args: resolvedArgs,
|
|
910
|
-
env: resolvedEnv
|
|
911
|
-
};
|
|
912
|
-
const recipeLog = createLogger({
|
|
913
|
-
kind: "mcp",
|
|
914
|
-
subkind: decl.id,
|
|
915
|
-
instance: "recipe-resolve"
|
|
916
|
-
});
|
|
917
|
-
const allResolved = [
|
|
918
|
-
resolvedCommand ?? "",
|
|
919
|
-
...resolvedArgs ?? [],
|
|
920
|
-
...Object.values(resolvedEnv ?? {})
|
|
921
|
-
];
|
|
922
|
-
for (const v of allResolved) {
|
|
923
|
-
if (v.includes("${recipe:")) {
|
|
924
|
-
recipeLog.warn("unresolved recipe marker after substitution", {
|
|
925
|
-
value: v,
|
|
926
|
-
declId: decl.id
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
const missingPaths = validateSubstitutedPaths(resolvedCommand, resolvedArgs);
|
|
931
|
-
if (missingPaths.length > 0) {
|
|
932
|
-
recipeLog.error("MCP server skipped: substituted path does not exist", void 0, {
|
|
933
|
-
id: decl.id,
|
|
934
|
-
...recipeLogKey,
|
|
935
|
-
missing: missingPaths
|
|
936
|
-
});
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
recipeLog.info("recipe substitution complete", { ...recipeLogKey, outPath });
|
|
940
|
-
} catch (err) {
|
|
941
|
-
createLogger({ kind: "mcp", subkind: decl.id, instance: "recipe-resolve" }).error(
|
|
942
|
-
"MCP server skipped: recipe resolution failed",
|
|
943
|
-
err,
|
|
944
|
-
{ id: decl.id, ...recipeLogKey, flakeRef: nixFlakeRef }
|
|
945
|
-
);
|
|
946
|
-
continue;
|
|
947
|
-
}
|
|
896
|
+
if (!decl.recipe) {
|
|
897
|
+
resolved.push(decl);
|
|
898
|
+
continue;
|
|
948
899
|
}
|
|
949
|
-
|
|
900
|
+
const resolvedDecl = resolveRecipeDeclaration(decl, nixFlakeRef, recipeCache);
|
|
901
|
+
if (resolvedDecl) resolved.push(resolvedDecl);
|
|
950
902
|
}
|
|
951
903
|
return resolved;
|
|
952
904
|
}
|
|
905
|
+
function resolveRecipeDeclaration(decl, nixFlakeRef, recipeCache) {
|
|
906
|
+
const recipe = decl.recipe;
|
|
907
|
+
const recipeLogKey = recipe.flake ? { flake: recipe.flake } : { attr: recipe.attr };
|
|
908
|
+
try {
|
|
909
|
+
const recipeKey = recipe.flake ? `mcps.${decl.id}` : recipe.attr ?? `mcps.${decl.id}`;
|
|
910
|
+
const outPath = resolveRecipePath(nixFlakeRef, recipeKey, recipeCache);
|
|
911
|
+
const outPaths = /* @__PURE__ */ new Map([[decl.id, outPath]]);
|
|
912
|
+
const resolvedCommand = decl.command ? substituteRecipeTemplating(decl.command, outPaths) : decl.command;
|
|
913
|
+
const resolvedArgs = decl.args?.map((a) => substituteRecipeTemplating(a, outPaths));
|
|
914
|
+
const resolvedEnv = substituteRecipeMap(decl.env, outPaths);
|
|
915
|
+
const resolvedDecl = {
|
|
916
|
+
...decl,
|
|
917
|
+
command: resolvedCommand,
|
|
918
|
+
args: resolvedArgs,
|
|
919
|
+
env: resolvedEnv
|
|
920
|
+
};
|
|
921
|
+
const recipeLog = createLogger({
|
|
922
|
+
kind: "mcp",
|
|
923
|
+
subkind: decl.id,
|
|
924
|
+
instance: "recipe-resolve"
|
|
925
|
+
});
|
|
926
|
+
warnUnresolvedRecipeMarkers(recipeLog, decl.id, resolvedCommand, resolvedArgs, resolvedEnv);
|
|
927
|
+
const missingPaths = validateSubstitutedPaths(resolvedCommand, resolvedArgs);
|
|
928
|
+
if (missingPaths.length > 0) {
|
|
929
|
+
recipeLog.error("MCP server skipped: substituted path does not exist", void 0, {
|
|
930
|
+
id: decl.id,
|
|
931
|
+
...recipeLogKey,
|
|
932
|
+
missing: missingPaths
|
|
933
|
+
});
|
|
934
|
+
return void 0;
|
|
935
|
+
}
|
|
936
|
+
recipeLog.info("recipe substitution complete", { ...recipeLogKey, outPath });
|
|
937
|
+
return resolvedDecl;
|
|
938
|
+
} catch (err) {
|
|
939
|
+
createLogger({ kind: "mcp", subkind: decl.id, instance: "recipe-resolve" }).error(
|
|
940
|
+
"MCP server skipped: recipe resolution failed",
|
|
941
|
+
err,
|
|
942
|
+
{ id: decl.id, ...recipeLogKey, flakeRef: nixFlakeRef }
|
|
943
|
+
);
|
|
944
|
+
return void 0;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function warnUnresolvedRecipeMarkers(recipeLog, declId, resolvedCommand, resolvedArgs, resolvedEnv) {
|
|
948
|
+
const allResolved = [
|
|
949
|
+
resolvedCommand ?? "",
|
|
950
|
+
...resolvedArgs ?? [],
|
|
951
|
+
...Object.values(resolvedEnv ?? {})
|
|
952
|
+
];
|
|
953
|
+
for (const v of allResolved) {
|
|
954
|
+
if (v.includes("${recipe:")) {
|
|
955
|
+
recipeLog.warn("unresolved recipe marker after substitution", {
|
|
956
|
+
value: v,
|
|
957
|
+
declId
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
953
962
|
var PASSTHROUGH_INPUT_ZOD = {
|
|
954
963
|
parse: (v) => v
|
|
955
964
|
};
|
|
@@ -1140,122 +1149,50 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
|
|
|
1140
1149
|
for (const w of runtimeAssets.warnings) {
|
|
1141
1150
|
resLog.warn("runtime asset warning", { code: w.code, message: w.message });
|
|
1142
1151
|
}
|
|
1143
|
-
|
|
1144
|
-
const required = runtimeAssets.requiredNpmDeps.required;
|
|
1145
|
-
if (required.length > 0) {
|
|
1146
|
-
const missing = findMissingPackages(required, projectDir);
|
|
1147
|
-
if (missing.length > 0) {
|
|
1148
|
-
resLog.info("installing missing npm peer deps", { packages: missing });
|
|
1149
|
-
const r = await installNpmPackages(missing, projectDir);
|
|
1150
|
-
if (!r.success) resLog.error("npm install failed", void 0, { output: r.output });
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1152
|
+
await maybeInstallNpmPeerDeps(runtimeAssets, projectDir, resLog);
|
|
1154
1153
|
const connectorDeclarations = loadConnectorDeclarations(projectDir);
|
|
1155
1154
|
let resourceManager = null;
|
|
1156
1155
|
if (connectorDeclarations.length > 0) {
|
|
1157
|
-
resourceManager =
|
|
1158
|
-
|
|
1159
|
-
|
|
1156
|
+
resourceManager = await connectAllConnectors(
|
|
1157
|
+
connectorDeclarations,
|
|
1158
|
+
projectDir,
|
|
1159
|
+
runtimeAssets,
|
|
1160
|
+
secretProvider,
|
|
1160
1161
|
tokenMediator,
|
|
1161
1162
|
preMintedSecrets,
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
failOnReadWriteError: true,
|
|
1167
|
-
deferMounts: deferFilesystemMounts
|
|
1168
|
-
});
|
|
1169
|
-
for (const r of report.results) {
|
|
1170
|
-
if (r.connected) {
|
|
1171
|
-
resLog.info("connector ok", { id: r.id, mountPath: r.mountPath });
|
|
1172
|
-
} else {
|
|
1173
|
-
resLog.error("connector failed", void 0, { id: r.id, error: r.error });
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
} catch (err) {
|
|
1177
|
-
if (err instanceof ConnectorStartupError) {
|
|
1178
|
-
for (const r of err.report.results) {
|
|
1179
|
-
if (r.connected) {
|
|
1180
|
-
resLog.info("connector ok", { id: r.id, mountPath: r.mountPath });
|
|
1181
|
-
} else {
|
|
1182
|
-
resLog.error("connector failed", void 0, { id: r.id, error: r.error });
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
throw err;
|
|
1187
|
-
}
|
|
1163
|
+
deferFilesystemMounts,
|
|
1164
|
+
watch,
|
|
1165
|
+
resLog
|
|
1166
|
+
);
|
|
1188
1167
|
}
|
|
1189
1168
|
const resourcePromptSection = resourceManager ? buildConnectorPromptSection(resourceManager) : "";
|
|
1190
1169
|
let mcpServers;
|
|
1191
1170
|
let workspacePluginToShutdown = null;
|
|
1192
1171
|
let externalMcpManager = null;
|
|
1193
1172
|
if (driverType === "claude-sdk") {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
try {
|
|
1197
|
-
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
1198
|
-
if (server) {
|
|
1199
|
-
mcpServers = { "skaile-connectors": server };
|
|
1200
|
-
connectorsLog.info("server constructed", {
|
|
1201
|
-
connectorCount: resourceManager.listConnectors().length
|
|
1202
|
-
});
|
|
1203
|
-
} else {
|
|
1204
|
-
connectorsLog.debug("server skipped (no tools to expose)");
|
|
1205
|
-
}
|
|
1206
|
-
} catch (err) {
|
|
1207
|
-
connectorsLog.error("server construction failed", err);
|
|
1208
|
-
throw err;
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
const workspacePlugin = new WorkspacePlugin({
|
|
1173
|
+
const inProcess = await buildInProcessMcpServers(
|
|
1174
|
+
resourceManager,
|
|
1212
1175
|
projectDir,
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const wsServer = await workspacePlugin.buildWorkspaceMcpServer();
|
|
1219
|
-
if (wsServer) {
|
|
1220
|
-
mcpServers = { ...mcpServers ?? {}, "skaile-workspace": wsServer };
|
|
1221
|
-
}
|
|
1222
|
-
workspacePluginToShutdown = workspacePlugin;
|
|
1176
|
+
driverType,
|
|
1177
|
+
sessionMeta
|
|
1178
|
+
);
|
|
1179
|
+
mcpServers = inProcess.mcpServers;
|
|
1180
|
+
workspacePluginToShutdown = inProcess.workspacePluginToShutdown;
|
|
1223
1181
|
}
|
|
1224
1182
|
if (capabilityRegistry) {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
);
|
|
1232
|
-
await externalMcpManager.start(resolvedDecls);
|
|
1233
|
-
}
|
|
1183
|
+
externalMcpManager = await startExternalMcpServers(
|
|
1184
|
+
capabilityRegistry,
|
|
1185
|
+
projectDir,
|
|
1186
|
+
secretProvider,
|
|
1187
|
+
sessionMeta
|
|
1188
|
+
);
|
|
1234
1189
|
} else if (driverType === "claude-sdk") {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
for (const decl of resolvedDecls) {
|
|
1242
|
-
const serverConfig = toSdkMcpServerConfig(
|
|
1243
|
-
decl,
|
|
1244
|
-
secretProvider
|
|
1245
|
-
);
|
|
1246
|
-
if (serverConfig) {
|
|
1247
|
-
mcpServers = { ...mcpServers ?? {}, [decl.id]: serverConfig };
|
|
1248
|
-
createLogger({ kind: "mcp", subkind: decl.id }).info("MCP server registered", {
|
|
1249
|
-
transport: decl.transport ?? "stdio",
|
|
1250
|
-
...decl.transport === "stdio" || !decl.transport ? { command: decl.command, args: decl.args } : { url: decl.url }
|
|
1251
|
-
});
|
|
1252
|
-
} else {
|
|
1253
|
-
mcpLog.error("MCP server config invalid (translation returned null)", void 0, {
|
|
1254
|
-
id: decl.id,
|
|
1255
|
-
transport: decl.transport
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1190
|
+
mcpServers = wireExternalMcpServersForSdk(
|
|
1191
|
+
mcpServers,
|
|
1192
|
+
projectDir,
|
|
1193
|
+
secretProvider,
|
|
1194
|
+
sessionMeta
|
|
1195
|
+
);
|
|
1259
1196
|
}
|
|
1260
1197
|
if (!resourceManager && driverType !== "claude-sdk" && !externalMcpManager?.hasServers())
|
|
1261
1198
|
return noopResult;
|
|
@@ -1306,6 +1243,120 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
|
|
|
1306
1243
|
return noopResult;
|
|
1307
1244
|
}
|
|
1308
1245
|
}
|
|
1246
|
+
async function maybeInstallNpmPeerDeps(runtimeAssets, projectDir, resLog) {
|
|
1247
|
+
if (process.env.SKAILE_AUTO_INSTALL_DEPS === "true") {
|
|
1248
|
+
const required = runtimeAssets.requiredNpmDeps.required;
|
|
1249
|
+
if (required.length > 0) {
|
|
1250
|
+
const missing = findMissingPackages(required, projectDir);
|
|
1251
|
+
if (missing.length > 0) {
|
|
1252
|
+
resLog.info("installing missing npm peer deps", { packages: missing });
|
|
1253
|
+
const r = await installNpmPackages(missing, projectDir);
|
|
1254
|
+
if (!r.success) resLog.error("npm install failed", void 0, { output: r.output });
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
function logConnectorReport(results, resLog) {
|
|
1260
|
+
for (const r of results) {
|
|
1261
|
+
if (r.connected) {
|
|
1262
|
+
resLog.info("connector ok", { id: r.id, mountPath: r.mountPath });
|
|
1263
|
+
} else {
|
|
1264
|
+
resLog.error("connector failed", void 0, { id: r.id, error: r.error });
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
async function connectAllConnectors(connectorDeclarations, projectDir, runtimeAssets, secretProvider, tokenMediator, preMintedSecrets, deferFilesystemMounts, watch, resLog) {
|
|
1269
|
+
const resourceManager = new ConnectorManager(projectDir, {
|
|
1270
|
+
secrets: secretProvider,
|
|
1271
|
+
catalogEntries: runtimeAssets.catalogEntries,
|
|
1272
|
+
tokenMediator,
|
|
1273
|
+
preMintedSecrets,
|
|
1274
|
+
onStatusChange: watch?.onConnectorStatus
|
|
1275
|
+
});
|
|
1276
|
+
try {
|
|
1277
|
+
const report = await resourceManager.connectAll(connectorDeclarations, {
|
|
1278
|
+
failOnReadWriteError: true,
|
|
1279
|
+
deferMounts: deferFilesystemMounts
|
|
1280
|
+
});
|
|
1281
|
+
logConnectorReport(report.results, resLog);
|
|
1282
|
+
} catch (err) {
|
|
1283
|
+
if (err instanceof ConnectorStartupError) {
|
|
1284
|
+
logConnectorReport(err.report.results, resLog);
|
|
1285
|
+
}
|
|
1286
|
+
throw err;
|
|
1287
|
+
}
|
|
1288
|
+
return resourceManager;
|
|
1289
|
+
}
|
|
1290
|
+
async function buildInProcessMcpServers(resourceManager, projectDir, driverType, sessionMeta) {
|
|
1291
|
+
let mcpServers;
|
|
1292
|
+
if (resourceManager) {
|
|
1293
|
+
const connectorsLog = createLogger({ kind: "mcp", subkind: "skaile-connectors" });
|
|
1294
|
+
try {
|
|
1295
|
+
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
1296
|
+
if (server) {
|
|
1297
|
+
mcpServers = { "skaile-connectors": server };
|
|
1298
|
+
connectorsLog.info("server constructed", {
|
|
1299
|
+
connectorCount: resourceManager.listConnectors().length
|
|
1300
|
+
});
|
|
1301
|
+
} else {
|
|
1302
|
+
connectorsLog.debug("server skipped (no tools to expose)");
|
|
1303
|
+
}
|
|
1304
|
+
} catch (err) {
|
|
1305
|
+
connectorsLog.error("server construction failed", err);
|
|
1306
|
+
throw err;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
const workspacePlugin = new WorkspacePlugin({
|
|
1310
|
+
projectDir,
|
|
1311
|
+
connectorManager: resourceManager ?? void 0,
|
|
1312
|
+
driver: driverType,
|
|
1313
|
+
provider: sessionMeta?.provider,
|
|
1314
|
+
model: sessionMeta?.model
|
|
1315
|
+
});
|
|
1316
|
+
const wsServer = await workspacePlugin.buildWorkspaceMcpServer();
|
|
1317
|
+
if (wsServer) {
|
|
1318
|
+
mcpServers = { ...mcpServers ?? {}, "skaile-workspace": wsServer };
|
|
1319
|
+
}
|
|
1320
|
+
return { mcpServers, workspacePluginToShutdown: workspacePlugin };
|
|
1321
|
+
}
|
|
1322
|
+
async function startExternalMcpServers(capabilityRegistry, projectDir, secretProvider, sessionMeta) {
|
|
1323
|
+
const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
|
|
1324
|
+
if (resolvedDecls.length === 0) return null;
|
|
1325
|
+
const externalMcpManager = new ExternalMcpManager(
|
|
1326
|
+
capabilityRegistry,
|
|
1327
|
+
secretProvider,
|
|
1328
|
+
sessionMeta?.sessionId
|
|
1329
|
+
);
|
|
1330
|
+
await externalMcpManager.start(resolvedDecls);
|
|
1331
|
+
return externalMcpManager;
|
|
1332
|
+
}
|
|
1333
|
+
function wireExternalMcpServersForSdk(mcpServers, projectDir, secretProvider, sessionMeta) {
|
|
1334
|
+
const mcpLog = createLogger({
|
|
1335
|
+
kind: "mcp",
|
|
1336
|
+
subkind: "wiring",
|
|
1337
|
+
instance: sessionMeta?.sessionId ?? "no-session"
|
|
1338
|
+
});
|
|
1339
|
+
const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
|
|
1340
|
+
for (const decl of resolvedDecls) {
|
|
1341
|
+
const serverConfig = toSdkMcpServerConfig(
|
|
1342
|
+
decl,
|
|
1343
|
+
secretProvider
|
|
1344
|
+
);
|
|
1345
|
+
if (serverConfig) {
|
|
1346
|
+
mcpServers = { ...mcpServers ?? {}, [decl.id]: serverConfig };
|
|
1347
|
+
createLogger({ kind: "mcp", subkind: decl.id }).info("MCP server registered", {
|
|
1348
|
+
transport: decl.transport ?? "stdio",
|
|
1349
|
+
...decl.transport === "stdio" || !decl.transport ? { command: decl.command, args: decl.args } : { url: decl.url }
|
|
1350
|
+
});
|
|
1351
|
+
} else {
|
|
1352
|
+
mcpLog.error("MCP server config invalid (translation returned null)", void 0, {
|
|
1353
|
+
id: decl.id,
|
|
1354
|
+
transport: decl.transport
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
return mcpServers;
|
|
1359
|
+
}
|
|
1309
1360
|
function toSdkMcpServerConfig(decl, secrets) {
|
|
1310
1361
|
const transport = decl.transport ?? "stdio";
|
|
1311
1362
|
if (transport === "stdio") {
|
|
@@ -1401,45 +1452,17 @@ async function createAgentSession(config) {
|
|
|
1401
1452
|
"externalPromptFile is deprecated and no longer injected; use agent.prompt-extensions in skaile.yaml instead"
|
|
1402
1453
|
);
|
|
1403
1454
|
}
|
|
1404
|
-
|
|
1405
|
-
if (agentDir && manifest?.composes?.length) {
|
|
1406
|
-
composition = await resolveAgentComposition({
|
|
1407
|
-
agentDir,
|
|
1408
|
-
manifest,
|
|
1409
|
-
log: sessionLog
|
|
1410
|
-
});
|
|
1411
|
-
} else if (manifest?.composes?.length && !agentDir) {
|
|
1412
|
-
sessionLog.debug("skipping composition resolution \u2014 no agentDir available");
|
|
1413
|
-
}
|
|
1455
|
+
const composition = await resolveSessionComposition(agentDir, manifest, projectDir, sessionLog);
|
|
1414
1456
|
const compositionBlocks = composition?.inlineBlocks ?? [];
|
|
1415
1457
|
const additionalSections = config.additionalPromptSections ?? [];
|
|
1416
|
-
const systemPromptSections =
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
systemPromptSections.push({
|
|
1425
|
-
label: "prompt-extensions",
|
|
1426
|
-
value: ext.value,
|
|
1427
|
-
source: ext.source
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
for (const block of compositionBlocks) {
|
|
1431
|
-
systemPromptSections.push({
|
|
1432
|
-
label: "composition",
|
|
1433
|
-
value: block.content,
|
|
1434
|
-
source: block.ref
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
additionalSections.forEach((value, idx) => {
|
|
1438
|
-
systemPromptSections.push({
|
|
1439
|
-
label: "additional",
|
|
1440
|
-
value,
|
|
1441
|
-
source: `additional[${idx}]`
|
|
1442
|
-
});
|
|
1458
|
+
const systemPromptSections = buildSystemPromptSections({
|
|
1459
|
+
contextSection,
|
|
1460
|
+
userPromptSection,
|
|
1461
|
+
environmentSection,
|
|
1462
|
+
resourcePromptSection,
|
|
1463
|
+
promptExtensions,
|
|
1464
|
+
compositionBlocks,
|
|
1465
|
+
additionalSections
|
|
1443
1466
|
});
|
|
1444
1467
|
const systemPrompt = assembleSystemPrompt([
|
|
1445
1468
|
contextSection,
|
|
@@ -1450,36 +1473,26 @@ async function createAgentSession(config) {
|
|
|
1450
1473
|
...compositionBlocks.map((b) => b.content),
|
|
1451
1474
|
...additionalSections
|
|
1452
1475
|
]);
|
|
1453
|
-
|
|
1454
|
-
const length = systemPrompt?.length ?? 0;
|
|
1455
|
-
sessionLog.debug("assembled system prompt", { length });
|
|
1456
|
-
sessionLog.debug("system prompt content", { content: systemPrompt ?? "(empty)" });
|
|
1457
|
-
}
|
|
1476
|
+
logAssembledSystemPrompt(systemPrompt, sessionLog);
|
|
1458
1477
|
const thinking = config.thinking ?? wsConfig.agent_config?.default?.thinking;
|
|
1459
1478
|
const effort = config.effort ?? wsConfig.agent_config?.default?.effort;
|
|
1460
1479
|
const maxTurns = maxTurnsOverride ?? manifest?.runtime?.max_turns;
|
|
1461
|
-
const driver = await
|
|
1462
|
-
|
|
1480
|
+
const driver = await createSessionDriver({
|
|
1481
|
+
config,
|
|
1482
|
+
driverType,
|
|
1483
|
+
agentCwd,
|
|
1463
1484
|
provider,
|
|
1464
|
-
model
|
|
1485
|
+
model,
|
|
1486
|
+
manifest,
|
|
1465
1487
|
maxTurns,
|
|
1466
|
-
|
|
1488
|
+
apiKey,
|
|
1467
1489
|
systemPrompt,
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
// Protocol v2 capability dispatch hooks. When provided, the driver builds
|
|
1475
|
-
// a synthetic `skaile-capabilities` MCP server alongside any existing
|
|
1476
|
-
// mcpServers so capability invocations route through the registry's
|
|
1477
|
-
// `invoke()` (which logs + validates + handles fire-and-forget / render).
|
|
1478
|
-
...config.capabilities ? { capabilities: config.capabilities } : {},
|
|
1479
|
-
...config.aiProviderConfigId ? { aiProviderConfigId: config.aiProviderConfigId } : {},
|
|
1480
|
-
...config.onAuthError ? { onAuthError: config.onAuthError } : {},
|
|
1481
|
-
// Always forward agentDir/agentName for native identity handling
|
|
1482
|
-
...agentDir ? { agentDir, agentName: manifest?.name } : {}
|
|
1490
|
+
thinking,
|
|
1491
|
+
effort,
|
|
1492
|
+
sessionId,
|
|
1493
|
+
resumeSessionId,
|
|
1494
|
+
mcpServers,
|
|
1495
|
+
agentDir
|
|
1483
1496
|
});
|
|
1484
1497
|
if (telemetry && trace) {
|
|
1485
1498
|
driver.setTelemetry(telemetry, trace);
|
|
@@ -1520,8 +1533,110 @@ function loadPromptExtensions(paths, projectDir, log) {
|
|
|
1520
1533
|
}
|
|
1521
1534
|
return result;
|
|
1522
1535
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1536
|
+
async function resolveSessionComposition(agentDir, manifest, projectDir, log) {
|
|
1537
|
+
if (agentDir && manifest?.composes?.length) {
|
|
1538
|
+
return resolveAgentComposition({
|
|
1539
|
+
agentDir,
|
|
1540
|
+
manifest,
|
|
1541
|
+
log
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
if (manifest?.composes?.length && !agentDir) {
|
|
1545
|
+
log.debug("skipping composition resolution \u2014 no agentDir available");
|
|
1546
|
+
}
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
function buildSystemPromptSections(parts) {
|
|
1550
|
+
const {
|
|
1551
|
+
contextSection,
|
|
1552
|
+
userPromptSection,
|
|
1553
|
+
environmentSection,
|
|
1554
|
+
resourcePromptSection,
|
|
1555
|
+
promptExtensions,
|
|
1556
|
+
compositionBlocks,
|
|
1557
|
+
additionalSections
|
|
1558
|
+
} = parts;
|
|
1559
|
+
const systemPromptSections = [];
|
|
1560
|
+
if (contextSection) systemPromptSections.push({ label: "context", value: contextSection });
|
|
1561
|
+
if (userPromptSection) systemPromptSections.push({ label: "prompt", value: userPromptSection });
|
|
1562
|
+
if (environmentSection)
|
|
1563
|
+
systemPromptSections.push({ label: "environment", value: environmentSection });
|
|
1564
|
+
if (resourcePromptSection)
|
|
1565
|
+
systemPromptSections.push({ label: "resources", value: resourcePromptSection });
|
|
1566
|
+
for (const ext of promptExtensions) {
|
|
1567
|
+
systemPromptSections.push({
|
|
1568
|
+
label: "prompt-extensions",
|
|
1569
|
+
value: ext.value,
|
|
1570
|
+
source: ext.source
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
for (const block of compositionBlocks) {
|
|
1574
|
+
systemPromptSections.push({
|
|
1575
|
+
label: "composition",
|
|
1576
|
+
value: block.content,
|
|
1577
|
+
source: block.ref
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
additionalSections.forEach((value, idx) => {
|
|
1581
|
+
systemPromptSections.push({
|
|
1582
|
+
label: "additional",
|
|
1583
|
+
value,
|
|
1584
|
+
source: `additional[${idx}]`
|
|
1585
|
+
});
|
|
1586
|
+
});
|
|
1587
|
+
return systemPromptSections;
|
|
1588
|
+
}
|
|
1589
|
+
function logAssembledSystemPrompt(systemPrompt, log) {
|
|
1590
|
+
if (process.env.SKAILE_LOG_SYSTEM_PROMPT !== "0") {
|
|
1591
|
+
const length = systemPrompt?.length ?? 0;
|
|
1592
|
+
log.debug("assembled system prompt", { length });
|
|
1593
|
+
log.debug("system prompt content", { content: systemPrompt ?? "(empty)" });
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
function createSessionDriver(args) {
|
|
1597
|
+
const {
|
|
1598
|
+
config,
|
|
1599
|
+
driverType,
|
|
1600
|
+
agentCwd,
|
|
1601
|
+
provider,
|
|
1602
|
+
model,
|
|
1603
|
+
manifest,
|
|
1604
|
+
maxTurns,
|
|
1605
|
+
apiKey,
|
|
1606
|
+
systemPrompt,
|
|
1607
|
+
thinking,
|
|
1608
|
+
effort,
|
|
1609
|
+
sessionId,
|
|
1610
|
+
resumeSessionId,
|
|
1611
|
+
mcpServers,
|
|
1612
|
+
agentDir
|
|
1613
|
+
} = args;
|
|
1614
|
+
return createDriver(driverType, {
|
|
1615
|
+
cwd: agentCwd,
|
|
1616
|
+
provider,
|
|
1617
|
+
model: model ?? manifest?.model?.preferred,
|
|
1618
|
+
maxTurns,
|
|
1619
|
+
apiKeys: apiKey ? { [provider ?? "anthropic"]: apiKey } : void 0,
|
|
1620
|
+
systemPrompt,
|
|
1621
|
+
tools: manifest?.tools,
|
|
1622
|
+
...thinking ? { thinking } : {},
|
|
1623
|
+
...effort ? { effort } : {},
|
|
1624
|
+
...sessionId ? { sessionId } : {},
|
|
1625
|
+
...resumeSessionId ? { resumeSessionId } : {},
|
|
1626
|
+
...mcpServers ? { mcpServers } : {},
|
|
1627
|
+
// Protocol v2 capability dispatch hooks. When provided, the driver builds
|
|
1628
|
+
// a synthetic `skaile-capabilities` MCP server alongside any existing
|
|
1629
|
+
// mcpServers so capability invocations route through the registry's
|
|
1630
|
+
// `invoke()` (which logs + validates + handles fire-and-forget / render).
|
|
1631
|
+
...config.capabilities ? { capabilities: config.capabilities } : {},
|
|
1632
|
+
...config.aiProviderConfigId ? { aiProviderConfigId: config.aiProviderConfigId } : {},
|
|
1633
|
+
...config.onAuthError ? { onAuthError: config.onAuthError } : {},
|
|
1634
|
+
// Always forward agentDir/agentName for native identity handling
|
|
1635
|
+
...agentDir ? { agentDir, agentName: manifest?.name } : {}
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// runner/src/agent.ts
|
|
1525
1640
|
var PROMPT_AGENT = `${pc.magenta(pc.bold("agent"))} ${pc.dim("\u25B6")} `;
|
|
1526
1641
|
async function runAgentChat(opts) {
|
|
1527
1642
|
const sessionId = `chat-${Date.now().toString(36)}`;
|
|
@@ -1601,345 +1716,134 @@ async function buildDriver(opts) {
|
|
|
1601
1716
|
onLog: opts.onLog
|
|
1602
1717
|
});
|
|
1603
1718
|
}
|
|
1604
|
-
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
path5,
|
|
1613
|
-
options
|
|
1614
|
-
);
|
|
1615
|
-
break;
|
|
1616
|
-
case "read":
|
|
1617
|
-
data = await manager.read(connectorId, path5);
|
|
1618
|
-
break;
|
|
1619
|
-
case "write":
|
|
1620
|
-
await manager.write(connectorId, path5, content);
|
|
1621
|
-
data = true;
|
|
1622
|
-
break;
|
|
1623
|
-
case "delete":
|
|
1624
|
-
data = await manager.delete(connectorId, path5);
|
|
1625
|
-
break;
|
|
1626
|
-
case "search":
|
|
1627
|
-
data = await manager.search(
|
|
1628
|
-
connectorId,
|
|
1629
|
-
path5,
|
|
1630
|
-
options
|
|
1631
|
-
);
|
|
1632
|
-
break;
|
|
1719
|
+
function keywordSearch(capabilities, query, limit) {
|
|
1720
|
+
const tokens = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
1721
|
+
if (tokens.length === 0) return capabilities.slice(0, limit);
|
|
1722
|
+
const scored = capabilities.map((cap) => {
|
|
1723
|
+
const haystack = [cap.name, cap.description, cap.promptFragment ?? ""].join(" ").toLowerCase();
|
|
1724
|
+
let score = 0;
|
|
1725
|
+
for (const token of tokens) {
|
|
1726
|
+
if (haystack.includes(token)) score += 1;
|
|
1633
1727
|
}
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1728
|
+
return { cap, score };
|
|
1729
|
+
}).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
|
|
1730
|
+
return scored.slice(0, limit).map((x) => x.cap);
|
|
1731
|
+
}
|
|
1732
|
+
function builtinCapabilities(registry) {
|
|
1733
|
+
const listFilterSchema = z5.strictObject({
|
|
1734
|
+
side: z5.enum(["client", "agent"]).optional(),
|
|
1735
|
+
originKind: z5.enum(["framework", "client", "agent", "flow", "skill", "mcp", "connector", "mount"]).optional(),
|
|
1736
|
+
scope: z5.enum(["session", "turn"]).optional()
|
|
1737
|
+
});
|
|
1738
|
+
return [
|
|
1739
|
+
defineCapability({
|
|
1740
|
+
name: "__capabilities.list",
|
|
1741
|
+
displayName: "List capabilities",
|
|
1742
|
+
description: "List available capabilities in this session. Optionally filter by side, origin kind, or scope.",
|
|
1743
|
+
side: "agent",
|
|
1744
|
+
origin: { kind: "framework" },
|
|
1745
|
+
kind: "query",
|
|
1746
|
+
userInvokable: false,
|
|
1747
|
+
input: listFilterSchema,
|
|
1748
|
+
handler: async (args) => registry.list({
|
|
1749
|
+
side: args.side,
|
|
1750
|
+
origin: args.originKind,
|
|
1751
|
+
scope: args.scope
|
|
1752
|
+
})
|
|
1753
|
+
}),
|
|
1754
|
+
defineCapability({
|
|
1755
|
+
name: "__capabilities.search",
|
|
1756
|
+
displayName: "Search capabilities",
|
|
1757
|
+
description: "Keyword search across capability names, descriptions, and prompt fragments. Returns the top matches by score (substring match count).",
|
|
1758
|
+
side: "agent",
|
|
1759
|
+
origin: { kind: "framework" },
|
|
1760
|
+
kind: "query",
|
|
1761
|
+
userInvokable: false,
|
|
1762
|
+
input: z5.strictObject({
|
|
1763
|
+
query: z5.string(),
|
|
1764
|
+
limit: z5.number().int().positive().optional()
|
|
1765
|
+
}),
|
|
1766
|
+
handler: async ({ query, limit }) => keywordSearch(registry.list(), query, limit ?? 10)
|
|
1767
|
+
}),
|
|
1768
|
+
defineCapability({
|
|
1769
|
+
name: "__capabilities.describe",
|
|
1770
|
+
displayName: "Describe capability",
|
|
1771
|
+
description: "Return the full Capability descriptor for a single capability by name.",
|
|
1772
|
+
side: "agent",
|
|
1773
|
+
origin: { kind: "framework" },
|
|
1774
|
+
kind: "query",
|
|
1775
|
+
userInvokable: false,
|
|
1776
|
+
input: z5.strictObject({ name: z5.string() }),
|
|
1777
|
+
handler: async ({ name }) => {
|
|
1778
|
+
const cap = registry.resolve(name);
|
|
1779
|
+
if (!cap) return null;
|
|
1780
|
+
const wire = registry.list().find((c) => c.name === name);
|
|
1781
|
+
return wire ?? null;
|
|
1782
|
+
}
|
|
1783
|
+
})
|
|
1784
|
+
];
|
|
1785
|
+
}
|
|
1786
|
+
function bootstrapCapabilityRegistry(registry) {
|
|
1787
|
+
for (const cap of builtinCapabilities(registry)) {
|
|
1788
|
+
registry.register(cap, "agent");
|
|
1643
1789
|
}
|
|
1644
1790
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1791
|
+
var DEFAULT_AUDIENCE = ["llm", "user"];
|
|
1792
|
+
function audienceOf(cap) {
|
|
1793
|
+
return cap.audience ?? DEFAULT_AUDIENCE;
|
|
1647
1794
|
}
|
|
1648
|
-
function
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1795
|
+
function hasAudience(cap, target) {
|
|
1796
|
+
return audienceOf(cap).includes(target);
|
|
1797
|
+
}
|
|
1798
|
+
var ORIGIN_PROMPT_ORDER = [
|
|
1799
|
+
"framework",
|
|
1800
|
+
"client",
|
|
1801
|
+
"agent",
|
|
1802
|
+
"flow",
|
|
1803
|
+
"skill",
|
|
1804
|
+
"mcp",
|
|
1805
|
+
"connector",
|
|
1806
|
+
"mount"
|
|
1807
|
+
];
|
|
1808
|
+
var CapabilityRegistry = class {
|
|
1809
|
+
byName = /* @__PURE__ */ new Map();
|
|
1810
|
+
logger;
|
|
1811
|
+
remoteInvoker;
|
|
1812
|
+
constructor(logger) {
|
|
1813
|
+
this.logger = logger ?? createLogger({ kind: "runner", subkind: "capability-registry" });
|
|
1814
|
+
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Install the platform-bound invoker used by {@link invokeRemote}.
|
|
1817
|
+
*
|
|
1818
|
+
* The runner wires this up after the transport is connected and the
|
|
1819
|
+
* per-session pending-call map is in place. Idempotent — repeated calls
|
|
1820
|
+
* replace the prior invoker.
|
|
1821
|
+
*
|
|
1822
|
+
* @category Runtime
|
|
1823
|
+
* @since 3.0.0
|
|
1824
|
+
*/
|
|
1825
|
+
setRemoteInvoker(invoker) {
|
|
1826
|
+
this.remoteInvoker = invoker;
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Invoke a `host.*` (or other peer-side) capability on the platform.
|
|
1830
|
+
*
|
|
1831
|
+
* Composes a `capability_invoked` event under the hood and resolves with
|
|
1832
|
+
* the matching `capability_result` payload. Infrastructure failures
|
|
1833
|
+
* (timeout, schema mismatch, transport disconnect) reject with `Error`;
|
|
1834
|
+
* domain failures (e.g. `{ ok: false, code }` for credential mints) ride
|
|
1835
|
+
* in the resolved payload.
|
|
1836
|
+
*
|
|
1837
|
+
* @category Runtime
|
|
1838
|
+
* @since 3.0.0
|
|
1839
|
+
*/
|
|
1840
|
+
async invokeRemote(name, input) {
|
|
1841
|
+
if (!this.remoteInvoker) {
|
|
1842
|
+
throw new Error(
|
|
1843
|
+
`Capability '${name}' invoked remotely but no remote invoker is wired. Call CapabilityRegistry.setRemoteInvoker(...) before dispatch.`
|
|
1844
|
+
);
|
|
1656
1845
|
}
|
|
1657
|
-
return
|
|
1658
|
-
} catch {
|
|
1659
|
-
return false;
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
function handleMountResourceRequest(command, manager, emit) {
|
|
1663
|
-
const { requestId, connectorId: mountId, operation, path: reqPath, content } = command;
|
|
1664
|
-
try {
|
|
1665
|
-
const entry = manager.get(mountId);
|
|
1666
|
-
const mountPath = entry.handle.mountPath;
|
|
1667
|
-
if (!mountPath) {
|
|
1668
|
-
throw new Error(`Connector "${mountId}" has no projected mount directory`);
|
|
1669
|
-
}
|
|
1670
|
-
const targetPath = reqPath ? resolve(mountPath, reqPath) : mountPath;
|
|
1671
|
-
const rel = relative(mountPath, targetPath);
|
|
1672
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
1673
|
-
throw new Error("Path traversal outside mount");
|
|
1674
|
-
}
|
|
1675
|
-
let data;
|
|
1676
|
-
switch (operation) {
|
|
1677
|
-
case "list": {
|
|
1678
|
-
const entries = readdirSync(targetPath, { withFileTypes: true });
|
|
1679
|
-
data = entries.filter((e) => !e.name.startsWith(".")).map((e) => {
|
|
1680
|
-
const entryPath = reqPath ? join(reqPath, e.name) : e.name;
|
|
1681
|
-
const result = {
|
|
1682
|
-
name: e.name,
|
|
1683
|
-
path: entryPath,
|
|
1684
|
-
type: e.isDirectory() ? "directory" : "file"
|
|
1685
|
-
};
|
|
1686
|
-
if (!e.isDirectory()) {
|
|
1687
|
-
try {
|
|
1688
|
-
result.size = statSync(join(targetPath, e.name)).size;
|
|
1689
|
-
} catch {
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
return result;
|
|
1693
|
-
});
|
|
1694
|
-
break;
|
|
1695
|
-
}
|
|
1696
|
-
case "read": {
|
|
1697
|
-
const contentType = getMimeType(basename(targetPath));
|
|
1698
|
-
if (isBinaryFile(targetPath)) {
|
|
1699
|
-
const buf = readFileSync(targetPath);
|
|
1700
|
-
data = {
|
|
1701
|
-
data: buf.toString("base64"),
|
|
1702
|
-
contentType,
|
|
1703
|
-
encoding: "binary"
|
|
1704
|
-
};
|
|
1705
|
-
} else {
|
|
1706
|
-
data = {
|
|
1707
|
-
data: readFileSync(targetPath, "utf-8"),
|
|
1708
|
-
contentType,
|
|
1709
|
-
encoding: "utf-8"
|
|
1710
|
-
};
|
|
1711
|
-
}
|
|
1712
|
-
break;
|
|
1713
|
-
}
|
|
1714
|
-
case "write": {
|
|
1715
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
1716
|
-
const writeContent = typeof content === "string" ? content : content?.data ?? "";
|
|
1717
|
-
const encoding = typeof content === "string" ? void 0 : content?.encoding;
|
|
1718
|
-
if (encoding === "binary") {
|
|
1719
|
-
writeFileSync(targetPath, Buffer.from(writeContent, "base64"));
|
|
1720
|
-
} else {
|
|
1721
|
-
writeFileSync(targetPath, writeContent, "utf-8");
|
|
1722
|
-
}
|
|
1723
|
-
data = true;
|
|
1724
|
-
break;
|
|
1725
|
-
}
|
|
1726
|
-
case "delete": {
|
|
1727
|
-
unlinkSync(targetPath);
|
|
1728
|
-
data = true;
|
|
1729
|
-
break;
|
|
1730
|
-
}
|
|
1731
|
-
default:
|
|
1732
|
-
throw new Error(`Unsupported operation for mount: ${operation}`);
|
|
1733
|
-
}
|
|
1734
|
-
emit({ type: "resource_response", requestId, resourceId: mountId, operation, data });
|
|
1735
|
-
} catch (err) {
|
|
1736
|
-
emit({
|
|
1737
|
-
type: "resource_response",
|
|
1738
|
-
requestId,
|
|
1739
|
-
resourceId: mountId,
|
|
1740
|
-
operation,
|
|
1741
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
// runner/src/ai-credential-refresh.ts
|
|
1747
|
-
var PROACTIVE_AI_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
1748
|
-
function createAiCredentialRefreshScheduler(deps) {
|
|
1749
|
-
const now = deps.now ?? (() => Date.now());
|
|
1750
|
-
const setTimeoutImpl = deps.setTimeoutImpl ?? ((handler, ms) => {
|
|
1751
|
-
const handle = setTimeout(handler, ms);
|
|
1752
|
-
return handle;
|
|
1753
|
-
});
|
|
1754
|
-
const clearTimeoutImpl = deps.clearTimeoutImpl ?? ((handle) => {
|
|
1755
|
-
clearTimeout(handle);
|
|
1756
|
-
});
|
|
1757
|
-
const marginMs = deps.marginMs ?? PROACTIVE_AI_REFRESH_MARGIN_MS;
|
|
1758
|
-
let timer = null;
|
|
1759
|
-
function cancel() {
|
|
1760
|
-
if (timer !== null) {
|
|
1761
|
-
clearTimeoutImpl(timer);
|
|
1762
|
-
timer = null;
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
async function fire() {
|
|
1766
|
-
timer = null;
|
|
1767
|
-
let next;
|
|
1768
|
-
try {
|
|
1769
|
-
next = await deps.refresh();
|
|
1770
|
-
} catch (err) {
|
|
1771
|
-
deps.log(
|
|
1772
|
-
`[ai-credential-refresh] refresh threw: ${err instanceof Error ? err.message : String(err)}; reactive 401 path remains as safety net`
|
|
1773
|
-
);
|
|
1774
|
-
return;
|
|
1775
|
-
}
|
|
1776
|
-
if (next === void 0) {
|
|
1777
|
-
deps.log(
|
|
1778
|
-
"[ai-credential-refresh] refresh returned no expiry; reactive 401 path remains as safety net"
|
|
1779
|
-
);
|
|
1780
|
-
return;
|
|
1781
|
-
}
|
|
1782
|
-
schedule(next);
|
|
1783
|
-
}
|
|
1784
|
-
function schedule(expiresAtIso) {
|
|
1785
|
-
cancel();
|
|
1786
|
-
if (!expiresAtIso) {
|
|
1787
|
-
return;
|
|
1788
|
-
}
|
|
1789
|
-
const expiresAtMs = Date.parse(expiresAtIso);
|
|
1790
|
-
if (!Number.isFinite(expiresAtMs)) return;
|
|
1791
|
-
const delayMs = expiresAtMs - now() - marginMs;
|
|
1792
|
-
if (delayMs <= 0) {
|
|
1793
|
-
deps.log(
|
|
1794
|
-
`[ai-credential-refresh] token within ${marginMs / 1e3}s of expiry; refreshing now`
|
|
1795
|
-
);
|
|
1796
|
-
timer = setTimeoutImpl(() => {
|
|
1797
|
-
void fire();
|
|
1798
|
-
}, 0);
|
|
1799
|
-
} else {
|
|
1800
|
-
deps.log(
|
|
1801
|
-
`[ai-credential-refresh] scheduled in ${Math.round(delayMs / 1e3)}s (expiresAt=${expiresAtIso})`
|
|
1802
|
-
);
|
|
1803
|
-
timer = setTimeoutImpl(() => {
|
|
1804
|
-
void fire();
|
|
1805
|
-
}, delayMs);
|
|
1806
|
-
}
|
|
1807
|
-
timer.unref?.();
|
|
1808
|
-
}
|
|
1809
|
-
return {
|
|
1810
|
-
schedule,
|
|
1811
|
-
cancel,
|
|
1812
|
-
isPending: () => timer !== null
|
|
1813
|
-
};
|
|
1814
|
-
}
|
|
1815
|
-
function keywordSearch(capabilities, query, limit) {
|
|
1816
|
-
const tokens = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
1817
|
-
if (tokens.length === 0) return capabilities.slice(0, limit);
|
|
1818
|
-
const scored = capabilities.map((cap) => {
|
|
1819
|
-
const haystack = [cap.name, cap.description, cap.promptFragment ?? ""].join(" ").toLowerCase();
|
|
1820
|
-
let score = 0;
|
|
1821
|
-
for (const token of tokens) {
|
|
1822
|
-
if (haystack.includes(token)) score += 1;
|
|
1823
|
-
}
|
|
1824
|
-
return { cap, score };
|
|
1825
|
-
}).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
|
|
1826
|
-
return scored.slice(0, limit).map((x) => x.cap);
|
|
1827
|
-
}
|
|
1828
|
-
function builtinCapabilities(registry) {
|
|
1829
|
-
const listFilterSchema = z5.strictObject({
|
|
1830
|
-
side: z5.enum(["client", "agent"]).optional(),
|
|
1831
|
-
originKind: z5.enum(["framework", "client", "agent", "flow", "skill", "mcp", "connector", "mount"]).optional(),
|
|
1832
|
-
scope: z5.enum(["session", "turn"]).optional()
|
|
1833
|
-
});
|
|
1834
|
-
return [
|
|
1835
|
-
defineCapability({
|
|
1836
|
-
name: "__capabilities.list",
|
|
1837
|
-
displayName: "List capabilities",
|
|
1838
|
-
description: "List available capabilities in this session. Optionally filter by side, origin kind, or scope.",
|
|
1839
|
-
side: "agent",
|
|
1840
|
-
origin: { kind: "framework" },
|
|
1841
|
-
kind: "query",
|
|
1842
|
-
userInvokable: false,
|
|
1843
|
-
input: listFilterSchema,
|
|
1844
|
-
handler: async (args) => registry.list({
|
|
1845
|
-
side: args.side,
|
|
1846
|
-
origin: args.originKind,
|
|
1847
|
-
scope: args.scope
|
|
1848
|
-
})
|
|
1849
|
-
}),
|
|
1850
|
-
defineCapability({
|
|
1851
|
-
name: "__capabilities.search",
|
|
1852
|
-
displayName: "Search capabilities",
|
|
1853
|
-
description: "Keyword search across capability names, descriptions, and prompt fragments. Returns the top matches by score (substring match count).",
|
|
1854
|
-
side: "agent",
|
|
1855
|
-
origin: { kind: "framework" },
|
|
1856
|
-
kind: "query",
|
|
1857
|
-
userInvokable: false,
|
|
1858
|
-
input: z5.strictObject({
|
|
1859
|
-
query: z5.string(),
|
|
1860
|
-
limit: z5.number().int().positive().optional()
|
|
1861
|
-
}),
|
|
1862
|
-
handler: async ({ query, limit }) => keywordSearch(registry.list(), query, limit ?? 10)
|
|
1863
|
-
}),
|
|
1864
|
-
defineCapability({
|
|
1865
|
-
name: "__capabilities.describe",
|
|
1866
|
-
displayName: "Describe capability",
|
|
1867
|
-
description: "Return the full Capability descriptor for a single capability by name.",
|
|
1868
|
-
side: "agent",
|
|
1869
|
-
origin: { kind: "framework" },
|
|
1870
|
-
kind: "query",
|
|
1871
|
-
userInvokable: false,
|
|
1872
|
-
input: z5.strictObject({ name: z5.string() }),
|
|
1873
|
-
handler: async ({ name }) => {
|
|
1874
|
-
const cap = registry.resolve(name);
|
|
1875
|
-
if (!cap) return null;
|
|
1876
|
-
const wire = registry.list().find((c) => c.name === name);
|
|
1877
|
-
return wire ?? null;
|
|
1878
|
-
}
|
|
1879
|
-
})
|
|
1880
|
-
];
|
|
1881
|
-
}
|
|
1882
|
-
function bootstrapCapabilityRegistry(registry) {
|
|
1883
|
-
for (const cap of builtinCapabilities(registry)) {
|
|
1884
|
-
registry.register(cap, "agent");
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
var DEFAULT_AUDIENCE = ["llm", "user"];
|
|
1888
|
-
function audienceOf(cap) {
|
|
1889
|
-
return cap.audience ?? DEFAULT_AUDIENCE;
|
|
1890
|
-
}
|
|
1891
|
-
function hasAudience(cap, target) {
|
|
1892
|
-
return audienceOf(cap).includes(target);
|
|
1893
|
-
}
|
|
1894
|
-
var ORIGIN_PROMPT_ORDER = [
|
|
1895
|
-
"framework",
|
|
1896
|
-
"client",
|
|
1897
|
-
"agent",
|
|
1898
|
-
"flow",
|
|
1899
|
-
"skill",
|
|
1900
|
-
"mcp",
|
|
1901
|
-
"connector",
|
|
1902
|
-
"mount"
|
|
1903
|
-
];
|
|
1904
|
-
var CapabilityRegistry = class {
|
|
1905
|
-
byName = /* @__PURE__ */ new Map();
|
|
1906
|
-
logger;
|
|
1907
|
-
remoteInvoker;
|
|
1908
|
-
constructor(logger) {
|
|
1909
|
-
this.logger = logger ?? createLogger({ kind: "runner", subkind: "capability-registry" });
|
|
1910
|
-
}
|
|
1911
|
-
/**
|
|
1912
|
-
* Install the platform-bound invoker used by {@link invokeRemote}.
|
|
1913
|
-
*
|
|
1914
|
-
* The runner wires this up after the transport is connected and the
|
|
1915
|
-
* per-session pending-call map is in place. Idempotent — repeated calls
|
|
1916
|
-
* replace the prior invoker.
|
|
1917
|
-
*
|
|
1918
|
-
* @category Runtime
|
|
1919
|
-
* @since 3.0.0
|
|
1920
|
-
*/
|
|
1921
|
-
setRemoteInvoker(invoker) {
|
|
1922
|
-
this.remoteInvoker = invoker;
|
|
1923
|
-
}
|
|
1924
|
-
/**
|
|
1925
|
-
* Invoke a `host.*` (or other peer-side) capability on the platform.
|
|
1926
|
-
*
|
|
1927
|
-
* Composes a `capability_invoked` event under the hood and resolves with
|
|
1928
|
-
* the matching `capability_result` payload. Infrastructure failures
|
|
1929
|
-
* (timeout, schema mismatch, transport disconnect) reject with `Error`;
|
|
1930
|
-
* domain failures (e.g. `{ ok: false, code }` for credential mints) ride
|
|
1931
|
-
* in the resolved payload.
|
|
1932
|
-
*
|
|
1933
|
-
* @category Runtime
|
|
1934
|
-
* @since 3.0.0
|
|
1935
|
-
*/
|
|
1936
|
-
async invokeRemote(name, input) {
|
|
1937
|
-
if (!this.remoteInvoker) {
|
|
1938
|
-
throw new Error(
|
|
1939
|
-
`Capability '${name}' invoked remotely but no remote invoker is wired. Call CapabilityRegistry.setRemoteInvoker(...) before dispatch.`
|
|
1940
|
-
);
|
|
1941
|
-
}
|
|
1942
|
-
return this.remoteInvoker(name, input, this.byName.get(name)?.callTimeoutMs);
|
|
1846
|
+
return this.remoteInvoker(name, input, this.byName.get(name)?.callTimeoutMs);
|
|
1943
1847
|
}
|
|
1944
1848
|
/**
|
|
1945
1849
|
* Register a capability. Validates that the registering `source` is
|
|
@@ -2218,34 +2122,227 @@ function resolveCapabilityResult(pending, cmd) {
|
|
|
2218
2122
|
} else {
|
|
2219
2123
|
entry.resolve(result);
|
|
2220
2124
|
}
|
|
2221
|
-
return true;
|
|
2222
|
-
}
|
|
2223
|
-
function rejectCapabilityOnApprovalDeny(pending, callId, feedback) {
|
|
2224
|
-
const entry = pending.get(callId);
|
|
2225
|
-
if (!entry) return false;
|
|
2226
|
-
clearTimeout(entry.timer);
|
|
2227
|
-
pending.delete(callId);
|
|
2228
|
-
entry.reject(new Error(feedback ?? "declined by user"));
|
|
2229
|
-
return true;
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2125
|
+
return true;
|
|
2126
|
+
}
|
|
2127
|
+
function rejectCapabilityOnApprovalDeny(pending, callId, feedback) {
|
|
2128
|
+
const entry = pending.get(callId);
|
|
2129
|
+
if (!entry) return false;
|
|
2130
|
+
clearTimeout(entry.timer);
|
|
2131
|
+
pending.delete(callId);
|
|
2132
|
+
entry.reject(new Error(feedback ?? "declined by user"));
|
|
2133
|
+
return true;
|
|
2134
|
+
}
|
|
2135
|
+
async function handleResourceRequest(command, manager, emit) {
|
|
2136
|
+
const { requestId, connectorId, operation, path: path5, options, content } = command;
|
|
2137
|
+
try {
|
|
2138
|
+
let data;
|
|
2139
|
+
switch (operation) {
|
|
2140
|
+
case "list":
|
|
2141
|
+
data = await manager.list(
|
|
2142
|
+
connectorId,
|
|
2143
|
+
path5,
|
|
2144
|
+
options
|
|
2145
|
+
);
|
|
2146
|
+
break;
|
|
2147
|
+
case "read":
|
|
2148
|
+
data = await manager.read(connectorId, path5);
|
|
2149
|
+
break;
|
|
2150
|
+
case "write":
|
|
2151
|
+
await manager.write(connectorId, path5, content);
|
|
2152
|
+
data = true;
|
|
2153
|
+
break;
|
|
2154
|
+
case "delete":
|
|
2155
|
+
data = await manager.delete(connectorId, path5);
|
|
2156
|
+
break;
|
|
2157
|
+
case "search":
|
|
2158
|
+
data = await manager.search(
|
|
2159
|
+
connectorId,
|
|
2160
|
+
path5,
|
|
2161
|
+
options
|
|
2162
|
+
);
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
emit({ type: "resource_response", requestId, resourceId: connectorId, operation, data });
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
emit({
|
|
2168
|
+
type: "resource_response",
|
|
2169
|
+
requestId,
|
|
2170
|
+
resourceId: connectorId,
|
|
2171
|
+
operation,
|
|
2172
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
function getMimeType(filename) {
|
|
2177
|
+
return mime.getType(filename) ?? "application/octet-stream";
|
|
2178
|
+
}
|
|
2179
|
+
function isBinaryFile(filePath) {
|
|
2180
|
+
try {
|
|
2181
|
+
const fd = openSync(filePath, "r");
|
|
2182
|
+
const buf = Buffer.alloc(8192);
|
|
2183
|
+
const bytesRead = readSync(fd, buf, 0, 8192, 0);
|
|
2184
|
+
closeSync(fd);
|
|
2185
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
2186
|
+
if (buf[i] === 0) return true;
|
|
2187
|
+
}
|
|
2188
|
+
return false;
|
|
2189
|
+
} catch {
|
|
2190
|
+
return false;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
function handleMountResourceRequest(command, manager, emit) {
|
|
2194
|
+
const { requestId, connectorId: mountId, operation, path: reqPath, content } = command;
|
|
2195
|
+
try {
|
|
2196
|
+
const entry = manager.get(mountId);
|
|
2197
|
+
const mountPath = entry.handle.mountPath;
|
|
2198
|
+
if (!mountPath) {
|
|
2199
|
+
throw new Error(`Connector "${mountId}" has no projected mount directory`);
|
|
2200
|
+
}
|
|
2201
|
+
const targetPath = reqPath ? resolve(mountPath, reqPath) : mountPath;
|
|
2202
|
+
const rel = relative(mountPath, targetPath);
|
|
2203
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
2204
|
+
throw new Error("Path traversal outside mount");
|
|
2205
|
+
}
|
|
2206
|
+
let data;
|
|
2207
|
+
switch (operation) {
|
|
2208
|
+
case "list": {
|
|
2209
|
+
const entries = readdirSync(targetPath, { withFileTypes: true });
|
|
2210
|
+
data = entries.filter((e) => !e.name.startsWith(".")).map((e) => {
|
|
2211
|
+
const entryPath = reqPath ? join(reqPath, e.name) : e.name;
|
|
2212
|
+
const result = {
|
|
2213
|
+
name: e.name,
|
|
2214
|
+
path: entryPath,
|
|
2215
|
+
type: e.isDirectory() ? "directory" : "file"
|
|
2216
|
+
};
|
|
2217
|
+
if (!e.isDirectory()) {
|
|
2218
|
+
try {
|
|
2219
|
+
result.size = statSync(join(targetPath, e.name)).size;
|
|
2220
|
+
} catch {
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
return result;
|
|
2224
|
+
});
|
|
2225
|
+
break;
|
|
2226
|
+
}
|
|
2227
|
+
case "read": {
|
|
2228
|
+
const contentType = getMimeType(basename(targetPath));
|
|
2229
|
+
if (isBinaryFile(targetPath)) {
|
|
2230
|
+
const buf = readFileSync(targetPath);
|
|
2231
|
+
data = {
|
|
2232
|
+
data: buf.toString("base64"),
|
|
2233
|
+
contentType,
|
|
2234
|
+
encoding: "binary"
|
|
2235
|
+
};
|
|
2236
|
+
} else {
|
|
2237
|
+
data = {
|
|
2238
|
+
data: readFileSync(targetPath, "utf-8"),
|
|
2239
|
+
contentType,
|
|
2240
|
+
encoding: "utf-8"
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
break;
|
|
2244
|
+
}
|
|
2245
|
+
case "write": {
|
|
2246
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
2247
|
+
const writeContent = typeof content === "string" ? content : content?.data ?? "";
|
|
2248
|
+
const encoding = typeof content === "string" ? void 0 : content?.encoding;
|
|
2249
|
+
if (encoding === "binary") {
|
|
2250
|
+
writeFileSync(targetPath, Buffer.from(writeContent, "base64"));
|
|
2251
|
+
} else {
|
|
2252
|
+
writeFileSync(targetPath, writeContent, "utf-8");
|
|
2253
|
+
}
|
|
2254
|
+
data = true;
|
|
2255
|
+
break;
|
|
2256
|
+
}
|
|
2257
|
+
case "delete": {
|
|
2258
|
+
unlinkSync(targetPath);
|
|
2259
|
+
data = true;
|
|
2260
|
+
break;
|
|
2261
|
+
}
|
|
2262
|
+
default:
|
|
2263
|
+
throw new Error(`Unsupported operation for mount: ${operation}`);
|
|
2264
|
+
}
|
|
2265
|
+
emit({ type: "resource_response", requestId, resourceId: mountId, operation, data });
|
|
2266
|
+
} catch (err) {
|
|
2267
|
+
emit({
|
|
2268
|
+
type: "resource_response",
|
|
2269
|
+
requestId,
|
|
2270
|
+
resourceId: mountId,
|
|
2271
|
+
operation,
|
|
2272
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// runner/src/ai-credential-refresh.ts
|
|
2278
|
+
var PROACTIVE_AI_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
2279
|
+
function createAiCredentialRefreshScheduler(deps) {
|
|
2280
|
+
const now = deps.now ?? (() => Date.now());
|
|
2281
|
+
const setTimeoutImpl = deps.setTimeoutImpl ?? ((handler, ms) => {
|
|
2282
|
+
const handle = setTimeout(handler, ms);
|
|
2283
|
+
return handle;
|
|
2284
|
+
});
|
|
2285
|
+
const clearTimeoutImpl = deps.clearTimeoutImpl ?? ((handle) => {
|
|
2286
|
+
clearTimeout(handle);
|
|
2287
|
+
});
|
|
2288
|
+
const marginMs = deps.marginMs ?? PROACTIVE_AI_REFRESH_MARGIN_MS;
|
|
2289
|
+
let timer = null;
|
|
2290
|
+
function cancel() {
|
|
2291
|
+
if (timer !== null) {
|
|
2292
|
+
clearTimeoutImpl(timer);
|
|
2293
|
+
timer = null;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
async function fire() {
|
|
2297
|
+
timer = null;
|
|
2298
|
+
let next;
|
|
2299
|
+
try {
|
|
2300
|
+
next = await deps.refresh();
|
|
2301
|
+
} catch (err) {
|
|
2302
|
+
deps.log(
|
|
2303
|
+
`[ai-credential-refresh] refresh threw: ${err instanceof Error ? err.message : String(err)}; reactive 401 path remains as safety net`
|
|
2304
|
+
);
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
if (next === void 0) {
|
|
2308
|
+
deps.log(
|
|
2309
|
+
"[ai-credential-refresh] refresh returned no expiry; reactive 401 path remains as safety net"
|
|
2310
|
+
);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
schedule(next);
|
|
2239
2314
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
if (
|
|
2243
|
-
|
|
2244
|
-
return false;
|
|
2315
|
+
function schedule(expiresAtIso) {
|
|
2316
|
+
cancel();
|
|
2317
|
+
if (!expiresAtIso) {
|
|
2318
|
+
return;
|
|
2245
2319
|
}
|
|
2246
|
-
|
|
2320
|
+
const expiresAtMs = Date.parse(expiresAtIso);
|
|
2321
|
+
if (!Number.isFinite(expiresAtMs)) return;
|
|
2322
|
+
const delayMs = expiresAtMs - now() - marginMs;
|
|
2323
|
+
if (delayMs <= 0) {
|
|
2324
|
+
deps.log(
|
|
2325
|
+
`[ai-credential-refresh] token within ${marginMs / 1e3}s of expiry; refreshing now`
|
|
2326
|
+
);
|
|
2327
|
+
timer = setTimeoutImpl(() => {
|
|
2328
|
+
void fire();
|
|
2329
|
+
}, 0);
|
|
2330
|
+
} else {
|
|
2331
|
+
deps.log(
|
|
2332
|
+
`[ai-credential-refresh] scheduled in ${Math.round(delayMs / 1e3)}s (expiresAt=${expiresAtIso})`
|
|
2333
|
+
);
|
|
2334
|
+
timer = setTimeoutImpl(() => {
|
|
2335
|
+
void fire();
|
|
2336
|
+
}, delayMs);
|
|
2337
|
+
}
|
|
2338
|
+
timer.unref?.();
|
|
2247
2339
|
}
|
|
2248
|
-
|
|
2340
|
+
return {
|
|
2341
|
+
schedule,
|
|
2342
|
+
cancel,
|
|
2343
|
+
isPending: () => timer !== null
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2249
2346
|
|
|
2250
2347
|
// runner/src/compaction/prompt.ts
|
|
2251
2348
|
var DEFAULT_COMPACTION_PROMPT = `You are being asked to compact this conversation into a structured summary.
|
|
@@ -2325,6 +2422,24 @@ ${opts.focus}`);
|
|
|
2325
2422
|
return parts.join("");
|
|
2326
2423
|
}
|
|
2327
2424
|
|
|
2425
|
+
// runner/src/compaction/strategy.ts
|
|
2426
|
+
var TokenThresholdStrategy = class {
|
|
2427
|
+
thresholdPercent;
|
|
2428
|
+
minCooldownMs;
|
|
2429
|
+
constructor(opts) {
|
|
2430
|
+
this.thresholdPercent = opts.thresholdPercent;
|
|
2431
|
+
this.minCooldownMs = opts.minCooldownMs;
|
|
2432
|
+
}
|
|
2433
|
+
shouldCompact(context) {
|
|
2434
|
+
const fillPercent = context.estimatedTokens / context.contextWindow * 100;
|
|
2435
|
+
if (fillPercent < this.thresholdPercent) return false;
|
|
2436
|
+
if (context.timeSinceLastCompaction !== null && context.timeSinceLastCompaction < this.minCooldownMs) {
|
|
2437
|
+
return false;
|
|
2438
|
+
}
|
|
2439
|
+
return true;
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
|
|
2328
2443
|
// runner/src/compaction/orchestrator.ts
|
|
2329
2444
|
var MIN_SUMMARY_CHARS = 200;
|
|
2330
2445
|
var MAX_COMPRESSION_RATIO = 0.9;
|
|
@@ -2370,9 +2485,6 @@ var CompactionOrchestrator = class {
|
|
|
2370
2485
|
minCooldownMs: this.config.minCooldownMs
|
|
2371
2486
|
});
|
|
2372
2487
|
}
|
|
2373
|
-
get hasNewMessagesSinceCompaction() {
|
|
2374
|
-
return this.messagesSinceCompaction > 0;
|
|
2375
|
-
}
|
|
2376
2488
|
trackMessage(seq) {
|
|
2377
2489
|
this.currentSeq = seq;
|
|
2378
2490
|
this.messagesSinceCompaction++;
|
|
@@ -2406,6 +2518,17 @@ var CompactionOrchestrator = class {
|
|
|
2406
2518
|
const driverProvider = this.driver.driverInfo?.id ?? null;
|
|
2407
2519
|
const coversFromSeq = this.lastCompactionSeq ?? 0;
|
|
2408
2520
|
const capabilitySignatureAtCompaction = this.opts.getCurrentCapabilitySignature?.() ?? null;
|
|
2521
|
+
const attemptBase = {
|
|
2522
|
+
trigger,
|
|
2523
|
+
model: driverModel,
|
|
2524
|
+
provider: driverProvider,
|
|
2525
|
+
inputTokens: tokensBefore,
|
|
2526
|
+
coversFromSeq,
|
|
2527
|
+
coversToSeq: this.currentSeq,
|
|
2528
|
+
driverSessionIdAtCompaction: driverSessionId,
|
|
2529
|
+
modelAtCompaction: driverModel,
|
|
2530
|
+
capabilitySignatureAtCompaction
|
|
2531
|
+
};
|
|
2409
2532
|
this.opts.onCompactionStart?.(trigger);
|
|
2410
2533
|
this.opts.onLog?.(`[compaction] Starting (trigger=${trigger}, tokens=${tokensBefore})`);
|
|
2411
2534
|
const prompt = buildCompactionPrompt({
|
|
@@ -2414,6 +2537,33 @@ var CompactionOrchestrator = class {
|
|
|
2414
2537
|
mcpDirectives: this.opts.mcpDirectives,
|
|
2415
2538
|
focus
|
|
2416
2539
|
});
|
|
2540
|
+
const { summaryText, promptError } = await this._runCompactionPrompt(prompt);
|
|
2541
|
+
if (promptError !== null) {
|
|
2542
|
+
this._emitThrownFailure(attemptBase, promptError);
|
|
2543
|
+
return null;
|
|
2544
|
+
}
|
|
2545
|
+
const actualUsage = this.driver.getTokenUsage();
|
|
2546
|
+
const tokensAfter = actualUsage?.outputTokens ?? Math.ceil(summaryText.length / 4);
|
|
2547
|
+
const validation = validateCompactionOutput(summaryText, tokensBefore, tokensAfter);
|
|
2548
|
+
if (!validation.ok) {
|
|
2549
|
+
this._emitValidationFailure(attemptBase, validation.errorCode, tokensAfter);
|
|
2550
|
+
return null;
|
|
2551
|
+
}
|
|
2552
|
+
return await this._completeCompaction(
|
|
2553
|
+
attemptBase,
|
|
2554
|
+
trigger,
|
|
2555
|
+
summaryText,
|
|
2556
|
+
tokensBefore,
|
|
2557
|
+
tokensAfter,
|
|
2558
|
+
coversFromSeq
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Drive the compaction `prompt()` call, capturing summary text from the
|
|
2563
|
+
* driver's raw bridge-level events and any thrown error. The listener is
|
|
2564
|
+
* always removed in `finally`.
|
|
2565
|
+
*/
|
|
2566
|
+
async _runCompactionPrompt(prompt) {
|
|
2417
2567
|
let summaryText = "";
|
|
2418
2568
|
const captureHandler = (raw) => {
|
|
2419
2569
|
const event = raw;
|
|
@@ -2444,59 +2594,55 @@ var CompactionOrchestrator = class {
|
|
|
2444
2594
|
} finally {
|
|
2445
2595
|
this.driver.removeListener("agent-event", captureHandler);
|
|
2446
2596
|
}
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
});
|
|
2497
|
-
this.opts.onCompactionEnd?.();
|
|
2498
|
-
return null;
|
|
2499
|
-
}
|
|
2597
|
+
return { summaryText, promptError };
|
|
2598
|
+
}
|
|
2599
|
+
/**
|
|
2600
|
+
* Record a `failed` attempt for a thrown `prompt()` error: classify the
|
|
2601
|
+
* error, map it to an error code, log, emit the attempt event, and signal
|
|
2602
|
+
* compaction end.
|
|
2603
|
+
*/
|
|
2604
|
+
_emitThrownFailure(attemptBase, promptError) {
|
|
2605
|
+
const classified = classifyClaudeSdkError(
|
|
2606
|
+
promptError instanceof Error ? promptError.message : String(promptError)
|
|
2607
|
+
);
|
|
2608
|
+
const errorCode = classified.category === "rate_limit" ? "rate_limit" : classified.category === "auth" ? "auth" : classified.category === "network" ? "timeout" : classified.category === "validation" ? "parse_error" : classified.category;
|
|
2609
|
+
this.opts.onLog?.(`[compaction] Failed: ${classified.message} (${errorCode})`);
|
|
2610
|
+
this.opts.onCompactionAttempt?.({
|
|
2611
|
+
type: "compaction_attempt",
|
|
2612
|
+
status: "failed",
|
|
2613
|
+
errorCode,
|
|
2614
|
+
summary: "",
|
|
2615
|
+
outputTokens: 0,
|
|
2616
|
+
validatedAt: null,
|
|
2617
|
+
...attemptBase
|
|
2618
|
+
});
|
|
2619
|
+
this.opts.onCompactionEnd?.();
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Record a `failed` attempt for output that passed `prompt()` but failed
|
|
2623
|
+
* structural validation. Logs, emits the attempt event, and signals
|
|
2624
|
+
* compaction end.
|
|
2625
|
+
*/
|
|
2626
|
+
_emitValidationFailure(attemptBase, errorCode, tokensAfter) {
|
|
2627
|
+
this.opts.onLog?.(
|
|
2628
|
+
`[compaction] Output failed validation (${errorCode}); recording failed attempt`
|
|
2629
|
+
);
|
|
2630
|
+
this.opts.onCompactionAttempt?.({
|
|
2631
|
+
type: "compaction_attempt",
|
|
2632
|
+
status: "failed",
|
|
2633
|
+
errorCode,
|
|
2634
|
+
summary: "",
|
|
2635
|
+
outputTokens: tokensAfter,
|
|
2636
|
+
validatedAt: null,
|
|
2637
|
+
...attemptBase
|
|
2638
|
+
});
|
|
2639
|
+
this.opts.onCompactionEnd?.();
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Success path: emit the attempt + snapshot events, update internal state,
|
|
2643
|
+
* and (unless hibernating) reset the driver and inject the snapshot.
|
|
2644
|
+
*/
|
|
2645
|
+
async _completeCompaction(attemptBase, trigger, summaryText, tokensBefore, tokensAfter, coversFromSeq) {
|
|
2500
2646
|
const validatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2501
2647
|
const snapshot = {
|
|
2502
2648
|
type: "snapshot",
|
|
@@ -2511,18 +2657,10 @@ var CompactionOrchestrator = class {
|
|
|
2511
2657
|
type: "compaction_attempt",
|
|
2512
2658
|
status: "success",
|
|
2513
2659
|
errorCode: null,
|
|
2514
|
-
trigger,
|
|
2515
2660
|
summary: summaryText,
|
|
2516
|
-
model: driverModel,
|
|
2517
|
-
provider: driverProvider,
|
|
2518
|
-
inputTokens: tokensBefore,
|
|
2519
2661
|
outputTokens: tokensAfter,
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
driverSessionIdAtCompaction: driverSessionId,
|
|
2523
|
-
modelAtCompaction: driverModel,
|
|
2524
|
-
capabilitySignatureAtCompaction,
|
|
2525
|
-
validatedAt
|
|
2662
|
+
validatedAt,
|
|
2663
|
+
...attemptBase
|
|
2526
2664
|
});
|
|
2527
2665
|
this.opts.onSnapshot?.(snapshot);
|
|
2528
2666
|
this.lastCompactionSeq = this.currentSeq + 1;
|
|
@@ -2545,11 +2683,6 @@ The conversation has been compacted. The above snapshot contains the full contex
|
|
|
2545
2683
|
);
|
|
2546
2684
|
return snapshot;
|
|
2547
2685
|
}
|
|
2548
|
-
initFromSnapshot(lastSnapshotSeq, lastSnapshotTime) {
|
|
2549
|
-
this.lastCompactionSeq = lastSnapshotSeq;
|
|
2550
|
-
this.lastCompactionTime = lastSnapshotTime;
|
|
2551
|
-
this.messagesSinceCompaction = 0;
|
|
2552
|
-
}
|
|
2553
2686
|
};
|
|
2554
2687
|
function rewriteFileChangedPath(event, cwd, mounts) {
|
|
2555
2688
|
if (event.type !== "file_changed") return event;
|
|
@@ -2782,6 +2915,54 @@ function buildRunnerCapabilities(handlers) {
|
|
|
2782
2915
|
})
|
|
2783
2916
|
];
|
|
2784
2917
|
}
|
|
2918
|
+
var CLAUDE_CODE_CREDENTIALS_KEY = "CLAUDE_CODE_CREDENTIALS_JSON";
|
|
2919
|
+
async function writeClaudeCodeCredentialsFile(home, json) {
|
|
2920
|
+
const claudeDir = join(home, ".claude");
|
|
2921
|
+
await mkdir(claudeDir, { recursive: true, mode: 448 });
|
|
2922
|
+
const finalPath = join(claudeDir, ".credentials.json");
|
|
2923
|
+
const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
2924
|
+
await writeFile(tmpPath, json, { mode: 384 });
|
|
2925
|
+
await rename(tmpPath, finalPath);
|
|
2926
|
+
return finalPath;
|
|
2927
|
+
}
|
|
2928
|
+
async function mergeAndRewriteCredentialsFile(home, accessToken, expiresAt) {
|
|
2929
|
+
const credPath = join(home, ".claude", ".credentials.json");
|
|
2930
|
+
let merged = {};
|
|
2931
|
+
if (existsSync(credPath)) {
|
|
2932
|
+
try {
|
|
2933
|
+
merged = JSON.parse(readFileSync(credPath, "utf-8"));
|
|
2934
|
+
} catch {
|
|
2935
|
+
merged = {};
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
const oauthBlock = merged.claudeAiOauth ?? {};
|
|
2939
|
+
oauthBlock.accessToken = accessToken;
|
|
2940
|
+
if (expiresAt) oauthBlock.expiresAt = expiresAt;
|
|
2941
|
+
merged.claudeAiOauth = oauthBlock;
|
|
2942
|
+
merged.accessToken = accessToken;
|
|
2943
|
+
return writeClaudeCodeCredentialsFile(home, JSON.stringify(merged));
|
|
2944
|
+
}
|
|
2945
|
+
function extractClaudeAiOauthExpiresAt(json) {
|
|
2946
|
+
let parsed;
|
|
2947
|
+
try {
|
|
2948
|
+
parsed = JSON.parse(json);
|
|
2949
|
+
} catch {
|
|
2950
|
+
return null;
|
|
2951
|
+
}
|
|
2952
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2953
|
+
const obj = parsed;
|
|
2954
|
+
const oauth = obj.claudeAiOauth;
|
|
2955
|
+
const raw = oauth?.expiresAt ?? obj.expiresAt;
|
|
2956
|
+
if (raw == null) return null;
|
|
2957
|
+
if (typeof raw === "string") {
|
|
2958
|
+
const t = Date.parse(raw);
|
|
2959
|
+
return Number.isFinite(t) ? new Date(t).toISOString() : null;
|
|
2960
|
+
}
|
|
2961
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
2962
|
+
return new Date(raw).toISOString();
|
|
2963
|
+
}
|
|
2964
|
+
return null;
|
|
2965
|
+
}
|
|
2785
2966
|
|
|
2786
2967
|
// runner/src/session-resource-handlers.ts
|
|
2787
2968
|
function asDeclaration(declaration) {
|
|
@@ -2852,54 +3033,6 @@ function buildSessionResourceHandlers(deps) {
|
|
|
2852
3033
|
}
|
|
2853
3034
|
};
|
|
2854
3035
|
}
|
|
2855
|
-
var CLAUDE_CODE_CREDENTIALS_KEY = "CLAUDE_CODE_CREDENTIALS_JSON";
|
|
2856
|
-
async function writeClaudeCodeCredentialsFile(home, json) {
|
|
2857
|
-
const claudeDir = join(home, ".claude");
|
|
2858
|
-
await mkdir(claudeDir, { recursive: true, mode: 448 });
|
|
2859
|
-
const finalPath = join(claudeDir, ".credentials.json");
|
|
2860
|
-
const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
2861
|
-
await writeFile(tmpPath, json, { mode: 384 });
|
|
2862
|
-
await rename(tmpPath, finalPath);
|
|
2863
|
-
return finalPath;
|
|
2864
|
-
}
|
|
2865
|
-
async function mergeAndRewriteCredentialsFile(home, accessToken, expiresAt) {
|
|
2866
|
-
const credPath = join(home, ".claude", ".credentials.json");
|
|
2867
|
-
let merged = {};
|
|
2868
|
-
if (existsSync(credPath)) {
|
|
2869
|
-
try {
|
|
2870
|
-
merged = JSON.parse(readFileSync(credPath, "utf-8"));
|
|
2871
|
-
} catch {
|
|
2872
|
-
merged = {};
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
const oauthBlock = merged.claudeAiOauth ?? {};
|
|
2876
|
-
oauthBlock.accessToken = accessToken;
|
|
2877
|
-
if (expiresAt) oauthBlock.expiresAt = expiresAt;
|
|
2878
|
-
merged.claudeAiOauth = oauthBlock;
|
|
2879
|
-
merged.accessToken = accessToken;
|
|
2880
|
-
return writeClaudeCodeCredentialsFile(home, JSON.stringify(merged));
|
|
2881
|
-
}
|
|
2882
|
-
function extractClaudeAiOauthExpiresAt(json) {
|
|
2883
|
-
let parsed;
|
|
2884
|
-
try {
|
|
2885
|
-
parsed = JSON.parse(json);
|
|
2886
|
-
} catch {
|
|
2887
|
-
return null;
|
|
2888
|
-
}
|
|
2889
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
2890
|
-
const obj = parsed;
|
|
2891
|
-
const oauth = obj.claudeAiOauth;
|
|
2892
|
-
const raw = oauth?.expiresAt ?? obj.expiresAt;
|
|
2893
|
-
if (raw == null) return null;
|
|
2894
|
-
if (typeof raw === "string") {
|
|
2895
|
-
const t = Date.parse(raw);
|
|
2896
|
-
return Number.isFinite(t) ? new Date(t).toISOString() : null;
|
|
2897
|
-
}
|
|
2898
|
-
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
2899
|
-
return new Date(raw).toISOString();
|
|
2900
|
-
}
|
|
2901
|
-
return null;
|
|
2902
|
-
}
|
|
2903
3036
|
|
|
2904
3037
|
// runner/src/session-stimulus.ts
|
|
2905
3038
|
var DEFAULT_COALESCE_MS = 50;
|
|
@@ -3086,6 +3219,17 @@ function ensureGitConfigInclude(managedGitconfigPath) {
|
|
|
3086
3219
|
`;
|
|
3087
3220
|
writeFileSync(gitconfigPath, block, { mode: 420 });
|
|
3088
3221
|
}
|
|
3222
|
+
function setupManagedGitconfig(projectDir, serverLog) {
|
|
3223
|
+
const managedGitconfigPath = join(projectDir, ".skaile", "managed-gitconfig");
|
|
3224
|
+
process.env.GIT_CONFIG_GLOBAL = managedGitconfigPath;
|
|
3225
|
+
try {
|
|
3226
|
+
ensureGitConfigInclude(managedGitconfigPath);
|
|
3227
|
+
} catch (err) {
|
|
3228
|
+
serverLog.warn("failed to write ~/.gitconfig include \u2014 relying on env only", {
|
|
3229
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3230
|
+
});
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3089
3233
|
async function startAgentServer(opts) {
|
|
3090
3234
|
const port = opts.port ?? 8080;
|
|
3091
3235
|
const sessionId = opts.sessionId ?? process.env.SKAILE_SESSION_ID ?? "session-unknown";
|
|
@@ -3105,15 +3249,7 @@ async function startAgentServer(opts) {
|
|
|
3105
3249
|
const log = (line) => serverLog.info(line);
|
|
3106
3250
|
const profile = wsConfig.agent_config?.default;
|
|
3107
3251
|
const maxTurns = wsConfig.agent?.permissions?.max_turns ?? profile?.max_turns;
|
|
3108
|
-
|
|
3109
|
-
process.env.GIT_CONFIG_GLOBAL = managedGitconfigPath;
|
|
3110
|
-
try {
|
|
3111
|
-
ensureGitConfigInclude(managedGitconfigPath);
|
|
3112
|
-
} catch (err) {
|
|
3113
|
-
serverLog.warn("failed to write ~/.gitconfig include \u2014 relying on env only", {
|
|
3114
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3115
|
-
});
|
|
3116
|
-
}
|
|
3252
|
+
setupManagedGitconfig(opts.projectDir, serverLog);
|
|
3117
3253
|
const agentDir = opts.agentDir ?? resolveAgentDir(opts.projectDir);
|
|
3118
3254
|
if (agentDir) {
|
|
3119
3255
|
serverLog.info("agent dir resolved", { agentDir });
|
|
@@ -3235,475 +3371,1182 @@ async function startAgentServer(opts) {
|
|
|
3235
3371
|
timeoutMs
|
|
3236
3372
|
});
|
|
3237
3373
|
}
|
|
3238
|
-
function buildCapabilityHooks() {
|
|
3239
|
-
return {
|
|
3240
|
-
sessionId,
|
|
3241
|
-
composeTools: () => capabilityRegistry.composeLLMTools(),
|
|
3242
|
-
resolve: (name) => {
|
|
3243
|
-
const cap = capabilityRegistry.resolve(name);
|
|
3244
|
-
if (!cap) return null;
|
|
3245
|
-
return capabilityRegistry.list().find((c) => c.name === name) ?? null;
|
|
3374
|
+
function buildCapabilityHooks() {
|
|
3375
|
+
return {
|
|
3376
|
+
sessionId,
|
|
3377
|
+
composeTools: () => capabilityRegistry.composeLLMTools(),
|
|
3378
|
+
resolve: (name) => {
|
|
3379
|
+
const cap = capabilityRegistry.resolve(name);
|
|
3380
|
+
if (!cap) return null;
|
|
3381
|
+
return capabilityRegistry.list().find((c) => c.name === name) ?? null;
|
|
3382
|
+
},
|
|
3383
|
+
invoke: (name, input) => capabilityRegistry.invoke(name, input, { sessionId }),
|
|
3384
|
+
emitRender: (event) => sendEvent(event),
|
|
3385
|
+
emitText: (text) => sendEvent({
|
|
3386
|
+
type: "text",
|
|
3387
|
+
content: text
|
|
3388
|
+
})
|
|
3389
|
+
};
|
|
3390
|
+
}
|
|
3391
|
+
function registerSessionComposition(session) {
|
|
3392
|
+
if (session.composition?.discoverableEntries.length) {
|
|
3393
|
+
registerCompositionCapabilities(capabilityRegistry, session.composition.discoverableEntries);
|
|
3394
|
+
log(
|
|
3395
|
+
`[serve] registered ${session.composition.discoverableEntries.length} composition capabilities`
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
const secretsMode = wsConfig.secrets?.provider ?? "env";
|
|
3400
|
+
wsConfig.secrets?.timeoutMs ?? 3e4;
|
|
3401
|
+
function buildSessionConfig(secretProvider2, apiKey) {
|
|
3402
|
+
return {
|
|
3403
|
+
projectDir: opts.projectDir,
|
|
3404
|
+
agentDir,
|
|
3405
|
+
driver: opts.driver,
|
|
3406
|
+
model: opts.model,
|
|
3407
|
+
provider: opts.provider,
|
|
3408
|
+
promptsDir: opts.promptsDir,
|
|
3409
|
+
sessionId,
|
|
3410
|
+
resumeSessionId,
|
|
3411
|
+
maxTurns,
|
|
3412
|
+
wsConfig,
|
|
3413
|
+
externalPromptFile: process.env.SKAILE_SYSTEM_PROMPT_FILE,
|
|
3414
|
+
secretProvider: secretProvider2,
|
|
3415
|
+
onAuthError,
|
|
3416
|
+
...apiKey ? { apiKey } : {},
|
|
3417
|
+
// Protocol v2 `<CAPABILITIES>` block — joins each registered capability's
|
|
3418
|
+
// `promptFragment` (framework first, then client, then agent-side
|
|
3419
|
+
// surfaces). Built lazily inside `buildSessionConfig` so newly registered
|
|
3420
|
+
// capabilities (e.g. via mid-session `capability_register` commands) are
|
|
3421
|
+
// reflected on the next session rebuild.
|
|
3422
|
+
additionalPromptSections: (() => {
|
|
3423
|
+
const sections = [];
|
|
3424
|
+
const capSection = buildCapabilitiesPromptSection(capabilityRegistry);
|
|
3425
|
+
if (capSection) sections.push(capSection);
|
|
3426
|
+
return sections.length > 0 ? sections : void 0;
|
|
3427
|
+
})(),
|
|
3428
|
+
watch: {
|
|
3429
|
+
onSyncStatus: (mountId, status) => {
|
|
3430
|
+
sendEvent({
|
|
3431
|
+
type: "sync_status",
|
|
3432
|
+
mountId,
|
|
3433
|
+
...status,
|
|
3434
|
+
phase: status.phase
|
|
3435
|
+
});
|
|
3436
|
+
if (status.phase === "stubs_ready") {
|
|
3437
|
+
log(
|
|
3438
|
+
`[serve] ${mountId}: file tree ready (${status.totalFiles} files, ${status.stubFiles} stubs)`
|
|
3439
|
+
);
|
|
3440
|
+
} else if (status.phase === "initial_sync_complete") {
|
|
3441
|
+
log(
|
|
3442
|
+
`[serve] ${mountId}: sync complete (${status.downloadedFiles}/${status.totalFiles} downloaded)`
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
onFileChanged: (connectorId, event) => {
|
|
3447
|
+
sendEvent({
|
|
3448
|
+
type: "file_changed",
|
|
3449
|
+
path: event.path,
|
|
3450
|
+
action: event.action,
|
|
3451
|
+
mountId: connectorId
|
|
3452
|
+
});
|
|
3453
|
+
},
|
|
3454
|
+
// Forward connector lifecycle transitions to the platform. This is the
|
|
3455
|
+
// channel deferred (rclone-backed) filesystem mounts report progress on
|
|
3456
|
+
// after session_init_ack — a wedged remote shows as "still syncing".
|
|
3457
|
+
onConnectorStatus: (event) => {
|
|
3458
|
+
sendEvent(event);
|
|
3459
|
+
}
|
|
3460
|
+
},
|
|
3461
|
+
// Bring rclone-backed filesystem mounts up in the background so a slow
|
|
3462
|
+
// remote can't blow the platform's session_init_ack deadline and trigger
|
|
3463
|
+
// the container restart loop.
|
|
3464
|
+
deferFilesystemMounts: true,
|
|
3465
|
+
onLog: (line) => log(`[serve] ${line}`),
|
|
3466
|
+
capabilities: buildCapabilityHooks(),
|
|
3467
|
+
// Hand the registry to the session builder so external (declarative /
|
|
3468
|
+
// recipe-backed) MCP servers are spawned by the RUNNER and their tools
|
|
3469
|
+
// registered as `mcp`-origin capabilities — works on every driver, no
|
|
3470
|
+
// dependence on the claude-agent-sdk `mcpServers` query option.
|
|
3471
|
+
capabilityRegistry,
|
|
3472
|
+
// v3: pre-minted credentials replace the tokenMediator for initial mints.
|
|
3473
|
+
// Refresh routes through `host.refresh_credential` (invoked from the
|
|
3474
|
+
// bridge driver's onAuthError callback). The legacy `tokenMediator`
|
|
3475
|
+
// wire is gone.
|
|
3476
|
+
...preMintedSecrets ? { preMintedSecrets } : {},
|
|
3477
|
+
...aiProviderConfigId ? { aiProviderConfigId } : {}
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
let resolveSessionReady;
|
|
3481
|
+
let rejectSessionReady;
|
|
3482
|
+
const sessionReady = new Promise((resolve3, reject) => {
|
|
3483
|
+
resolveSessionReady = resolve3;
|
|
3484
|
+
rejectSessionReady = reject;
|
|
3485
|
+
});
|
|
3486
|
+
let sessionConfig;
|
|
3487
|
+
let agentSession;
|
|
3488
|
+
let secretProvider;
|
|
3489
|
+
let runtimeIdentity;
|
|
3490
|
+
let preMintedSecrets;
|
|
3491
|
+
stageMaterializedSkillsForSession();
|
|
3492
|
+
function stageMaterializedSkillsForSession() {
|
|
3493
|
+
try {
|
|
3494
|
+
const { skillsDir } = resolveDriverPaths({
|
|
3495
|
+
driver: opts.driver ?? wsConfig.agent_config?.default?.driver ?? "claude-sdk"
|
|
3496
|
+
});
|
|
3497
|
+
const strict = process.env.SKAILE_STRICT_YAML === "true";
|
|
3498
|
+
const declared = strict ? (wsConfig.dependencies ?? []).flatMap((dep) => {
|
|
3499
|
+
try {
|
|
3500
|
+
const ref = parseAssetRef(dep);
|
|
3501
|
+
return ref.kind === "skill" ? [ref.name] : [];
|
|
3502
|
+
} catch {
|
|
3503
|
+
return [];
|
|
3504
|
+
}
|
|
3505
|
+
}) : [];
|
|
3506
|
+
const stagedSkills = stageMaterializedSkills(opts.projectDir, skillsDir, {
|
|
3507
|
+
strict,
|
|
3508
|
+
declared
|
|
3509
|
+
});
|
|
3510
|
+
if (stagedSkills.length > 0) {
|
|
3511
|
+
log(
|
|
3512
|
+
`[serve] staged ${stagedSkills.length} materialized skill(s): ${stagedSkills.join(", ")}`
|
|
3513
|
+
);
|
|
3514
|
+
}
|
|
3515
|
+
} catch {
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
if (secretsMode === "provisioned") {
|
|
3519
|
+
log("[serve] secrets mode: provisioned \u2014 waiting for provision_secrets command");
|
|
3520
|
+
} else {
|
|
3521
|
+
sessionConfig = buildSessionConfig();
|
|
3522
|
+
agentSession = await createAgentSession(sessionConfig);
|
|
3523
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3524
|
+
registerSessionComposition(agentSession);
|
|
3525
|
+
resolveSessionReady();
|
|
3526
|
+
}
|
|
3527
|
+
let resourceManager = secretsMode === "env" ? agentSession.resourceManager : null;
|
|
3528
|
+
if (secretsMode === "env") {
|
|
3529
|
+
if (!agentSession.provider) {
|
|
3530
|
+
log(
|
|
3531
|
+
`[serve] WARNING: no API key found for ${agentSession.provider ?? "anthropic"} \u2014 agent calls will fail`
|
|
3532
|
+
);
|
|
3533
|
+
log(`[serve] Pass via env var (e.g. ANTHROPIC_API_KEY) or .skaile/settings.json`);
|
|
3534
|
+
}
|
|
3535
|
+
if (resourceManager) {
|
|
3536
|
+
const watched = resourceManager.watchedResources ?? [];
|
|
3537
|
+
if (watched.length > 0) {
|
|
3538
|
+
log(`[serve] watching ${watched.length} resource(s): ${watched.join(", ")}`);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
if (secretsMode === "env" && agentSession.runtimeAssets) {
|
|
3543
|
+
emitRuntimeAssetWarnings(agentSession.runtimeAssets);
|
|
3544
|
+
}
|
|
3545
|
+
validateSkaileYamlDependencies();
|
|
3546
|
+
function emitRuntimeAssetWarnings(runtimeAssets) {
|
|
3547
|
+
for (const w of runtimeAssets.warnings) {
|
|
3548
|
+
const asset = w.code === "missing_driver" && w.declarationId ? `${w.declarationId} \u2192 ${w.name ?? "unknown"}` : w.name ?? w.code;
|
|
3549
|
+
log(`[serve] validation_warning: ${w.code}: ${w.message}`);
|
|
3550
|
+
sendEvent({
|
|
3551
|
+
type: "validation_warning",
|
|
3552
|
+
asset,
|
|
3553
|
+
reason: w.code,
|
|
3554
|
+
suggestion: w.message
|
|
3555
|
+
});
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
function validateSkaileYamlDependencies() {
|
|
3559
|
+
try {
|
|
3560
|
+
const wsConfig2 = resolveSkWorkspaceConfig(opts.projectDir);
|
|
3561
|
+
for (const dep of wsConfig2.dependencies ?? []) {
|
|
3562
|
+
let ref;
|
|
3563
|
+
try {
|
|
3564
|
+
const parsed = parseAssetRef(dep);
|
|
3565
|
+
if (parsed.kind !== "skill") continue;
|
|
3566
|
+
ref = parsed.name;
|
|
3567
|
+
} catch {
|
|
3568
|
+
continue;
|
|
3569
|
+
}
|
|
3570
|
+
const skillDir = join(opts.projectDir, ".claude", "skills", ref);
|
|
3571
|
+
if (!existsSync(skillDir)) {
|
|
3572
|
+
log(`[serve] validation_warning: dependency "${dep}" not found \u2014 continuing without it`);
|
|
3573
|
+
sendEvent({
|
|
3574
|
+
type: "validation_warning",
|
|
3575
|
+
asset: dep,
|
|
3576
|
+
reason: "not_found",
|
|
3577
|
+
suggestion: "Sync the catalog and re-add this asset"
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
} catch {
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
const normalizer = new EventNormalizer();
|
|
3585
|
+
function forwardNormalizedEvent(event) {
|
|
3586
|
+
if (compacting) return;
|
|
3587
|
+
const isTransient = event.type === "status" || event.type === "sync_status" || event.type === "resources_available" || event.type === "resource_response" || event.type === "commands_available";
|
|
3588
|
+
if (!isTransient) {
|
|
3589
|
+
eventSeq++;
|
|
3590
|
+
compactionOrchestrator?.trackMessage(eventSeq);
|
|
3591
|
+
}
|
|
3592
|
+
if (event.type === "finished") {
|
|
3593
|
+
if (!compacting && compactionOrchestrator?.shouldCompact()) {
|
|
3594
|
+
compactionOrchestrator.compact("threshold").catch((err) => {
|
|
3595
|
+
log(
|
|
3596
|
+
`[serve] auto-compaction failed: ${err instanceof Error ? err.message : String(err)}`
|
|
3597
|
+
);
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
if (!resourceManager) {
|
|
3602
|
+
sendEvent(event);
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
const mounts = resourceManager.listConnectors().filter((info) => typeof info.mountPath === "string").map((info) => ({
|
|
3606
|
+
id: info.id,
|
|
3607
|
+
mountPath: info.mountPath
|
|
3608
|
+
}));
|
|
3609
|
+
sendEvent(rewriteFileChangedPath(event, opts.projectDir, mounts));
|
|
3610
|
+
}
|
|
3611
|
+
let driverStarted = false;
|
|
3612
|
+
let driver = secretsMode === "env" ? agentSession.driver : void 0;
|
|
3613
|
+
let secretsProvisioned = secretsMode === "env";
|
|
3614
|
+
let eventSeq = 0;
|
|
3615
|
+
let compacting = false;
|
|
3616
|
+
let compactionOrchestrator = null;
|
|
3617
|
+
const compactionConfig = wsConfig.compaction;
|
|
3618
|
+
function rebuildCompactionOrchestrator() {
|
|
3619
|
+
if (!driver) return;
|
|
3620
|
+
compactionOrchestrator = new CompactionOrchestrator({
|
|
3621
|
+
driver,
|
|
3622
|
+
config: compactionConfig,
|
|
3623
|
+
onCompactionStart: (trigger) => {
|
|
3624
|
+
compacting = true;
|
|
3625
|
+
sendEvent({ type: "status", phase: "compacting" });
|
|
3626
|
+
log(`[serve] compaction started (trigger=${trigger})`);
|
|
3627
|
+
},
|
|
3628
|
+
onSnapshot: (snapshot) => {
|
|
3629
|
+
sendEvent(snapshot);
|
|
3630
|
+
},
|
|
3631
|
+
onCompactionAttempt: (event) => {
|
|
3632
|
+
sendEvent(event);
|
|
3633
|
+
},
|
|
3634
|
+
onCompactionEnd: () => {
|
|
3635
|
+
compacting = false;
|
|
3636
|
+
sendEvent({ type: "status", phase: "idle" });
|
|
3637
|
+
},
|
|
3638
|
+
onLog: (line) => log(`[serve] ${line}`),
|
|
3639
|
+
// Phase 3: stamp every CompactionAttemptEvent with the registry's
|
|
3640
|
+
// current signature so wake-time tier-1 selection has the input it
|
|
3641
|
+
// needs. The registry exists for the entire serve lifetime, so this
|
|
3642
|
+
// closure is safe across orchestrator rebuilds.
|
|
3643
|
+
getCurrentCapabilitySignature: () => computeCapabilitySignature(capabilityRegistry.list())
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
if (secretsMode === "env" && driver) {
|
|
3647
|
+
rebuildCompactionOrchestrator();
|
|
3648
|
+
}
|
|
3649
|
+
const activeFlows = /* @__PURE__ */ new Map();
|
|
3650
|
+
let stimulusBus = null;
|
|
3651
|
+
const FLOW_TURN_TIMEOUT_MS = 4 * 60 * 60 * 1e3;
|
|
3652
|
+
function getOnlyActiveFlow() {
|
|
3653
|
+
const iter = activeFlows.values().next();
|
|
3654
|
+
return iter.done ? null : iter.value;
|
|
3655
|
+
}
|
|
3656
|
+
function getOrCreateStimulusBus() {
|
|
3657
|
+
if (stimulusBus) return stimulusBus;
|
|
3658
|
+
stimulusBus = createSessionStimulusBus({
|
|
3659
|
+
driveTurn: async (promptFragment, metas) => {
|
|
3660
|
+
const lastMeta = metas[metas.length - 1] ?? {};
|
|
3661
|
+
const runId = typeof lastMeta.runId === "string" ? lastMeta.runId : null;
|
|
3662
|
+
const entry = runId ? activeFlows.get(runId) : getOnlyActiveFlow();
|
|
3663
|
+
if (!entry) {
|
|
3664
|
+
serverLog.warn("driveTurn fired with no active flow", { runId });
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
const lastKind = lastMeta.kind;
|
|
3668
|
+
let stimulus;
|
|
3669
|
+
if (lastKind === "flow_started") {
|
|
3670
|
+
stimulus = { kind: "flow_started" };
|
|
3671
|
+
} else if (lastKind === "user_message" && typeof lastMeta.text === "string") {
|
|
3672
|
+
stimulus = {
|
|
3673
|
+
kind: "user_message",
|
|
3674
|
+
text: String(lastMeta.text),
|
|
3675
|
+
senderId: String(lastMeta.senderId ?? "user")
|
|
3676
|
+
};
|
|
3677
|
+
} else {
|
|
3678
|
+
stimulus = { kind: "state_changed" };
|
|
3679
|
+
}
|
|
3680
|
+
const fullPrompt = buildOrchestratorPrompt(
|
|
3681
|
+
entry.flowDef,
|
|
3682
|
+
entry.adapter.getExecution(entry.handle),
|
|
3683
|
+
stimulus
|
|
3684
|
+
);
|
|
3685
|
+
if (!driverStarted) {
|
|
3686
|
+
await driver.start();
|
|
3687
|
+
driverStarted = true;
|
|
3688
|
+
driver.on("agent-event", (event) => {
|
|
3689
|
+
const normalized = normalizer.normalize(event);
|
|
3690
|
+
for (const e of normalized) forwardNormalizedEvent(e);
|
|
3691
|
+
});
|
|
3692
|
+
driver.on("error", (err) => {
|
|
3693
|
+
log(`[serve] driver error: ${err.message}`);
|
|
3694
|
+
sendEvent({ type: "error", message: err.message, fatal: true });
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
3697
|
+
await awaitFlowAgentEnd(fullPrompt, FLOW_TURN_TIMEOUT_MS);
|
|
3246
3698
|
},
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
type: "text",
|
|
3251
|
-
content: text
|
|
3252
|
-
})
|
|
3253
|
-
};
|
|
3699
|
+
log: serverLog
|
|
3700
|
+
});
|
|
3701
|
+
return stimulusBus;
|
|
3254
3702
|
}
|
|
3255
|
-
function
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3703
|
+
function awaitFlowAgentEnd(prompt, timeoutMs) {
|
|
3704
|
+
return new Promise((resolve3, reject) => {
|
|
3705
|
+
let settled = false;
|
|
3706
|
+
const onEvent = (event) => {
|
|
3707
|
+
if (event.type === "agent_end") done();
|
|
3708
|
+
else if (event.type === "error") {
|
|
3709
|
+
const msg = event.error ?? event.message ?? "Agent error";
|
|
3710
|
+
done(new Error(msg));
|
|
3711
|
+
}
|
|
3712
|
+
};
|
|
3713
|
+
const done = (err) => {
|
|
3714
|
+
if (settled) return;
|
|
3715
|
+
settled = true;
|
|
3716
|
+
clearTimeout(timer);
|
|
3717
|
+
driver.off?.(
|
|
3718
|
+
"agent-event",
|
|
3719
|
+
onEvent
|
|
3720
|
+
) ?? driver.removeListener("agent-event", onEvent);
|
|
3721
|
+
if (err) reject(err);
|
|
3722
|
+
else resolve3();
|
|
3723
|
+
};
|
|
3724
|
+
const timer = setTimeout(
|
|
3725
|
+
() => done(new Error(`Flow turn timed out after ${timeoutMs / 1e3}s`)),
|
|
3726
|
+
timeoutMs
|
|
3260
3727
|
);
|
|
3261
|
-
|
|
3728
|
+
driver.on("agent-event", onEvent);
|
|
3729
|
+
driver.prompt(prompt).catch(done);
|
|
3730
|
+
});
|
|
3262
3731
|
}
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
if (status.phase === "stubs_ready") {
|
|
3301
|
-
log(
|
|
3302
|
-
`[serve] ${mountId}: file tree ready (${status.totalFiles} files, ${status.stubFiles} stubs)`
|
|
3303
|
-
);
|
|
3304
|
-
} else if (status.phase === "initial_sync_complete") {
|
|
3732
|
+
async function createActiveFlow(flowDef, seedOrExecution) {
|
|
3733
|
+
if (!resourceManager) {
|
|
3734
|
+
registerBuiltinConnectors();
|
|
3735
|
+
resourceManager = new ConnectorManager(opts.projectDir, {
|
|
3736
|
+
catalogEntries: getSessionCatalogEntries(agentSession)
|
|
3737
|
+
});
|
|
3738
|
+
log("[serve] created ConnectorManager for flow execution (no other connectors declared)");
|
|
3739
|
+
}
|
|
3740
|
+
const bus = getOrCreateStimulusBus();
|
|
3741
|
+
const baseOptions = {
|
|
3742
|
+
stimulusBus: bus,
|
|
3743
|
+
useBusForStimulus: true
|
|
3744
|
+
};
|
|
3745
|
+
const options = "execution" in seedOrExecution ? { flow: flowDef, execution: seedOrExecution.execution, ...baseOptions } : { flow: flowDef, seed: seedOrExecution.seed, ...baseOptions };
|
|
3746
|
+
const seedRunId = "execution" in seedOrExecution ? seedOrExecution.execution.runId : seedOrExecution.seed.runId;
|
|
3747
|
+
const connectorId = `flow:${seedRunId}`;
|
|
3748
|
+
const handle = await resourceManager.connect({
|
|
3749
|
+
id: connectorId,
|
|
3750
|
+
driver: "flow",
|
|
3751
|
+
access: "read-write",
|
|
3752
|
+
options
|
|
3753
|
+
});
|
|
3754
|
+
const adapter = resourceManager.get(connectorId).connector;
|
|
3755
|
+
const runId = adapter.getExecution(handle).runId;
|
|
3756
|
+
adapter.onStateChange(handle, (state) => {
|
|
3757
|
+
sendEvent({
|
|
3758
|
+
type: "state_changed",
|
|
3759
|
+
store: `flow:${state.runId}`,
|
|
3760
|
+
state
|
|
3761
|
+
});
|
|
3762
|
+
});
|
|
3763
|
+
if (agentSession?.driverType === "claude-sdk") {
|
|
3764
|
+
try {
|
|
3765
|
+
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
3766
|
+
if (server) {
|
|
3767
|
+
if (driverStarted) {
|
|
3768
|
+
const prevSessionId = driver.runtimeSessionId;
|
|
3305
3769
|
log(
|
|
3306
|
-
`[serve]
|
|
3770
|
+
`[serve] restarting driver to register flow connector tools (resume=${prevSessionId ?? "none"})`
|
|
3307
3771
|
);
|
|
3772
|
+
await agentSession.dispose();
|
|
3773
|
+
agentSession = await createAgentSession({
|
|
3774
|
+
...sessionConfig,
|
|
3775
|
+
resumeSessionId: prevSessionId
|
|
3776
|
+
});
|
|
3777
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3778
|
+
driver = agentSession.driver;
|
|
3779
|
+
normalizer.reset();
|
|
3780
|
+
driverStarted = false;
|
|
3781
|
+
rebuildCompactionOrchestrator();
|
|
3308
3782
|
}
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
path: event.path,
|
|
3314
|
-
action: event.action,
|
|
3315
|
-
mountId: connectorId
|
|
3316
|
-
});
|
|
3317
|
-
},
|
|
3318
|
-
// Forward connector lifecycle transitions to the platform. This is the
|
|
3319
|
-
// channel deferred (rclone-backed) filesystem mounts report progress on
|
|
3320
|
-
// after session_init_ack — a wedged remote shows as "still syncing".
|
|
3321
|
-
onConnectorStatus: (event) => {
|
|
3322
|
-
sendEvent(event);
|
|
3783
|
+
driver.config.mcpServers = { "skaile-connectors": server };
|
|
3784
|
+
log(`[serve] rebuilt MCP connector tools for flow ${connectorId}`);
|
|
3785
|
+
} else {
|
|
3786
|
+
log(`[serve] buildSdkConnectorTools returned null for flow ${connectorId}`);
|
|
3323
3787
|
}
|
|
3324
|
-
}
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
// recipe-backed) MCP servers are spawned by the RUNNER and their tools
|
|
3333
|
-
// registered as `mcp`-origin capabilities — works on every driver, no
|
|
3334
|
-
// dependence on the claude-agent-sdk `mcpServers` query option.
|
|
3335
|
-
capabilityRegistry,
|
|
3336
|
-
// v3: pre-minted credentials replace the tokenMediator for initial mints.
|
|
3337
|
-
// Refresh routes through `host.refresh_credential` (invoked from the
|
|
3338
|
-
// bridge driver's onAuthError callback). The legacy `tokenMediator`
|
|
3339
|
-
// wire is gone.
|
|
3340
|
-
...preMintedSecrets ? { preMintedSecrets } : {},
|
|
3341
|
-
...aiProviderConfigId ? { aiProviderConfigId } : {}
|
|
3342
|
-
};
|
|
3788
|
+
} catch (err) {
|
|
3789
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3790
|
+
log(`[serve] failed to rebuild MCP tools for flow: ${errMsg}`);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
const entry = { runId, flowDef, adapter, handle, connectorId };
|
|
3794
|
+
activeFlows.set(runId, entry);
|
|
3795
|
+
return entry;
|
|
3343
3796
|
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3797
|
+
const refreshFlagPath = join(opts.projectDir, ".skaile", "refresh-request");
|
|
3798
|
+
try {
|
|
3799
|
+
mkdirSync(join(opts.projectDir, ".skaile"), { recursive: true });
|
|
3800
|
+
if (!existsSync(refreshFlagPath)) {
|
|
3801
|
+
writeFileSync(refreshFlagPath, "", { mode: 432, flag: "a" });
|
|
3802
|
+
}
|
|
3803
|
+
} catch (err) {
|
|
3804
|
+
serverLog.warn("failed to pre-create refresh-flag \u2014 wake-mid-401 disabled", {
|
|
3805
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3806
|
+
});
|
|
3807
|
+
}
|
|
3808
|
+
let lastRefreshAt = 0;
|
|
3809
|
+
const REFRESH_DEBOUNCE_MS = 2e3;
|
|
3810
|
+
let refreshFlagWatcher = null;
|
|
3355
3811
|
try {
|
|
3356
|
-
|
|
3357
|
-
|
|
3812
|
+
refreshFlagWatcher = watch(refreshFlagPath, (event) => {
|
|
3813
|
+
if (event !== "change") return;
|
|
3814
|
+
const now = Date.now();
|
|
3815
|
+
if (now - lastRefreshAt < REFRESH_DEBOUNCE_MS) return;
|
|
3816
|
+
lastRefreshAt = now;
|
|
3817
|
+
void dispatchRefreshToExposedGitMounts(resourceManager, serverLog);
|
|
3818
|
+
});
|
|
3819
|
+
} catch (err) {
|
|
3820
|
+
serverLog.warn("fs.watch on refresh-flag failed \u2014 wake-mid-401 disabled", {
|
|
3821
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3358
3822
|
});
|
|
3359
|
-
|
|
3360
|
-
|
|
3823
|
+
}
|
|
3824
|
+
async function cleanup() {
|
|
3825
|
+
try {
|
|
3826
|
+
await Promise.race([
|
|
3827
|
+
sessionReady,
|
|
3828
|
+
new Promise((resolve3) => {
|
|
3829
|
+
const t = setTimeout(resolve3, 2e3);
|
|
3830
|
+
t.unref?.();
|
|
3831
|
+
})
|
|
3832
|
+
]);
|
|
3833
|
+
} catch {
|
|
3834
|
+
}
|
|
3835
|
+
try {
|
|
3836
|
+
driver?.kill();
|
|
3837
|
+
} catch {
|
|
3838
|
+
}
|
|
3839
|
+
aiCredentialRefreshScheduler.cancel();
|
|
3840
|
+
for (const [callId, pending] of pendingCapabilityCalls) {
|
|
3841
|
+
clearTimeout(pending.timer);
|
|
3842
|
+
pending.reject(new Error(`Session terminated before capability_result (callId=${callId})`));
|
|
3843
|
+
}
|
|
3844
|
+
pendingCapabilityCalls.clear();
|
|
3845
|
+
try {
|
|
3846
|
+
refreshFlagWatcher?.close();
|
|
3847
|
+
} catch {
|
|
3848
|
+
}
|
|
3849
|
+
refreshFlagWatcher = null;
|
|
3850
|
+
if (agentSession) {
|
|
3361
3851
|
try {
|
|
3362
|
-
|
|
3363
|
-
return ref.kind === "skill" ? [ref.name] : [];
|
|
3852
|
+
await agentSession.dispose();
|
|
3364
3853
|
} catch {
|
|
3365
|
-
return [];
|
|
3366
3854
|
}
|
|
3367
|
-
}) : [];
|
|
3368
|
-
const stagedSkills = stageMaterializedSkills(opts.projectDir, skillsDir, { strict, declared });
|
|
3369
|
-
if (stagedSkills.length > 0) {
|
|
3370
|
-
log(
|
|
3371
|
-
`[serve] staged ${stagedSkills.length} materialized skill(s): ${stagedSkills.join(", ")}`
|
|
3372
|
-
);
|
|
3373
3855
|
}
|
|
3374
|
-
} catch {
|
|
3375
|
-
}
|
|
3376
|
-
if (secretsMode === "provisioned") {
|
|
3377
|
-
log("[serve] secrets mode: provisioned \u2014 waiting for provision_secrets command");
|
|
3378
|
-
} else {
|
|
3379
|
-
sessionConfig = buildSessionConfig();
|
|
3380
|
-
agentSession = await createAgentSession(sessionConfig);
|
|
3381
|
-
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3382
|
-
registerSessionComposition(agentSession);
|
|
3383
|
-
resolveSessionReady();
|
|
3384
3856
|
}
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3857
|
+
async function awaitSessionReadyOrFail() {
|
|
3858
|
+
try {
|
|
3859
|
+
await sessionReady;
|
|
3860
|
+
return true;
|
|
3861
|
+
} catch (err) {
|
|
3862
|
+
sendEvent({
|
|
3863
|
+
type: "error",
|
|
3864
|
+
message: `Session not available: ${err.message}`,
|
|
3865
|
+
fatal: true
|
|
3866
|
+
});
|
|
3867
|
+
return false;
|
|
3392
3868
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3869
|
+
}
|
|
3870
|
+
function registerInitClientCapabilities(incoming) {
|
|
3871
|
+
for (const wire of incoming) {
|
|
3872
|
+
if (wire.audience && !wire.audience.includes("llm") && !wire.audience.includes("user")) {
|
|
3873
|
+
continue;
|
|
3397
3874
|
}
|
|
3875
|
+
const stub = {
|
|
3876
|
+
...wire,
|
|
3877
|
+
inputZod: { parse: (v) => v },
|
|
3878
|
+
outputZod: void 0,
|
|
3879
|
+
handler: buildClientCapabilityHandler2(wire)
|
|
3880
|
+
};
|
|
3881
|
+
capabilityRegistry.register(stub, "client");
|
|
3398
3882
|
}
|
|
3399
3883
|
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3884
|
+
async function provisionInitSecrets(secrets) {
|
|
3885
|
+
let initialAiCredentialExpiresAt = null;
|
|
3886
|
+
if (secrets) {
|
|
3887
|
+
const ccdValue = secrets[CLAUDE_CODE_CREDENTIALS_KEY];
|
|
3888
|
+
if (ccdValue) {
|
|
3889
|
+
const home = process.env.HOME ?? homedir();
|
|
3890
|
+
if (home) {
|
|
3891
|
+
try {
|
|
3892
|
+
await writeClaudeCodeCredentialsFile(home, ccdValue);
|
|
3893
|
+
log(
|
|
3894
|
+
`[serve] wrote Claude Code credentials file to ${home}/.claude/.credentials.json (mode 0600)`
|
|
3895
|
+
);
|
|
3896
|
+
} catch (err) {
|
|
3897
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3898
|
+
log(`[serve] failed to write Claude Code credentials file: ${errMsg}`);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
initialAiCredentialExpiresAt = extractClaudeAiOauthExpiresAt(ccdValue);
|
|
3902
|
+
}
|
|
3903
|
+
const otherSecrets = Object.fromEntries(
|
|
3904
|
+
Object.entries(secrets).filter(([k]) => k !== CLAUDE_CODE_CREDENTIALS_KEY)
|
|
3905
|
+
);
|
|
3906
|
+
const provider = new InMemorySecretProvider();
|
|
3907
|
+
provider.provision(otherSecrets);
|
|
3908
|
+
secretProvider = provider;
|
|
3909
|
+
secretsProvisioned = true;
|
|
3910
|
+
}
|
|
3911
|
+
return initialAiCredentialExpiresAt;
|
|
3912
|
+
}
|
|
3913
|
+
async function buildSessionFromInit(secrets) {
|
|
3914
|
+
const effectiveProvider = opts.provider ?? "anthropic";
|
|
3915
|
+
const envKey = providerEnvKey(effectiveProvider);
|
|
3916
|
+
const apiKeyFromSecrets = secrets?.[envKey];
|
|
3917
|
+
sessionConfig = buildSessionConfig(secretProvider, apiKeyFromSecrets);
|
|
3918
|
+
if (!agentSession) {
|
|
3919
|
+
agentSession = await createAgentSession(sessionConfig);
|
|
3920
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3921
|
+
resourceManager = agentSession.resourceManager;
|
|
3922
|
+
driver = agentSession.driver;
|
|
3923
|
+
rebuildCompactionOrchestrator();
|
|
3924
|
+
registerSessionComposition(agentSession);
|
|
3404
3925
|
sendEvent({
|
|
3405
|
-
type: "
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3926
|
+
type: "resources_available",
|
|
3927
|
+
...splitConnectorsForWire(resourceManager)
|
|
3928
|
+
});
|
|
3929
|
+
agentSession.startWatching();
|
|
3930
|
+
capabilityRegistry.setRemoteInvoker(remoteCapabilityInvoker);
|
|
3931
|
+
const runnerCaps = buildRunnerCapabilities({
|
|
3932
|
+
// Resource hot-plug handlers, wired to the live session's
|
|
3933
|
+
// ConnectorManager + PreMintedSecretProvider via getters because
|
|
3934
|
+
// both are lazily (re)assigned in this scope.
|
|
3935
|
+
...buildSessionResourceHandlers({
|
|
3936
|
+
getResourceManager: () => resourceManager,
|
|
3937
|
+
getPreMintedSecrets: () => preMintedSecrets,
|
|
3938
|
+
emitResourcesAvailable: () => {
|
|
3939
|
+
sendEvent({
|
|
3940
|
+
type: "resources_available",
|
|
3941
|
+
...splitConnectorsForWire(resourceManager)
|
|
3942
|
+
});
|
|
3943
|
+
},
|
|
3944
|
+
log
|
|
3945
|
+
})
|
|
3946
|
+
// setLogLevel left undefined — the cap's built-in fallback calls
|
|
3947
|
+
// getLogStore()?.setLevel(...) directly. setState / lifecycle
|
|
3948
|
+
// remain stubs (not resource hot-plug; out of Sprint 2 scope).
|
|
3409
3949
|
});
|
|
3950
|
+
for (const cap of runnerCaps) {
|
|
3951
|
+
capabilityRegistry.register(cap, "agent");
|
|
3952
|
+
}
|
|
3953
|
+
log("[serve] session built from session_init envelope");
|
|
3410
3954
|
}
|
|
3411
3955
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
reason: "not_found",
|
|
3430
|
-
suggestion: "Sync the catalog and re-add this asset"
|
|
3431
|
-
});
|
|
3432
|
-
}
|
|
3956
|
+
async function recreateSessionForInit(pendingResumeSessionId) {
|
|
3957
|
+
await agentSession.dispose();
|
|
3958
|
+
agentSession = await createAgentSession({
|
|
3959
|
+
...sessionConfig,
|
|
3960
|
+
...aiProviderConfigId ? { aiProviderConfigId } : {},
|
|
3961
|
+
...pendingResumeSessionId ? { resumeSessionId: pendingResumeSessionId } : {}
|
|
3962
|
+
});
|
|
3963
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3964
|
+
driver = agentSession.driver;
|
|
3965
|
+
normalizer.reset();
|
|
3966
|
+
driverStarted = false;
|
|
3967
|
+
rebuildCompactionOrchestrator();
|
|
3968
|
+
}
|
|
3969
|
+
async function applyInitSharedState(sharedState) {
|
|
3970
|
+
if (!ensureResourceManagerForSharedState()) {
|
|
3971
|
+
sendEvent({ type: "configured" });
|
|
3972
|
+
return false;
|
|
3433
3973
|
}
|
|
3434
|
-
|
|
3974
|
+
let storesRegistered = 0;
|
|
3975
|
+
for (const stateConfig of sharedState) {
|
|
3976
|
+
if (await registerSharedStateStore(stateConfig)) storesRegistered++;
|
|
3977
|
+
}
|
|
3978
|
+
await rebuildMcpToolsForNewStores(storesRegistered);
|
|
3979
|
+
return true;
|
|
3435
3980
|
}
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3981
|
+
function ensureResourceManagerForSharedState() {
|
|
3982
|
+
if (resourceManager) return true;
|
|
3983
|
+
try {
|
|
3984
|
+
registerBuiltinConnectors();
|
|
3985
|
+
resourceManager = new ConnectorManager(opts.projectDir, {
|
|
3986
|
+
catalogEntries: getSessionCatalogEntries(agentSession)
|
|
3987
|
+
});
|
|
3988
|
+
log("[serve] created ConnectorManager for sharedState (no connectors were declared)");
|
|
3989
|
+
return true;
|
|
3990
|
+
} catch (err) {
|
|
3991
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3992
|
+
log(`[serve] failed to create ConnectorManager: ${errMsg}`);
|
|
3993
|
+
return false;
|
|
3443
3994
|
}
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3995
|
+
}
|
|
3996
|
+
async function registerSharedStateStore(stateConfig) {
|
|
3997
|
+
try {
|
|
3998
|
+
if (resourceManager.has(stateConfig.id)) {
|
|
3999
|
+
try {
|
|
4000
|
+
const snapshot = await resourceManager.executeOp(stateConfig.id, "get", {});
|
|
4001
|
+
const state = snapshot ? JSON.parse(snapshot) : {};
|
|
4002
|
+
sendEvent({
|
|
4003
|
+
type: "state_changed",
|
|
4004
|
+
store: stateConfig.id,
|
|
4005
|
+
state
|
|
4006
|
+
});
|
|
3447
4007
|
log(
|
|
3448
|
-
`[serve]
|
|
4008
|
+
`[serve] sharedState ${stateConfig.id} already registered \u2014 re-emitted current state`
|
|
4009
|
+
);
|
|
4010
|
+
} catch {
|
|
4011
|
+
log(
|
|
4012
|
+
`[serve] sharedState ${stateConfig.id} already registered \u2014 skipping (could not read state)`
|
|
3449
4013
|
);
|
|
4014
|
+
}
|
|
4015
|
+
return false;
|
|
4016
|
+
}
|
|
4017
|
+
const handle = await resourceManager.connect({
|
|
4018
|
+
id: stateConfig.id,
|
|
4019
|
+
driver: stateConfig.adapter,
|
|
4020
|
+
access: "read-write",
|
|
4021
|
+
options: {
|
|
4022
|
+
...stateConfig.definition ?? {},
|
|
4023
|
+
initial: stateConfig.initialState ?? {}
|
|
4024
|
+
}
|
|
4025
|
+
});
|
|
4026
|
+
const entry = resourceManager.get(stateConfig.id);
|
|
4027
|
+
if (typeof entry.connector.onStoreChange === "function") {
|
|
4028
|
+
entry.connector.onStoreChange(handle, (snapshot) => {
|
|
4029
|
+
sendEvent({
|
|
4030
|
+
type: "state_changed",
|
|
4031
|
+
store: stateConfig.id,
|
|
4032
|
+
state: snapshot
|
|
4033
|
+
});
|
|
3450
4034
|
});
|
|
3451
4035
|
}
|
|
4036
|
+
log(`[serve] sharedState registered: ${stateConfig.id} (${stateConfig.adapter})`);
|
|
4037
|
+
sendEvent({
|
|
4038
|
+
type: "state_changed",
|
|
4039
|
+
store: stateConfig.id,
|
|
4040
|
+
state: stateConfig.initialState ?? {}
|
|
4041
|
+
});
|
|
4042
|
+
return true;
|
|
4043
|
+
} catch (err) {
|
|
4044
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4045
|
+
log(`[serve] sharedState registration failed for ${stateConfig.id}: ${errMsg}`);
|
|
4046
|
+
sendEvent({
|
|
4047
|
+
type: "error",
|
|
4048
|
+
message: `sharedState ${stateConfig.id}: ${errMsg}`,
|
|
4049
|
+
fatal: false
|
|
4050
|
+
});
|
|
4051
|
+
return false;
|
|
3452
4052
|
}
|
|
3453
|
-
|
|
3454
|
-
|
|
4053
|
+
}
|
|
4054
|
+
async function rebuildMcpToolsForNewStores(storesRegistered) {
|
|
4055
|
+
if (!(storesRegistered > 0 && !driverStarted && agentSession?.driverType === "claude-sdk")) {
|
|
3455
4056
|
return;
|
|
3456
4057
|
}
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
const compactionConfig = wsConfig.compaction;
|
|
3470
|
-
function rebuildCompactionOrchestrator() {
|
|
3471
|
-
if (!driver) return;
|
|
3472
|
-
compactionOrchestrator = new CompactionOrchestrator({
|
|
3473
|
-
driver,
|
|
3474
|
-
config: compactionConfig,
|
|
3475
|
-
onCompactionStart: (trigger) => {
|
|
3476
|
-
compacting = true;
|
|
3477
|
-
sendEvent({ type: "status", phase: "compacting" });
|
|
3478
|
-
log(`[serve] compaction started (trigger=${trigger})`);
|
|
3479
|
-
},
|
|
3480
|
-
onSnapshot: (snapshot) => {
|
|
3481
|
-
sendEvent(snapshot);
|
|
3482
|
-
},
|
|
3483
|
-
onCompactionAttempt: (event) => {
|
|
3484
|
-
sendEvent(event);
|
|
3485
|
-
},
|
|
3486
|
-
onCompactionEnd: () => {
|
|
3487
|
-
compacting = false;
|
|
3488
|
-
sendEvent({ type: "status", phase: "idle" });
|
|
3489
|
-
},
|
|
3490
|
-
onLog: (line) => log(`[serve] ${line}`),
|
|
3491
|
-
// Phase 3: stamp every CompactionAttemptEvent with the registry's
|
|
3492
|
-
// current signature so wake-time tier-1 selection has the input it
|
|
3493
|
-
// needs. The registry exists for the entire serve lifetime, so this
|
|
3494
|
-
// closure is safe across orchestrator rebuilds.
|
|
3495
|
-
getCurrentCapabilitySignature: () => computeCapabilitySignature(capabilityRegistry.list())
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
if (secretsMode === "env" && driver) {
|
|
3499
|
-
rebuildCompactionOrchestrator();
|
|
4058
|
+
try {
|
|
4059
|
+
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
4060
|
+
if (server) {
|
|
4061
|
+
driver.config.mcpServers = { "skaile-connectors": server };
|
|
4062
|
+
log(`[serve] rebuilt MCP connector tools (${storesRegistered} new store(s))`);
|
|
4063
|
+
} else {
|
|
4064
|
+
log(`[serve] buildSdkConnectorTools returned null`);
|
|
4065
|
+
}
|
|
4066
|
+
} catch (err) {
|
|
4067
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4068
|
+
log(`[serve] failed to rebuild MCP tools: ${errMsg}`);
|
|
4069
|
+
}
|
|
3500
4070
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
4071
|
+
async function applyInitAiResources(aiResources) {
|
|
4072
|
+
const catalogActions = aiResources.filter((a) => a.action);
|
|
4073
|
+
if (catalogActions.length === 0) return;
|
|
4074
|
+
log(`[serve] aiResources catalog actions: ${catalogActions.length}`);
|
|
4075
|
+
try {
|
|
4076
|
+
const skillsDir = join(sessionConfig.projectDir ?? process.cwd(), ".claude", "skills");
|
|
4077
|
+
const assetsRoot = process.env.SKAILE_ASSETS_ROOT ?? findAiAssetsRoot(sessionConfig.projectDir ?? process.cwd()) ?? "/app/ai-assets";
|
|
4078
|
+
for (const res of catalogActions) {
|
|
4079
|
+
applyCatalogAction(res, { assetsRoot, skillsDir });
|
|
4080
|
+
}
|
|
4081
|
+
await restartDriverForSkillChanges(catalogActions);
|
|
4082
|
+
} catch (err) {
|
|
4083
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4084
|
+
log(`[serve] aiResources handling failed: ${errMsg}`);
|
|
4085
|
+
sendEvent({
|
|
4086
|
+
type: "error",
|
|
4087
|
+
message: `aiResources: ${errMsg}`,
|
|
4088
|
+
fatal: false
|
|
4089
|
+
});
|
|
4090
|
+
}
|
|
3507
4091
|
}
|
|
3508
|
-
function
|
|
3509
|
-
if (
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
if (lastKind === "flow_started") {
|
|
3522
|
-
stimulus = { kind: "flow_started" };
|
|
3523
|
-
} else if (lastKind === "user_message" && typeof lastMeta.text === "string") {
|
|
3524
|
-
stimulus = {
|
|
3525
|
-
kind: "user_message",
|
|
3526
|
-
text: String(lastMeta.text),
|
|
3527
|
-
senderId: String(lastMeta.senderId ?? "user")
|
|
3528
|
-
};
|
|
3529
|
-
} else {
|
|
3530
|
-
stimulus = { kind: "state_changed" };
|
|
3531
|
-
}
|
|
3532
|
-
const fullPrompt = buildOrchestratorPrompt(
|
|
3533
|
-
entry.flowDef,
|
|
3534
|
-
entry.adapter.getExecution(entry.handle),
|
|
3535
|
-
stimulus
|
|
4092
|
+
function applyCatalogAction(res, paths) {
|
|
4093
|
+
if (res.action === "add" && res.entry) {
|
|
4094
|
+
const entry = res.entry;
|
|
4095
|
+
const result = deployCatalogEntry(entry, {
|
|
4096
|
+
assetsRoot: paths.assetsRoot,
|
|
4097
|
+
skillsDir: paths.skillsDir,
|
|
4098
|
+
context: res.context
|
|
4099
|
+
});
|
|
4100
|
+
if (result.success) {
|
|
4101
|
+
log(`[serve] deployed skill: ${entry.name ?? res.name}`);
|
|
4102
|
+
} else {
|
|
4103
|
+
log(
|
|
4104
|
+
`[serve] failed to deploy skill ${entry.name ?? res.name}: ${result.error}`
|
|
3536
4105
|
);
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
4106
|
+
}
|
|
4107
|
+
} else if (res.action === "remove") {
|
|
4108
|
+
const removed = undeployCatalogEntry(res.name, paths.skillsDir);
|
|
4109
|
+
log(`[serve] ${removed ? "removed" : "not found"} skill: ${res.name}`);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
async function restartDriverForSkillChanges(catalogActions) {
|
|
4113
|
+
if (!driverStarted) {
|
|
4114
|
+
log("[serve] aiResources applied before driver start \u2014 skills discovered on first prompt");
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
const prevSessionId = driver.runtimeSessionId;
|
|
4118
|
+
log(`[serve] restarting driver to pick up skill changes (resume=${prevSessionId ?? "none"})`);
|
|
4119
|
+
await agentSession.dispose();
|
|
4120
|
+
agentSession = await createAgentSession({
|
|
4121
|
+
...sessionConfig,
|
|
4122
|
+
resumeSessionId: prevSessionId
|
|
4123
|
+
});
|
|
4124
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
4125
|
+
driver = agentSession.driver;
|
|
4126
|
+
normalizer.reset();
|
|
4127
|
+
driverStarted = false;
|
|
4128
|
+
rebuildCompactionOrchestrator();
|
|
4129
|
+
sendEvent({
|
|
4130
|
+
type: "skills_changed",
|
|
4131
|
+
skills: catalogActions.map((a) => ({
|
|
4132
|
+
name: a.entry?.name ?? a.name,
|
|
4133
|
+
action: a.action ?? "add"
|
|
4134
|
+
}))
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
async function handleSessionInit(cmd) {
|
|
4138
|
+
if (cmd.protocolVersion.major !== PROTOCOL_VERSION.major) {
|
|
4139
|
+
log(
|
|
4140
|
+
`[serve] incompatible protocol: agent=${PROTOCOL_VERSION.major} platform=${cmd.protocolVersion.major}`
|
|
4141
|
+
);
|
|
4142
|
+
sendEvent({
|
|
4143
|
+
type: "incompatible_protocol",
|
|
4144
|
+
agentVersion: PROTOCOL_VERSION,
|
|
4145
|
+
platformVersion: cmd.protocolVersion
|
|
4146
|
+
});
|
|
4147
|
+
await cleanup();
|
|
4148
|
+
disposeLogStore();
|
|
4149
|
+
return;
|
|
4150
|
+
}
|
|
4151
|
+
if (runtimeIdentity && runtimeIdentity.containerId !== cmd.identity.containerId) {
|
|
4152
|
+
log(
|
|
4153
|
+
`[serve] session_init_mismatch: expected=${runtimeIdentity.containerId} received=${cmd.identity.containerId}`
|
|
4154
|
+
);
|
|
4155
|
+
sendEvent({
|
|
4156
|
+
type: "session_init_mismatch",
|
|
4157
|
+
expectedContainerId: runtimeIdentity.containerId,
|
|
4158
|
+
receivedContainerId: cmd.identity.containerId
|
|
4159
|
+
});
|
|
4160
|
+
await cleanup();
|
|
4161
|
+
disposeLogStore();
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
runtimeIdentity = cmd.identity;
|
|
4165
|
+
registerInitClientCapabilities(cmd.capabilities ?? []);
|
|
4166
|
+
sendEvent({ type: "protocol_info", version: PROTOCOL_VERSION });
|
|
4167
|
+
preMintedSecrets = new PreMintedSecretProvider(cmd.credentials);
|
|
4168
|
+
const initialAiCredentialExpiresAt = await provisionInitSecrets(cmd.secrets);
|
|
4169
|
+
if (cmd.resolvedConfig.aiProviderConfigId) {
|
|
4170
|
+
aiProviderConfigId = cmd.resolvedConfig.aiProviderConfigId;
|
|
4171
|
+
log(`[serve] stashed aiProviderConfigId=${aiProviderConfigId}`);
|
|
4172
|
+
}
|
|
4173
|
+
if (aiProviderConfigId && initialAiCredentialExpiresAt) {
|
|
4174
|
+
aiCredentialRefreshScheduler.schedule(initialAiCredentialExpiresAt);
|
|
4175
|
+
}
|
|
4176
|
+
sendEvent({
|
|
4177
|
+
type: "session_init_ack",
|
|
4178
|
+
capabilitySignature: computeCapabilitySignature(capabilityRegistry.list())
|
|
4179
|
+
});
|
|
4180
|
+
const buildSession = async () => {
|
|
4181
|
+
await buildSessionFromInit(cmd.secrets);
|
|
4182
|
+
const cfg = {
|
|
4183
|
+
aiResources: cmd.resolvedConfig.aiResources,
|
|
4184
|
+
subagents: cmd.resolvedConfig.subagents,
|
|
4185
|
+
sharedState: cmd.sharedState,
|
|
4186
|
+
resumeSessionId: cmd.resume?.sessionId,
|
|
4187
|
+
expectedCapabilitySignature: cmd.resume?.expectedCapabilitySignature,
|
|
4188
|
+
aiProviderConfigId: cmd.resolvedConfig.aiProviderConfigId
|
|
4189
|
+
};
|
|
4190
|
+
log(
|
|
4191
|
+
`[serve] configure cfg keys=${Object.keys(cfg).join(",")} sharedState=${cfg.sharedState ? cfg.sharedState.length : "undefined"} resourceManager=${resourceManager ? "set" : "null"}`
|
|
4192
|
+
);
|
|
4193
|
+
let pendingResumeSessionId;
|
|
4194
|
+
const cfgResumeSessionId = cfg.resumeSessionId;
|
|
4195
|
+
const cfgExpectedSig = cfg.expectedCapabilitySignature;
|
|
4196
|
+
if (cfgResumeSessionId) {
|
|
4197
|
+
const currentSig = computeCapabilitySignature(capabilityRegistry.list());
|
|
4198
|
+
if (cfgExpectedSig && cfgExpectedSig !== currentSig) {
|
|
4199
|
+
log(
|
|
4200
|
+
`[serve] tier-1 resume rejected: signature mismatch (expected=${cfgExpectedSig.slice(0, 12)} current=${currentSig.slice(0, 12)})`
|
|
4201
|
+
);
|
|
4202
|
+
sendEvent({
|
|
4203
|
+
type: "resume_failed",
|
|
4204
|
+
resumeSessionId: cfgResumeSessionId,
|
|
4205
|
+
reason: "signature_mismatch"
|
|
3543
4206
|
});
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
4207
|
+
} else {
|
|
4208
|
+
log(`[serve] tier-1 resume attempted: resumeSessionId=${cfgResumeSessionId}`);
|
|
4209
|
+
sendEvent({
|
|
4210
|
+
type: "resume_attempted",
|
|
4211
|
+
resumeSessionId: cfgResumeSessionId,
|
|
4212
|
+
capabilitySignature: currentSig
|
|
3547
4213
|
});
|
|
4214
|
+
pendingResumeSessionId = cfgResumeSessionId;
|
|
3548
4215
|
}
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
4216
|
+
}
|
|
4217
|
+
if (pendingResumeSessionId || cfg.subagents) {
|
|
4218
|
+
await recreateSessionForInit(pendingResumeSessionId);
|
|
4219
|
+
}
|
|
4220
|
+
if (cfg.sharedState) {
|
|
4221
|
+
const handled = await applyInitSharedState(cfg.sharedState);
|
|
4222
|
+
if (!handled) throw new Error("session_init: shared-state setup failed");
|
|
4223
|
+
}
|
|
4224
|
+
if (cfg.aiResources) {
|
|
4225
|
+
await applyInitAiResources(cfg.aiResources);
|
|
4226
|
+
}
|
|
4227
|
+
resolveSessionReady();
|
|
4228
|
+
};
|
|
4229
|
+
void buildSession().catch((err) => {
|
|
4230
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4231
|
+
log(`[serve] session_init failed to build session: ${errMsg}`);
|
|
4232
|
+
sendEvent({ type: "error", message: errMsg, fatal: true });
|
|
4233
|
+
rejectSessionReady(err instanceof Error ? err : new Error(errMsg));
|
|
3552
4234
|
});
|
|
3553
|
-
return stimulusBus;
|
|
3554
4235
|
}
|
|
3555
|
-
function
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
4236
|
+
async function handlePrompt(cmd) {
|
|
4237
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4238
|
+
if (!secretsProvisioned) {
|
|
4239
|
+
log("[serve] prompt received before secrets provisioned \u2014 rejecting");
|
|
4240
|
+
sendEvent({
|
|
4241
|
+
type: "error",
|
|
4242
|
+
message: "Secrets not yet provisioned \u2014 send provision_secrets first",
|
|
4243
|
+
fatal: false
|
|
4244
|
+
});
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
|
|
4248
|
+
const activeFlow = getOnlyActiveFlow();
|
|
4249
|
+
if (activeFlow) {
|
|
4250
|
+
sendEvent({ type: "status", phase: "thinking" });
|
|
4251
|
+
try {
|
|
4252
|
+
const bus = getOrCreateStimulusBus();
|
|
4253
|
+
const userStimulus = {
|
|
4254
|
+
kind: "user_message",
|
|
4255
|
+
text: cmd.prompt,
|
|
4256
|
+
senderId: "user"
|
|
4257
|
+
};
|
|
4258
|
+
await bus.signal(activeFlow.connectorId, {
|
|
4259
|
+
promptFragment: renderStimulusPrompt(userStimulus),
|
|
4260
|
+
meta: {
|
|
4261
|
+
kind: "user_message",
|
|
4262
|
+
runId: activeFlow.runId,
|
|
4263
|
+
text: cmd.prompt,
|
|
4264
|
+
senderId: "user"
|
|
4265
|
+
}
|
|
4266
|
+
});
|
|
4267
|
+
} catch (err) {
|
|
4268
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4269
|
+
log(`[serve] flow user-message turn failed: ${errMsg}`);
|
|
4270
|
+
sendEvent({ type: "error", message: `Flow turn failed: ${errMsg}`, fatal: false });
|
|
4271
|
+
}
|
|
4272
|
+
return;
|
|
4273
|
+
}
|
|
4274
|
+
if (!driverStarted) {
|
|
4275
|
+
try {
|
|
4276
|
+
await driver.start();
|
|
4277
|
+
driverStarted = true;
|
|
4278
|
+
} catch (err) {
|
|
4279
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4280
|
+
log(`[serve] driver start failed: ${errMsg}`);
|
|
4281
|
+
sendEvent({ type: "error", message: `Driver start failed: ${errMsg}`, fatal: true });
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
driver.on("agent-event", (event) => {
|
|
4285
|
+
const normalized = normalizer.normalize(event);
|
|
4286
|
+
for (const e of normalized) {
|
|
4287
|
+
forwardNormalizedEvent(e);
|
|
3563
4288
|
}
|
|
3564
|
-
};
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
};
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
timeoutMs
|
|
3579
|
-
);
|
|
3580
|
-
driver.on("agent-event", onEvent);
|
|
3581
|
-
driver.prompt(prompt).catch(done);
|
|
3582
|
-
});
|
|
4289
|
+
});
|
|
4290
|
+
driver.on("error", (err) => {
|
|
4291
|
+
log(`[serve] driver error: ${err.message}`);
|
|
4292
|
+
sendEvent({ type: "error", message: err.message, fatal: true });
|
|
4293
|
+
});
|
|
4294
|
+
}
|
|
4295
|
+
sendEvent({ type: "status", phase: "thinking" });
|
|
4296
|
+
try {
|
|
4297
|
+
await driver.prompt(cmd.prompt);
|
|
4298
|
+
} catch (err) {
|
|
4299
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4300
|
+
log(`[serve] prompt failed: ${errMsg}`);
|
|
4301
|
+
sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
|
|
4302
|
+
}
|
|
3583
4303
|
}
|
|
3584
|
-
async function
|
|
4304
|
+
async function handleReply(cmd) {
|
|
4305
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4306
|
+
log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
|
|
4307
|
+
try {
|
|
4308
|
+
await driver.prompt(cmd.answer);
|
|
4309
|
+
} catch (err) {
|
|
4310
|
+
sendEvent({
|
|
4311
|
+
type: "error",
|
|
4312
|
+
message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4313
|
+
fatal: false
|
|
4314
|
+
});
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
async function handleCancel() {
|
|
4318
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4319
|
+
log("[serve] cancel");
|
|
4320
|
+
try {
|
|
4321
|
+
await driver.abort();
|
|
4322
|
+
} catch (err) {
|
|
4323
|
+
log(`[serve] cancel failed: ${err}`);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
async function handleResourceRequestCommand(cmd) {
|
|
4327
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4328
|
+
if (resourceManager?.has(cmd.connectorId)) {
|
|
4329
|
+
handleMountResourceRequest(cmd, resourceManager, sendEvent);
|
|
4330
|
+
return;
|
|
4331
|
+
}
|
|
3585
4332
|
if (!resourceManager) {
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
4333
|
+
sendEvent({
|
|
4334
|
+
type: "resource_response",
|
|
4335
|
+
requestId: cmd.requestId,
|
|
4336
|
+
resourceId: cmd.connectorId,
|
|
4337
|
+
operation: cmd.operation,
|
|
4338
|
+
error: "No resources available \u2014 no mounts or connectors configured"
|
|
3589
4339
|
});
|
|
3590
|
-
|
|
4340
|
+
return;
|
|
3591
4341
|
}
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
const seedRunId = "execution" in seedOrExecution ? seedOrExecution.execution.runId : seedOrExecution.seed.runId;
|
|
3599
|
-
const connectorId = `flow:${seedRunId}`;
|
|
3600
|
-
const handle = await resourceManager.connect({
|
|
3601
|
-
id: connectorId,
|
|
3602
|
-
driver: "flow",
|
|
3603
|
-
access: "read-write",
|
|
3604
|
-
options
|
|
3605
|
-
});
|
|
3606
|
-
const adapter = resourceManager.get(connectorId).connector;
|
|
3607
|
-
const runId = adapter.getExecution(handle).runId;
|
|
3608
|
-
adapter.onStateChange(handle, (state) => {
|
|
4342
|
+
void handleResourceRequest(cmd, resourceManager, sendEvent);
|
|
4343
|
+
}
|
|
4344
|
+
async function handleConnectorMutate(cmd) {
|
|
4345
|
+
try {
|
|
4346
|
+
await sessionReady;
|
|
4347
|
+
} catch (err) {
|
|
3609
4348
|
sendEvent({
|
|
3610
|
-
type: "
|
|
3611
|
-
|
|
3612
|
-
|
|
4349
|
+
type: "error",
|
|
4350
|
+
message: `Session not available: ${err.message}`,
|
|
4351
|
+
fatal: true
|
|
3613
4352
|
});
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
4353
|
+
return;
|
|
4354
|
+
}
|
|
4355
|
+
try {
|
|
4356
|
+
if (!resourceManager) throw new Error("connector_mutate before resources ready");
|
|
4357
|
+
const targetEntry = resourceManager.has?.(cmd.id) ? resourceManager.get(cmd.id) : null;
|
|
4358
|
+
const isExistingFlow = targetEntry?.connector?.name === "flow";
|
|
4359
|
+
const isBareFlowId = cmd.id === "flow";
|
|
4360
|
+
if (cmd.op === "start") {
|
|
4361
|
+
if (isExistingFlow || isBareFlowId) {
|
|
4362
|
+
const payload = cmd.payload ?? {};
|
|
4363
|
+
const flowDef = payload.flow;
|
|
4364
|
+
const seed = payload.seed;
|
|
4365
|
+
if (flowDef?.id && seed?.runId && seed?.startedBy) {
|
|
4366
|
+
if (activeFlows.size > 0) {
|
|
4367
|
+
serverLog.warn("connector_mutate flow.start: another flow already active", {
|
|
4368
|
+
activeRunIds: Array.from(activeFlows.keys())
|
|
4369
|
+
});
|
|
4370
|
+
return;
|
|
4371
|
+
}
|
|
4372
|
+
const entry = await createActiveFlow(flowDef, { seed });
|
|
4373
|
+
serverLog.info("connector_mutate flow.start", {
|
|
4374
|
+
runId: entry.runId,
|
|
4375
|
+
flowId: flowDef.id
|
|
3628
4376
|
});
|
|
3629
|
-
|
|
3630
|
-
driver = agentSession.driver;
|
|
3631
|
-
normalizer.reset();
|
|
3632
|
-
driverStarted = false;
|
|
3633
|
-
rebuildCompactionOrchestrator();
|
|
4377
|
+
return;
|
|
3634
4378
|
}
|
|
3635
|
-
driver.config.mcpServers = { "skaile-connectors": server };
|
|
3636
|
-
log(`[serve] rebuilt MCP connector tools for flow ${connectorId}`);
|
|
3637
|
-
} else {
|
|
3638
|
-
log(`[serve] buildSdkConnectorTools returned null for flow ${connectorId}`);
|
|
3639
4379
|
}
|
|
3640
|
-
} catch (err) {
|
|
3641
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3642
|
-
log(`[serve] failed to rebuild MCP tools for flow: ${errMsg}`);
|
|
3643
4380
|
}
|
|
4381
|
+
let dispatchId = cmd.id;
|
|
4382
|
+
if (!targetEntry && isBareFlowId) {
|
|
4383
|
+
const onlyActive = getOnlyActiveFlow();
|
|
4384
|
+
if (onlyActive) {
|
|
4385
|
+
dispatchId = onlyActive.connectorId;
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4389
|
+
} catch (err) {
|
|
4390
|
+
serverLog.warn("connector_mutate failed", {
|
|
4391
|
+
id: cmd.id,
|
|
4392
|
+
op: cmd.op,
|
|
4393
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4394
|
+
});
|
|
3644
4395
|
}
|
|
3645
|
-
const entry = { runId, flowDef, adapter, handle, connectorId };
|
|
3646
|
-
activeFlows.set(runId, entry);
|
|
3647
|
-
return entry;
|
|
3648
4396
|
}
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
4397
|
+
async function handleConnectorQuery(cmd) {
|
|
4398
|
+
try {
|
|
4399
|
+
await sessionReady;
|
|
4400
|
+
} catch (err) {
|
|
4401
|
+
sendEvent({
|
|
4402
|
+
type: "error",
|
|
4403
|
+
message: `Session not available: ${err.message}`,
|
|
4404
|
+
fatal: true
|
|
4405
|
+
});
|
|
4406
|
+
return;
|
|
3654
4407
|
}
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
4408
|
+
let ok = true;
|
|
4409
|
+
let result;
|
|
4410
|
+
let error;
|
|
4411
|
+
try {
|
|
4412
|
+
if (!resourceManager) throw new Error("connector_query before resources ready");
|
|
4413
|
+
let dispatchId = cmd.id;
|
|
4414
|
+
if (!resourceManager.has?.(cmd.id) && cmd.id === "flow") {
|
|
4415
|
+
const onlyActive = getOnlyActiveFlow();
|
|
4416
|
+
if (onlyActive) {
|
|
4417
|
+
dispatchId = onlyActive.connectorId;
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
result = await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4421
|
+
} catch (err) {
|
|
4422
|
+
ok = false;
|
|
4423
|
+
error = err instanceof Error ? err.message : String(err);
|
|
4424
|
+
}
|
|
4425
|
+
sendEvent({
|
|
4426
|
+
type: "connector_query_response",
|
|
4427
|
+
requestId: cmd.requestId,
|
|
4428
|
+
ok,
|
|
4429
|
+
...result !== void 0 ? { result } : {},
|
|
4430
|
+
...error !== void 0 ? { error } : {}
|
|
3658
4431
|
});
|
|
3659
4432
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
4433
|
+
async function computeDebugResult(cmd) {
|
|
4434
|
+
switch (cmd.query) {
|
|
4435
|
+
case "tools": {
|
|
4436
|
+
const mcpServers = Object.keys(driver?.config?.mcpServers ?? {});
|
|
4437
|
+
const allowedTools = driver?.config?.allowedTools ?? null;
|
|
4438
|
+
return { mcpServers, allowedTools, driverStarted };
|
|
4439
|
+
}
|
|
4440
|
+
case "connectors": {
|
|
4441
|
+
return resourceManager?.listConnectors?.() ?? [];
|
|
4442
|
+
}
|
|
4443
|
+
case "mcp": {
|
|
4444
|
+
const servers = Object.entries(driver?.config?.mcpServers ?? {}).map(([name]) => ({
|
|
4445
|
+
name
|
|
4446
|
+
}));
|
|
4447
|
+
return { servers };
|
|
4448
|
+
}
|
|
4449
|
+
case "config": {
|
|
4450
|
+
const liveModel = driver?.getModel() ?? agentSession?.model;
|
|
4451
|
+
return {
|
|
4452
|
+
driver: agentSession?.driverType,
|
|
4453
|
+
model: liveModel,
|
|
4454
|
+
provider: agentSession?.provider,
|
|
4455
|
+
maxTurns,
|
|
4456
|
+
secretsMode,
|
|
4457
|
+
driverStarted,
|
|
4458
|
+
projectDir: opts.projectDir,
|
|
4459
|
+
versions: getFrameworkVersions()
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
case "state": {
|
|
4463
|
+
const storeId = cmd.args?.store;
|
|
4464
|
+
if (storeId) {
|
|
4465
|
+
try {
|
|
4466
|
+
const snapshot = await resourceManager?.executeOp(storeId, "get", {});
|
|
4467
|
+
return snapshot ? JSON.parse(snapshot) : null;
|
|
4468
|
+
} catch (err) {
|
|
4469
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
const all = resourceManager?.listConnectors?.() ?? [];
|
|
4473
|
+
return { stores: all.map((c) => c.id) };
|
|
4474
|
+
}
|
|
4475
|
+
case "commands": {
|
|
4476
|
+
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4477
|
+
return { commands, driverType: agentSession?.driverType };
|
|
4478
|
+
}
|
|
4479
|
+
default:
|
|
4480
|
+
return { error: `Unknown debug query: ${cmd.query}` };
|
|
4481
|
+
}
|
|
3675
4482
|
}
|
|
3676
|
-
async function
|
|
4483
|
+
async function handleDebug(cmd) {
|
|
4484
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4485
|
+
log(`[serve] debug query=${cmd.query}`);
|
|
4486
|
+
let result;
|
|
3677
4487
|
try {
|
|
3678
|
-
await
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
const t = setTimeout(resolve3, 2e3);
|
|
3682
|
-
t.unref?.();
|
|
3683
|
-
})
|
|
3684
|
-
]);
|
|
3685
|
-
} catch {
|
|
4488
|
+
result = await computeDebugResult(cmd);
|
|
4489
|
+
} catch (err) {
|
|
4490
|
+
result = { error: err instanceof Error ? err.message : String(err) };
|
|
3686
4491
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
4492
|
+
sendEvent({ type: "debug_result", query: cmd.query, data: result });
|
|
4493
|
+
}
|
|
4494
|
+
async function handleShutdown() {
|
|
4495
|
+
log("[serve] shutdown requested");
|
|
4496
|
+
await cleanup();
|
|
4497
|
+
if (transport instanceof WebSocketServerTransport) {
|
|
4498
|
+
await transport.close();
|
|
4499
|
+
}
|
|
4500
|
+
disposeLogStore();
|
|
4501
|
+
process.exit(0);
|
|
4502
|
+
}
|
|
4503
|
+
function handleCapabilityResult(cmd) {
|
|
4504
|
+
const matched = resolveCapabilityResult(pendingCapabilityCalls, cmd);
|
|
4505
|
+
if (!matched) {
|
|
4506
|
+
log(
|
|
4507
|
+
`[serve] capability_result for unknown callId=${cmd.callId} (timed out or already resolved); dropping`
|
|
4508
|
+
);
|
|
3690
4509
|
}
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4510
|
+
}
|
|
4511
|
+
function handleCapabilityRegister(cmd) {
|
|
4512
|
+
for (const raw of cmd.capabilities) {
|
|
4513
|
+
const wire = raw;
|
|
4514
|
+
capabilityRegistry.register(
|
|
4515
|
+
{
|
|
4516
|
+
...wire,
|
|
4517
|
+
inputZod: { parse: (v) => v },
|
|
4518
|
+
outputZod: void 0,
|
|
4519
|
+
handler: buildClientCapabilityHandler2(wire)
|
|
4520
|
+
},
|
|
4521
|
+
"client"
|
|
4522
|
+
);
|
|
3695
4523
|
}
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
4524
|
+
}
|
|
4525
|
+
function handleCapabilityDeregister(cmd) {
|
|
4526
|
+
for (const name of cmd.names) {
|
|
4527
|
+
capabilityRegistry.deregister(name);
|
|
3700
4528
|
}
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
4529
|
+
}
|
|
4530
|
+
function handleCapabilityApprove(cmd) {
|
|
4531
|
+
if (cmd.decision === "rejected") {
|
|
4532
|
+
const matched = rejectCapabilityOnApprovalDeny(
|
|
4533
|
+
pendingCapabilityCalls,
|
|
4534
|
+
cmd.callId,
|
|
4535
|
+
cmd.feedback
|
|
4536
|
+
);
|
|
4537
|
+
if (!matched) {
|
|
4538
|
+
log(
|
|
4539
|
+
`[serve] capability_approve(rejected) for unknown callId=${cmd.callId} (already resolved or timed out); dropping`
|
|
4540
|
+
);
|
|
4541
|
+
} else {
|
|
4542
|
+
log(
|
|
4543
|
+
`[serve] capability_approve(rejected) callId=${cmd.callId} decidedBy=${cmd.decidedBy}${cmd.feedback ? ` feedback=${JSON.stringify(cmd.feedback)}` : ""}`
|
|
4544
|
+
);
|
|
3706
4545
|
}
|
|
4546
|
+
} else {
|
|
4547
|
+
log(
|
|
4548
|
+
`[serve] capability_approve(approved) callId=${cmd.callId} decidedBy=${cmd.decidedBy} (awaiting capability_result)`
|
|
4549
|
+
);
|
|
3707
4550
|
}
|
|
3708
4551
|
}
|
|
3709
4552
|
async function handleCommand(cmd) {
|
|
@@ -3719,492 +4562,23 @@ async function startAgentServer(opts) {
|
|
|
3719
4562
|
return;
|
|
3720
4563
|
}
|
|
3721
4564
|
switch (cmd.type) {
|
|
3722
|
-
case "session_init":
|
|
3723
|
-
|
|
3724
|
-
log(
|
|
3725
|
-
`[serve] incompatible protocol: agent=${PROTOCOL_VERSION.major} platform=${cmd.protocolVersion.major}`
|
|
3726
|
-
);
|
|
3727
|
-
sendEvent({
|
|
3728
|
-
type: "incompatible_protocol",
|
|
3729
|
-
agentVersion: PROTOCOL_VERSION,
|
|
3730
|
-
platformVersion: cmd.protocolVersion
|
|
3731
|
-
});
|
|
3732
|
-
await cleanup();
|
|
3733
|
-
disposeLogStore();
|
|
3734
|
-
return;
|
|
3735
|
-
}
|
|
3736
|
-
if (runtimeIdentity && runtimeIdentity.containerId !== cmd.identity.containerId) {
|
|
3737
|
-
log(
|
|
3738
|
-
`[serve] session_init_mismatch: expected=${runtimeIdentity.containerId} received=${cmd.identity.containerId}`
|
|
3739
|
-
);
|
|
3740
|
-
sendEvent({
|
|
3741
|
-
type: "session_init_mismatch",
|
|
3742
|
-
expectedContainerId: runtimeIdentity.containerId,
|
|
3743
|
-
receivedContainerId: cmd.identity.containerId
|
|
3744
|
-
});
|
|
3745
|
-
await cleanup();
|
|
3746
|
-
disposeLogStore();
|
|
3747
|
-
return;
|
|
3748
|
-
}
|
|
3749
|
-
runtimeIdentity = cmd.identity;
|
|
3750
|
-
const incoming = cmd.capabilities ?? [];
|
|
3751
|
-
for (const wire of incoming) {
|
|
3752
|
-
if (wire.audience && !wire.audience.includes("llm") && !wire.audience.includes("user")) {
|
|
3753
|
-
continue;
|
|
3754
|
-
}
|
|
3755
|
-
const stub = {
|
|
3756
|
-
...wire,
|
|
3757
|
-
inputZod: { parse: (v) => v },
|
|
3758
|
-
outputZod: void 0,
|
|
3759
|
-
handler: buildClientCapabilityHandler2(wire)
|
|
3760
|
-
};
|
|
3761
|
-
capabilityRegistry.register(stub, "client");
|
|
3762
|
-
}
|
|
3763
|
-
sendEvent({ type: "protocol_info", version: PROTOCOL_VERSION });
|
|
3764
|
-
preMintedSecrets = new PreMintedSecretProvider(cmd.credentials);
|
|
3765
|
-
let initialAiCredentialExpiresAt = null;
|
|
3766
|
-
if (cmd.secrets) {
|
|
3767
|
-
const ccdValue = cmd.secrets[CLAUDE_CODE_CREDENTIALS_KEY];
|
|
3768
|
-
if (ccdValue) {
|
|
3769
|
-
const home = process.env.HOME ?? homedir();
|
|
3770
|
-
if (home) {
|
|
3771
|
-
try {
|
|
3772
|
-
await writeClaudeCodeCredentialsFile(home, ccdValue);
|
|
3773
|
-
log(
|
|
3774
|
-
`[serve] wrote Claude Code credentials file to ${home}/.claude/.credentials.json (mode 0600)`
|
|
3775
|
-
);
|
|
3776
|
-
} catch (err) {
|
|
3777
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3778
|
-
log(`[serve] failed to write Claude Code credentials file: ${errMsg}`);
|
|
3779
|
-
}
|
|
3780
|
-
}
|
|
3781
|
-
initialAiCredentialExpiresAt = extractClaudeAiOauthExpiresAt(ccdValue);
|
|
3782
|
-
}
|
|
3783
|
-
const otherSecrets = Object.fromEntries(
|
|
3784
|
-
Object.entries(cmd.secrets).filter(([k]) => k !== CLAUDE_CODE_CREDENTIALS_KEY)
|
|
3785
|
-
);
|
|
3786
|
-
const provider = new InMemorySecretProvider();
|
|
3787
|
-
provider.provision(otherSecrets);
|
|
3788
|
-
secretProvider = provider;
|
|
3789
|
-
secretsProvisioned = true;
|
|
3790
|
-
}
|
|
3791
|
-
try {
|
|
3792
|
-
const effectiveProvider = opts.provider ?? "anthropic";
|
|
3793
|
-
const envKey = providerEnvKey(effectiveProvider);
|
|
3794
|
-
const apiKeyFromSecrets = cmd.secrets?.[envKey];
|
|
3795
|
-
sessionConfig = buildSessionConfig(secretProvider, apiKeyFromSecrets);
|
|
3796
|
-
if (!agentSession) {
|
|
3797
|
-
agentSession = await createAgentSession(sessionConfig);
|
|
3798
|
-
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3799
|
-
resourceManager = agentSession.resourceManager;
|
|
3800
|
-
driver = agentSession.driver;
|
|
3801
|
-
rebuildCompactionOrchestrator();
|
|
3802
|
-
registerSessionComposition(agentSession);
|
|
3803
|
-
sendEvent({
|
|
3804
|
-
type: "resources_available",
|
|
3805
|
-
...splitConnectorsForWire(resourceManager)
|
|
3806
|
-
});
|
|
3807
|
-
agentSession.startWatching();
|
|
3808
|
-
capabilityRegistry.setRemoteInvoker(remoteCapabilityInvoker);
|
|
3809
|
-
const runnerCaps = buildRunnerCapabilities({
|
|
3810
|
-
// Resource hot-plug handlers, wired to the live session's
|
|
3811
|
-
// ConnectorManager + PreMintedSecretProvider via getters because
|
|
3812
|
-
// both are lazily (re)assigned in this scope.
|
|
3813
|
-
...buildSessionResourceHandlers({
|
|
3814
|
-
getResourceManager: () => resourceManager,
|
|
3815
|
-
getPreMintedSecrets: () => preMintedSecrets,
|
|
3816
|
-
emitResourcesAvailable: () => {
|
|
3817
|
-
sendEvent({
|
|
3818
|
-
type: "resources_available",
|
|
3819
|
-
...splitConnectorsForWire(resourceManager)
|
|
3820
|
-
});
|
|
3821
|
-
},
|
|
3822
|
-
log
|
|
3823
|
-
})
|
|
3824
|
-
// setLogLevel left undefined — the cap's built-in fallback calls
|
|
3825
|
-
// getLogStore()?.setLevel(...) directly. setState / lifecycle
|
|
3826
|
-
// remain stubs (not resource hot-plug; out of Sprint 2 scope).
|
|
3827
|
-
});
|
|
3828
|
-
for (const cap of runnerCaps) {
|
|
3829
|
-
capabilityRegistry.register(cap, "agent");
|
|
3830
|
-
}
|
|
3831
|
-
resolveSessionReady();
|
|
3832
|
-
log("[serve] session built from session_init envelope");
|
|
3833
|
-
}
|
|
3834
|
-
} catch (err) {
|
|
3835
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3836
|
-
log(`[serve] session_init failed to build session: ${errMsg}`);
|
|
3837
|
-
sendEvent({ type: "error", message: errMsg, fatal: true });
|
|
3838
|
-
rejectSessionReady(err instanceof Error ? err : new Error(errMsg));
|
|
3839
|
-
}
|
|
3840
|
-
if (cmd.resolvedConfig.aiProviderConfigId) {
|
|
3841
|
-
aiProviderConfigId = cmd.resolvedConfig.aiProviderConfigId;
|
|
3842
|
-
log(`[serve] stashed aiProviderConfigId=${aiProviderConfigId}`);
|
|
3843
|
-
}
|
|
3844
|
-
if (aiProviderConfigId && initialAiCredentialExpiresAt) {
|
|
3845
|
-
aiCredentialRefreshScheduler.schedule(initialAiCredentialExpiresAt);
|
|
3846
|
-
}
|
|
3847
|
-
const cfg = {
|
|
3848
|
-
aiResources: cmd.resolvedConfig.aiResources,
|
|
3849
|
-
subagents: cmd.resolvedConfig.subagents,
|
|
3850
|
-
sharedState: cmd.sharedState,
|
|
3851
|
-
resumeSessionId: cmd.resume?.sessionId,
|
|
3852
|
-
expectedCapabilitySignature: cmd.resume?.expectedCapabilitySignature,
|
|
3853
|
-
aiProviderConfigId: cmd.resolvedConfig.aiProviderConfigId
|
|
3854
|
-
};
|
|
3855
|
-
log(
|
|
3856
|
-
`[serve] configure cfg keys=${Object.keys(cfg).join(",")} sharedState=${cfg.sharedState ? cfg.sharedState.length : "undefined"} resourceManager=${resourceManager ? "set" : "null"}`
|
|
3857
|
-
);
|
|
3858
|
-
let pendingResumeSessionId;
|
|
3859
|
-
const cfgResumeSessionId = cfg.resumeSessionId;
|
|
3860
|
-
const cfgExpectedSig = cfg.expectedCapabilitySignature;
|
|
3861
|
-
if (cfgResumeSessionId) {
|
|
3862
|
-
const currentSig = computeCapabilitySignature(capabilityRegistry.list());
|
|
3863
|
-
if (cfgExpectedSig && cfgExpectedSig !== currentSig) {
|
|
3864
|
-
log(
|
|
3865
|
-
`[serve] tier-1 resume rejected: signature mismatch (expected=${cfgExpectedSig.slice(0, 12)} current=${currentSig.slice(0, 12)})`
|
|
3866
|
-
);
|
|
3867
|
-
sendEvent({
|
|
3868
|
-
type: "resume_failed",
|
|
3869
|
-
resumeSessionId: cfgResumeSessionId,
|
|
3870
|
-
reason: "signature_mismatch"
|
|
3871
|
-
});
|
|
3872
|
-
} else {
|
|
3873
|
-
log(`[serve] tier-1 resume attempted: resumeSessionId=${cfgResumeSessionId}`);
|
|
3874
|
-
sendEvent({
|
|
3875
|
-
type: "resume_attempted",
|
|
3876
|
-
resumeSessionId: cfgResumeSessionId,
|
|
3877
|
-
capabilitySignature: currentSig
|
|
3878
|
-
});
|
|
3879
|
-
pendingResumeSessionId = cfgResumeSessionId;
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
if (pendingResumeSessionId || cfg.subagents) {
|
|
3883
|
-
await agentSession.dispose();
|
|
3884
|
-
agentSession = await createAgentSession({
|
|
3885
|
-
...sessionConfig,
|
|
3886
|
-
...aiProviderConfigId ? { aiProviderConfigId } : {},
|
|
3887
|
-
...pendingResumeSessionId ? { resumeSessionId: pendingResumeSessionId } : {}
|
|
3888
|
-
});
|
|
3889
|
-
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3890
|
-
driver = agentSession.driver;
|
|
3891
|
-
normalizer.reset();
|
|
3892
|
-
driverStarted = false;
|
|
3893
|
-
rebuildCompactionOrchestrator();
|
|
3894
|
-
}
|
|
3895
|
-
if (cfg.sharedState) {
|
|
3896
|
-
if (!resourceManager) {
|
|
3897
|
-
try {
|
|
3898
|
-
registerBuiltinConnectors();
|
|
3899
|
-
resourceManager = new ConnectorManager(opts.projectDir, {
|
|
3900
|
-
catalogEntries: getSessionCatalogEntries(agentSession)
|
|
3901
|
-
});
|
|
3902
|
-
log("[serve] created ConnectorManager for sharedState (no connectors were declared)");
|
|
3903
|
-
} catch (err) {
|
|
3904
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3905
|
-
log(`[serve] failed to create ConnectorManager: ${errMsg}`);
|
|
3906
|
-
sendEvent({ type: "configured" });
|
|
3907
|
-
break;
|
|
3908
|
-
}
|
|
3909
|
-
}
|
|
3910
|
-
let storesRegistered = 0;
|
|
3911
|
-
for (const stateConfig of cfg.sharedState) {
|
|
3912
|
-
try {
|
|
3913
|
-
if (resourceManager.has(stateConfig.id)) {
|
|
3914
|
-
try {
|
|
3915
|
-
const snapshot = await resourceManager.executeOp(stateConfig.id, "get", {});
|
|
3916
|
-
const state = snapshot ? JSON.parse(snapshot) : {};
|
|
3917
|
-
sendEvent({
|
|
3918
|
-
type: "state_changed",
|
|
3919
|
-
store: stateConfig.id,
|
|
3920
|
-
state
|
|
3921
|
-
});
|
|
3922
|
-
log(
|
|
3923
|
-
`[serve] sharedState ${stateConfig.id} already registered \u2014 re-emitted current state`
|
|
3924
|
-
);
|
|
3925
|
-
} catch {
|
|
3926
|
-
log(
|
|
3927
|
-
`[serve] sharedState ${stateConfig.id} already registered \u2014 skipping (could not read state)`
|
|
3928
|
-
);
|
|
3929
|
-
}
|
|
3930
|
-
continue;
|
|
3931
|
-
}
|
|
3932
|
-
const handle = await resourceManager.connect({
|
|
3933
|
-
id: stateConfig.id,
|
|
3934
|
-
driver: stateConfig.adapter,
|
|
3935
|
-
access: "read-write",
|
|
3936
|
-
options: {
|
|
3937
|
-
...stateConfig.definition ?? {},
|
|
3938
|
-
initial: stateConfig.initialState ?? {}
|
|
3939
|
-
}
|
|
3940
|
-
});
|
|
3941
|
-
const entry = resourceManager.get(stateConfig.id);
|
|
3942
|
-
if (typeof entry.connector.onStoreChange === "function") {
|
|
3943
|
-
entry.connector.onStoreChange(handle, (snapshot) => {
|
|
3944
|
-
sendEvent({
|
|
3945
|
-
type: "state_changed",
|
|
3946
|
-
store: stateConfig.id,
|
|
3947
|
-
state: snapshot
|
|
3948
|
-
});
|
|
3949
|
-
});
|
|
3950
|
-
}
|
|
3951
|
-
log(`[serve] sharedState registered: ${stateConfig.id} (${stateConfig.adapter})`);
|
|
3952
|
-
storesRegistered++;
|
|
3953
|
-
sendEvent({
|
|
3954
|
-
type: "state_changed",
|
|
3955
|
-
store: stateConfig.id,
|
|
3956
|
-
state: stateConfig.initialState ?? {}
|
|
3957
|
-
});
|
|
3958
|
-
} catch (err) {
|
|
3959
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3960
|
-
log(`[serve] sharedState registration failed for ${stateConfig.id}: ${errMsg}`);
|
|
3961
|
-
sendEvent({
|
|
3962
|
-
type: "error",
|
|
3963
|
-
message: `sharedState ${stateConfig.id}: ${errMsg}`,
|
|
3964
|
-
fatal: false
|
|
3965
|
-
});
|
|
3966
|
-
}
|
|
3967
|
-
}
|
|
3968
|
-
if (storesRegistered > 0 && !driverStarted && agentSession?.driverType === "claude-sdk") {
|
|
3969
|
-
try {
|
|
3970
|
-
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
3971
|
-
if (server) {
|
|
3972
|
-
driver.config.mcpServers = { "skaile-connectors": server };
|
|
3973
|
-
log(`[serve] rebuilt MCP connector tools (${storesRegistered} new store(s))`);
|
|
3974
|
-
} else {
|
|
3975
|
-
log(`[serve] buildSdkConnectorTools returned null`);
|
|
3976
|
-
}
|
|
3977
|
-
} catch (err) {
|
|
3978
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3979
|
-
log(`[serve] failed to rebuild MCP tools: ${errMsg}`);
|
|
3980
|
-
}
|
|
3981
|
-
}
|
|
3982
|
-
}
|
|
3983
|
-
if (cfg.aiResources) {
|
|
3984
|
-
const catalogActions = cfg.aiResources.filter((a) => a.action);
|
|
3985
|
-
if (catalogActions.length > 0) {
|
|
3986
|
-
log(`[serve] aiResources catalog actions: ${catalogActions.length}`);
|
|
3987
|
-
try {
|
|
3988
|
-
const skillsDir = join(
|
|
3989
|
-
sessionConfig.projectDir ?? process.cwd(),
|
|
3990
|
-
".claude",
|
|
3991
|
-
"skills"
|
|
3992
|
-
);
|
|
3993
|
-
const assetsRoot = process.env.SKAILE_ASSETS_ROOT ?? findAiAssetsRoot(sessionConfig.projectDir ?? process.cwd()) ?? "/app/ai-assets";
|
|
3994
|
-
for (const res of catalogActions) {
|
|
3995
|
-
if (res.action === "add" && res.entry) {
|
|
3996
|
-
const entry = res.entry;
|
|
3997
|
-
const result = deployCatalogEntry(entry, {
|
|
3998
|
-
assetsRoot,
|
|
3999
|
-
skillsDir,
|
|
4000
|
-
context: res.context
|
|
4001
|
-
});
|
|
4002
|
-
if (result.success) {
|
|
4003
|
-
log(`[serve] deployed skill: ${entry.name ?? res.name}`);
|
|
4004
|
-
} else {
|
|
4005
|
-
log(
|
|
4006
|
-
`[serve] failed to deploy skill ${entry.name ?? res.name}: ${result.error}`
|
|
4007
|
-
);
|
|
4008
|
-
}
|
|
4009
|
-
} else if (res.action === "remove") {
|
|
4010
|
-
const removed = undeployCatalogEntry(res.name, skillsDir);
|
|
4011
|
-
log(`[serve] ${removed ? "removed" : "not found"} skill: ${res.name}`);
|
|
4012
|
-
}
|
|
4013
|
-
}
|
|
4014
|
-
if (driverStarted) {
|
|
4015
|
-
const prevSessionId = driver.runtimeSessionId;
|
|
4016
|
-
log(
|
|
4017
|
-
`[serve] restarting driver to pick up skill changes (resume=${prevSessionId ?? "none"})`
|
|
4018
|
-
);
|
|
4019
|
-
await agentSession.dispose();
|
|
4020
|
-
agentSession = await createAgentSession({
|
|
4021
|
-
...sessionConfig,
|
|
4022
|
-
resumeSessionId: prevSessionId
|
|
4023
|
-
});
|
|
4024
|
-
emitSystemPromptComposed(agentSession, sendEvent);
|
|
4025
|
-
driver = agentSession.driver;
|
|
4026
|
-
normalizer.reset();
|
|
4027
|
-
driverStarted = false;
|
|
4028
|
-
rebuildCompactionOrchestrator();
|
|
4029
|
-
sendEvent({
|
|
4030
|
-
type: "skills_changed",
|
|
4031
|
-
skills: catalogActions.map((a) => ({
|
|
4032
|
-
name: a.entry?.name ?? a.name,
|
|
4033
|
-
action: a.action ?? "add"
|
|
4034
|
-
}))
|
|
4035
|
-
});
|
|
4036
|
-
} else {
|
|
4037
|
-
log(
|
|
4038
|
-
"[serve] aiResources applied before driver start \u2014 skills discovered on first prompt"
|
|
4039
|
-
);
|
|
4040
|
-
}
|
|
4041
|
-
} catch (err) {
|
|
4042
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4043
|
-
log(`[serve] aiResources handling failed: ${errMsg}`);
|
|
4044
|
-
sendEvent({
|
|
4045
|
-
type: "error",
|
|
4046
|
-
message: `aiResources: ${errMsg}`,
|
|
4047
|
-
fatal: false
|
|
4048
|
-
});
|
|
4049
|
-
}
|
|
4050
|
-
}
|
|
4051
|
-
}
|
|
4052
|
-
sendEvent({
|
|
4053
|
-
type: "session_init_ack",
|
|
4054
|
-
capabilitySignature: computeCapabilitySignature(capabilityRegistry.list())
|
|
4055
|
-
});
|
|
4565
|
+
case "session_init":
|
|
4566
|
+
await handleSessionInit(cmd);
|
|
4056
4567
|
break;
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
try {
|
|
4060
|
-
await sessionReady;
|
|
4061
|
-
} catch (err) {
|
|
4062
|
-
sendEvent({
|
|
4063
|
-
type: "error",
|
|
4064
|
-
message: `Session not available: ${err.message}`,
|
|
4065
|
-
fatal: true
|
|
4066
|
-
});
|
|
4067
|
-
break;
|
|
4068
|
-
}
|
|
4069
|
-
if (!secretsProvisioned) {
|
|
4070
|
-
log("[serve] prompt received before secrets provisioned \u2014 rejecting");
|
|
4071
|
-
sendEvent({
|
|
4072
|
-
type: "error",
|
|
4073
|
-
message: "Secrets not yet provisioned \u2014 send provision_secrets first",
|
|
4074
|
-
fatal: false
|
|
4075
|
-
});
|
|
4076
|
-
break;
|
|
4077
|
-
}
|
|
4078
|
-
log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
|
|
4079
|
-
const activeFlow = getOnlyActiveFlow();
|
|
4080
|
-
if (activeFlow) {
|
|
4081
|
-
sendEvent({ type: "status", phase: "thinking" });
|
|
4082
|
-
try {
|
|
4083
|
-
const bus = getOrCreateStimulusBus();
|
|
4084
|
-
const userStimulus = {
|
|
4085
|
-
kind: "user_message",
|
|
4086
|
-
text: cmd.prompt,
|
|
4087
|
-
senderId: "user"
|
|
4088
|
-
};
|
|
4089
|
-
await bus.signal(activeFlow.connectorId, {
|
|
4090
|
-
promptFragment: renderStimulusPrompt(userStimulus),
|
|
4091
|
-
meta: {
|
|
4092
|
-
kind: "user_message",
|
|
4093
|
-
runId: activeFlow.runId,
|
|
4094
|
-
text: cmd.prompt,
|
|
4095
|
-
senderId: "user"
|
|
4096
|
-
}
|
|
4097
|
-
});
|
|
4098
|
-
} catch (err) {
|
|
4099
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4100
|
-
log(`[serve] flow user-message turn failed: ${errMsg}`);
|
|
4101
|
-
sendEvent({ type: "error", message: `Flow turn failed: ${errMsg}`, fatal: false });
|
|
4102
|
-
}
|
|
4103
|
-
break;
|
|
4104
|
-
}
|
|
4105
|
-
if (!driverStarted) {
|
|
4106
|
-
try {
|
|
4107
|
-
await driver.start();
|
|
4108
|
-
driverStarted = true;
|
|
4109
|
-
} catch (err) {
|
|
4110
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4111
|
-
log(`[serve] driver start failed: ${errMsg}`);
|
|
4112
|
-
sendEvent({ type: "error", message: `Driver start failed: ${errMsg}`, fatal: true });
|
|
4113
|
-
break;
|
|
4114
|
-
}
|
|
4115
|
-
driver.on("agent-event", (event) => {
|
|
4116
|
-
const normalized = normalizer.normalize(event);
|
|
4117
|
-
for (const e of normalized) {
|
|
4118
|
-
forwardNormalizedEvent(e);
|
|
4119
|
-
}
|
|
4120
|
-
});
|
|
4121
|
-
driver.on("error", (err) => {
|
|
4122
|
-
log(`[serve] driver error: ${err.message}`);
|
|
4123
|
-
sendEvent({ type: "error", message: err.message, fatal: true });
|
|
4124
|
-
});
|
|
4125
|
-
}
|
|
4126
|
-
sendEvent({ type: "status", phase: "thinking" });
|
|
4127
|
-
try {
|
|
4128
|
-
await driver.prompt(cmd.prompt);
|
|
4129
|
-
} catch (err) {
|
|
4130
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4131
|
-
log(`[serve] prompt failed: ${errMsg}`);
|
|
4132
|
-
sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
|
|
4133
|
-
}
|
|
4568
|
+
case "prompt":
|
|
4569
|
+
await handlePrompt(cmd);
|
|
4134
4570
|
break;
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
try {
|
|
4138
|
-
await sessionReady;
|
|
4139
|
-
} catch (err) {
|
|
4140
|
-
sendEvent({
|
|
4141
|
-
type: "error",
|
|
4142
|
-
message: `Session not available: ${err.message}`,
|
|
4143
|
-
fatal: true
|
|
4144
|
-
});
|
|
4145
|
-
break;
|
|
4146
|
-
}
|
|
4147
|
-
log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
|
|
4148
|
-
try {
|
|
4149
|
-
await driver.prompt(cmd.answer);
|
|
4150
|
-
} catch (err) {
|
|
4151
|
-
sendEvent({
|
|
4152
|
-
type: "error",
|
|
4153
|
-
message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4154
|
-
fatal: false
|
|
4155
|
-
});
|
|
4156
|
-
}
|
|
4571
|
+
case "reply":
|
|
4572
|
+
await handleReply(cmd);
|
|
4157
4573
|
break;
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
try {
|
|
4161
|
-
await sessionReady;
|
|
4162
|
-
} catch (err) {
|
|
4163
|
-
sendEvent({
|
|
4164
|
-
type: "error",
|
|
4165
|
-
message: `Session not available: ${err.message}`,
|
|
4166
|
-
fatal: true
|
|
4167
|
-
});
|
|
4168
|
-
break;
|
|
4169
|
-
}
|
|
4170
|
-
log("[serve] cancel");
|
|
4171
|
-
try {
|
|
4172
|
-
await driver.abort();
|
|
4173
|
-
} catch (err) {
|
|
4174
|
-
log(`[serve] cancel failed: ${err}`);
|
|
4175
|
-
}
|
|
4574
|
+
case "cancel":
|
|
4575
|
+
await handleCancel();
|
|
4176
4576
|
break;
|
|
4177
|
-
}
|
|
4178
4577
|
// v3: compact + lifecycle removed — reachable via runner.lifecycle capability
|
|
4179
4578
|
// (the v3 `runner.lifecycle` cap takes `{ phase: 'hibernate' | 'close' | 'compact' }`).
|
|
4180
|
-
case "resource_request":
|
|
4181
|
-
|
|
4182
|
-
await sessionReady;
|
|
4183
|
-
} catch (err) {
|
|
4184
|
-
sendEvent({
|
|
4185
|
-
type: "error",
|
|
4186
|
-
message: `Session not available: ${err.message}`,
|
|
4187
|
-
fatal: true
|
|
4188
|
-
});
|
|
4189
|
-
break;
|
|
4190
|
-
}
|
|
4191
|
-
if (resourceManager?.has(cmd.connectorId)) {
|
|
4192
|
-
handleMountResourceRequest(cmd, resourceManager, sendEvent);
|
|
4193
|
-
break;
|
|
4194
|
-
}
|
|
4195
|
-
if (!resourceManager) {
|
|
4196
|
-
sendEvent({
|
|
4197
|
-
type: "resource_response",
|
|
4198
|
-
requestId: cmd.requestId,
|
|
4199
|
-
resourceId: cmd.connectorId,
|
|
4200
|
-
operation: cmd.operation,
|
|
4201
|
-
error: "No resources available \u2014 no mounts or connectors configured"
|
|
4202
|
-
});
|
|
4203
|
-
break;
|
|
4204
|
-
}
|
|
4205
|
-
void handleResourceRequest(cmd, resourceManager, sendEvent);
|
|
4579
|
+
case "resource_request":
|
|
4580
|
+
await handleResourceRequestCommand(cmd);
|
|
4206
4581
|
break;
|
|
4207
|
-
}
|
|
4208
4582
|
// v3: state_action removed — reachable via the runner.set_state capability.
|
|
4209
4583
|
// ── Generic connector dispatch (Protocol 2.2) ──────────────────────
|
|
4210
4584
|
// The 6 typed flow commands (`start_flow`, `approve_flow_node`,
|
|
@@ -4213,152 +4587,15 @@ async function startAgentServer(opts) {
|
|
|
4213
4587
|
// extraction. Hosts now drive flow execution exclusively through
|
|
4214
4588
|
// `connector_mutate` against the `flow` connector. See
|
|
4215
4589
|
// `MIGRATION-flow-connector.md` for the per-op mapping table.
|
|
4216
|
-
case "connector_mutate":
|
|
4217
|
-
|
|
4218
|
-
if (!resourceManager) throw new Error("connector_mutate before resources ready");
|
|
4219
|
-
const targetEntry = resourceManager.has?.(cmd.id) ? resourceManager.get(cmd.id) : null;
|
|
4220
|
-
const isExistingFlow = targetEntry?.connector?.name === "flow";
|
|
4221
|
-
const isBareFlowId = cmd.id === "flow";
|
|
4222
|
-
if (cmd.op === "start") {
|
|
4223
|
-
if (isExistingFlow || isBareFlowId) {
|
|
4224
|
-
const payload = cmd.payload ?? {};
|
|
4225
|
-
const flowDef = payload.flow;
|
|
4226
|
-
const seed = payload.seed;
|
|
4227
|
-
if (flowDef?.id && seed?.runId && seed?.startedBy) {
|
|
4228
|
-
if (activeFlows.size > 0) {
|
|
4229
|
-
serverLog.warn("connector_mutate flow.start: another flow already active", {
|
|
4230
|
-
activeRunIds: Array.from(activeFlows.keys())
|
|
4231
|
-
});
|
|
4232
|
-
break;
|
|
4233
|
-
}
|
|
4234
|
-
const entry = await createActiveFlow(flowDef, { seed });
|
|
4235
|
-
serverLog.info("connector_mutate flow.start", {
|
|
4236
|
-
runId: entry.runId,
|
|
4237
|
-
flowId: flowDef.id
|
|
4238
|
-
});
|
|
4239
|
-
break;
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
let dispatchId = cmd.id;
|
|
4244
|
-
if (!targetEntry && isBareFlowId) {
|
|
4245
|
-
const onlyActive = getOnlyActiveFlow();
|
|
4246
|
-
if (onlyActive) {
|
|
4247
|
-
dispatchId = onlyActive.connectorId;
|
|
4248
|
-
}
|
|
4249
|
-
}
|
|
4250
|
-
await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4251
|
-
} catch (err) {
|
|
4252
|
-
serverLog.warn("connector_mutate failed", {
|
|
4253
|
-
id: cmd.id,
|
|
4254
|
-
op: cmd.op,
|
|
4255
|
-
error: err instanceof Error ? err.message : String(err)
|
|
4256
|
-
});
|
|
4257
|
-
}
|
|
4590
|
+
case "connector_mutate":
|
|
4591
|
+
await handleConnectorMutate(cmd);
|
|
4258
4592
|
break;
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
let ok = true;
|
|
4262
|
-
let result;
|
|
4263
|
-
let error;
|
|
4264
|
-
try {
|
|
4265
|
-
if (!resourceManager) throw new Error("connector_query before resources ready");
|
|
4266
|
-
let dispatchId = cmd.id;
|
|
4267
|
-
if (!resourceManager.has?.(cmd.id) && cmd.id === "flow") {
|
|
4268
|
-
const onlyActive = getOnlyActiveFlow();
|
|
4269
|
-
if (onlyActive) {
|
|
4270
|
-
dispatchId = onlyActive.connectorId;
|
|
4271
|
-
}
|
|
4272
|
-
}
|
|
4273
|
-
result = await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4274
|
-
} catch (err) {
|
|
4275
|
-
ok = false;
|
|
4276
|
-
error = err instanceof Error ? err.message : String(err);
|
|
4277
|
-
}
|
|
4278
|
-
sendEvent({
|
|
4279
|
-
type: "connector_query_response",
|
|
4280
|
-
requestId: cmd.requestId,
|
|
4281
|
-
ok,
|
|
4282
|
-
...result !== void 0 ? { result } : {},
|
|
4283
|
-
...error !== void 0 ? { error } : {}
|
|
4284
|
-
});
|
|
4593
|
+
case "connector_query":
|
|
4594
|
+
await handleConnectorQuery(cmd);
|
|
4285
4595
|
break;
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
try {
|
|
4289
|
-
await sessionReady;
|
|
4290
|
-
} catch (err) {
|
|
4291
|
-
sendEvent({
|
|
4292
|
-
type: "error",
|
|
4293
|
-
message: `Session not available: ${err.message}`,
|
|
4294
|
-
fatal: true
|
|
4295
|
-
});
|
|
4296
|
-
break;
|
|
4297
|
-
}
|
|
4298
|
-
log(`[serve] debug query=${cmd.query}`);
|
|
4299
|
-
let result;
|
|
4300
|
-
try {
|
|
4301
|
-
switch (cmd.query) {
|
|
4302
|
-
case "tools": {
|
|
4303
|
-
const mcpServers = Object.keys(driver?.config?.mcpServers ?? {});
|
|
4304
|
-
const allowedTools = driver?.config?.allowedTools ?? null;
|
|
4305
|
-
result = { mcpServers, allowedTools, driverStarted };
|
|
4306
|
-
break;
|
|
4307
|
-
}
|
|
4308
|
-
case "connectors": {
|
|
4309
|
-
result = resourceManager?.listConnectors?.() ?? [];
|
|
4310
|
-
break;
|
|
4311
|
-
}
|
|
4312
|
-
case "mcp": {
|
|
4313
|
-
const servers = Object.entries(driver?.config?.mcpServers ?? {}).map(
|
|
4314
|
-
([name]) => ({ name })
|
|
4315
|
-
);
|
|
4316
|
-
result = { servers };
|
|
4317
|
-
break;
|
|
4318
|
-
}
|
|
4319
|
-
case "config": {
|
|
4320
|
-
const liveModel = driver?.getModel() ?? agentSession?.model;
|
|
4321
|
-
result = {
|
|
4322
|
-
driver: agentSession?.driverType,
|
|
4323
|
-
model: liveModel,
|
|
4324
|
-
provider: agentSession?.provider,
|
|
4325
|
-
maxTurns,
|
|
4326
|
-
secretsMode,
|
|
4327
|
-
driverStarted,
|
|
4328
|
-
projectDir: opts.projectDir,
|
|
4329
|
-
versions: getFrameworkVersions()
|
|
4330
|
-
};
|
|
4331
|
-
break;
|
|
4332
|
-
}
|
|
4333
|
-
case "state": {
|
|
4334
|
-
const storeId = cmd.args?.store;
|
|
4335
|
-
if (storeId) {
|
|
4336
|
-
try {
|
|
4337
|
-
const snapshot = await resourceManager?.executeOp(storeId, "get", {});
|
|
4338
|
-
result = snapshot ? JSON.parse(snapshot) : null;
|
|
4339
|
-
} catch (err) {
|
|
4340
|
-
result = { error: err instanceof Error ? err.message : String(err) };
|
|
4341
|
-
}
|
|
4342
|
-
} else {
|
|
4343
|
-
const all = resourceManager?.listConnectors?.() ?? [];
|
|
4344
|
-
result = { stores: all.map((c) => c.id) };
|
|
4345
|
-
}
|
|
4346
|
-
break;
|
|
4347
|
-
}
|
|
4348
|
-
case "commands": {
|
|
4349
|
-
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4350
|
-
result = { commands, driverType: agentSession?.driverType };
|
|
4351
|
-
break;
|
|
4352
|
-
}
|
|
4353
|
-
default:
|
|
4354
|
-
result = { error: `Unknown debug query: ${cmd.query}` };
|
|
4355
|
-
}
|
|
4356
|
-
} catch (err) {
|
|
4357
|
-
result = { error: err instanceof Error ? err.message : String(err) };
|
|
4358
|
-
}
|
|
4359
|
-
sendEvent({ type: "debug_result", query: cmd.query, data: result });
|
|
4596
|
+
case "debug":
|
|
4597
|
+
await handleDebug(cmd);
|
|
4360
4598
|
break;
|
|
4361
|
-
}
|
|
4362
4599
|
// -----------------------------------------------------------------------
|
|
4363
4600
|
// -----------------------------------------------------------------------
|
|
4364
4601
|
// v3: add_resource, remove_resource, remount_mount, set_log_level,
|
|
@@ -4367,75 +4604,27 @@ async function startAgentServer(opts) {
|
|
|
4367
4604
|
// capability invocations (audience: ['runtime']) — the platform calls
|
|
4368
4605
|
// them through invokeRunnerCapability on the gateway. Handler bodies
|
|
4369
4606
|
// live in the runner.* capability registrations.
|
|
4370
|
-
case "shutdown":
|
|
4371
|
-
|
|
4372
|
-
await cleanup();
|
|
4373
|
-
if (transport instanceof WebSocketServerTransport) {
|
|
4374
|
-
await transport.close();
|
|
4375
|
-
}
|
|
4376
|
-
disposeLogStore();
|
|
4377
|
-
process.exit(0);
|
|
4607
|
+
case "shutdown":
|
|
4608
|
+
await handleShutdown();
|
|
4378
4609
|
break;
|
|
4379
|
-
}
|
|
4380
4610
|
// Protocol v2 — capability round-trip results from the platform.
|
|
4381
|
-
case "capability_result":
|
|
4382
|
-
|
|
4383
|
-
if (!matched) {
|
|
4384
|
-
log(
|
|
4385
|
-
`[serve] capability_result for unknown callId=${cmd.callId} (timed out or already resolved); dropping`
|
|
4386
|
-
);
|
|
4387
|
-
}
|
|
4611
|
+
case "capability_result":
|
|
4612
|
+
handleCapabilityResult(cmd);
|
|
4388
4613
|
break;
|
|
4389
|
-
}
|
|
4390
4614
|
// Protocol v2 — additional capability lifecycle commands. The platform
|
|
4391
4615
|
// can register / deregister capabilities mid-session (e.g. when a user
|
|
4392
4616
|
// installs an extension after `configure`). Approval routing is reserved
|
|
4393
4617
|
// for a later phase; we accept the command shape for forward
|
|
4394
4618
|
// compatibility and log it.
|
|
4395
|
-
case "capability_register":
|
|
4396
|
-
|
|
4397
|
-
const wire = raw;
|
|
4398
|
-
capabilityRegistry.register(
|
|
4399
|
-
{
|
|
4400
|
-
...wire,
|
|
4401
|
-
inputZod: { parse: (v) => v },
|
|
4402
|
-
outputZod: void 0,
|
|
4403
|
-
handler: buildClientCapabilityHandler2(wire)
|
|
4404
|
-
},
|
|
4405
|
-
"client"
|
|
4406
|
-
);
|
|
4407
|
-
}
|
|
4619
|
+
case "capability_register":
|
|
4620
|
+
handleCapabilityRegister(cmd);
|
|
4408
4621
|
break;
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
for (const name of cmd.names) {
|
|
4412
|
-
capabilityRegistry.deregister(name);
|
|
4413
|
-
}
|
|
4622
|
+
case "capability_deregister":
|
|
4623
|
+
handleCapabilityDeregister(cmd);
|
|
4414
4624
|
break;
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
if (cmd.decision === "rejected") {
|
|
4418
|
-
const matched = rejectCapabilityOnApprovalDeny(
|
|
4419
|
-
pendingCapabilityCalls,
|
|
4420
|
-
cmd.callId,
|
|
4421
|
-
cmd.feedback
|
|
4422
|
-
);
|
|
4423
|
-
if (!matched) {
|
|
4424
|
-
log(
|
|
4425
|
-
`[serve] capability_approve(rejected) for unknown callId=${cmd.callId} (already resolved or timed out); dropping`
|
|
4426
|
-
);
|
|
4427
|
-
} else {
|
|
4428
|
-
log(
|
|
4429
|
-
`[serve] capability_approve(rejected) callId=${cmd.callId} decidedBy=${cmd.decidedBy}${cmd.feedback ? ` feedback=${JSON.stringify(cmd.feedback)}` : ""}`
|
|
4430
|
-
);
|
|
4431
|
-
}
|
|
4432
|
-
} else {
|
|
4433
|
-
log(
|
|
4434
|
-
`[serve] capability_approve(approved) callId=${cmd.callId} decidedBy=${cmd.decidedBy} (awaiting capability_result)`
|
|
4435
|
-
);
|
|
4436
|
-
}
|
|
4625
|
+
case "capability_approve":
|
|
4626
|
+
handleCapabilityApprove(cmd);
|
|
4437
4627
|
break;
|
|
4438
|
-
}
|
|
4439
4628
|
}
|
|
4440
4629
|
}
|
|
4441
4630
|
transport.onCommand((cmd) => {
|
|
@@ -4447,21 +4636,22 @@ async function startAgentServer(opts) {
|
|
|
4447
4636
|
if (secretsMode === "env" && agentSession) {
|
|
4448
4637
|
agentSession.startWatching();
|
|
4449
4638
|
}
|
|
4639
|
+
function emitAvailabilityOnConnect() {
|
|
4640
|
+
if (resourceManager) {
|
|
4641
|
+
const wire = splitConnectorsForWire(resourceManager);
|
|
4642
|
+
log(
|
|
4643
|
+
`[serve] emitting resources_available: ${wire.mounts.length} mount(s), ${wire.connectors.length} connector(s)`
|
|
4644
|
+
);
|
|
4645
|
+
sendEvent({ type: "resources_available", ...wire });
|
|
4646
|
+
}
|
|
4647
|
+
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4648
|
+
if (commands.length > 0) {
|
|
4649
|
+
log(`[serve] emitting commands_available: ${commands.length} command(s)`);
|
|
4650
|
+
sendEvent({ type: "commands_available", commands });
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4450
4653
|
if (transport instanceof WebSocketServerTransport) {
|
|
4451
|
-
transport.onConnect(
|
|
4452
|
-
if (resourceManager) {
|
|
4453
|
-
const wire = splitConnectorsForWire(resourceManager);
|
|
4454
|
-
log(
|
|
4455
|
-
`[serve] emitting resources_available: ${wire.mounts.length} mount(s), ${wire.connectors.length} connector(s)`
|
|
4456
|
-
);
|
|
4457
|
-
sendEvent({ type: "resources_available", ...wire });
|
|
4458
|
-
}
|
|
4459
|
-
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4460
|
-
if (commands.length > 0) {
|
|
4461
|
-
log(`[serve] emitting commands_available: ${commands.length} command(s)`);
|
|
4462
|
-
sendEvent({ type: "commands_available", commands });
|
|
4463
|
-
}
|
|
4464
|
-
});
|
|
4654
|
+
transport.onConnect(emitAvailabilityOnConnect);
|
|
4465
4655
|
}
|
|
4466
4656
|
if (secretsMode === "env") {
|
|
4467
4657
|
log(
|
|
@@ -4471,53 +4661,56 @@ async function startAgentServer(opts) {
|
|
|
4471
4661
|
log(`[serve] secrets=provisioned \u2014 session will initialize after provision_secrets`);
|
|
4472
4662
|
}
|
|
4473
4663
|
log(`[serve] project=${opts.projectDir}`);
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
log(`[serve] ${signal} received \u2014 shutting down (press Ctrl-C again to force)`);
|
|
4493
|
-
const forceExitTimer = setTimeout(() => {
|
|
4494
|
-
log(`[serve] graceful shutdown exceeded ${GRACEFUL_SHUTDOWN_TIMEOUT_MS}ms \u2014 forcing exit`);
|
|
4495
|
-
process.exit(130);
|
|
4496
|
-
}, GRACEFUL_SHUTDOWN_TIMEOUT_MS);
|
|
4497
|
-
forceExitTimer.unref?.();
|
|
4498
|
-
try {
|
|
4499
|
-
await cleanup();
|
|
4500
|
-
} catch (err) {
|
|
4501
|
-
log(`[serve] cleanup error: ${err.message}`);
|
|
4502
|
-
}
|
|
4503
|
-
try {
|
|
4504
|
-
if (transport instanceof WebSocketServerTransport) {
|
|
4505
|
-
await transport.close();
|
|
4664
|
+
installProcessHandlers();
|
|
4665
|
+
function installProcessHandlers() {
|
|
4666
|
+
process.on("uncaughtException", (err) => {
|
|
4667
|
+
log(
|
|
4668
|
+
`[serve] uncaughtException (non-fatal): ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
4669
|
+
);
|
|
4670
|
+
});
|
|
4671
|
+
process.on("unhandledRejection", (reason) => {
|
|
4672
|
+
log(
|
|
4673
|
+
`[serve] unhandledRejection (non-fatal): ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}`
|
|
4674
|
+
);
|
|
4675
|
+
});
|
|
4676
|
+
const GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
4677
|
+
let shuttingDown = false;
|
|
4678
|
+
async function initiateShutdown(signal) {
|
|
4679
|
+
if (shuttingDown) {
|
|
4680
|
+
log(`[serve] ${signal} received again \u2014 forcing exit`);
|
|
4681
|
+
process.exit(130);
|
|
4506
4682
|
}
|
|
4507
|
-
|
|
4508
|
-
log(`[serve]
|
|
4683
|
+
shuttingDown = true;
|
|
4684
|
+
log(`[serve] ${signal} received \u2014 shutting down (press Ctrl-C again to force)`);
|
|
4685
|
+
const forceExitTimer = setTimeout(() => {
|
|
4686
|
+
log(`[serve] graceful shutdown exceeded ${GRACEFUL_SHUTDOWN_TIMEOUT_MS}ms \u2014 forcing exit`);
|
|
4687
|
+
process.exit(130);
|
|
4688
|
+
}, GRACEFUL_SHUTDOWN_TIMEOUT_MS);
|
|
4689
|
+
forceExitTimer.unref?.();
|
|
4690
|
+
try {
|
|
4691
|
+
await cleanup();
|
|
4692
|
+
} catch (err) {
|
|
4693
|
+
log(`[serve] cleanup error: ${err.message}`);
|
|
4694
|
+
}
|
|
4695
|
+
try {
|
|
4696
|
+
if (transport instanceof WebSocketServerTransport) {
|
|
4697
|
+
await transport.close();
|
|
4698
|
+
}
|
|
4699
|
+
} catch (err) {
|
|
4700
|
+
log(`[serve] transport close error: ${err.message}`);
|
|
4701
|
+
}
|
|
4702
|
+
clearTimeout(forceExitTimer);
|
|
4703
|
+
log("[serve] shutdown complete");
|
|
4704
|
+
disposeLogStore();
|
|
4705
|
+
process.exit(0);
|
|
4509
4706
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
process.
|
|
4707
|
+
process.on("SIGTERM", () => {
|
|
4708
|
+
void initiateShutdown("SIGTERM");
|
|
4709
|
+
});
|
|
4710
|
+
process.on("SIGINT", () => {
|
|
4711
|
+
void initiateShutdown("SIGINT");
|
|
4712
|
+
});
|
|
4514
4713
|
}
|
|
4515
|
-
process.on("SIGTERM", () => {
|
|
4516
|
-
void initiateShutdown("SIGTERM");
|
|
4517
|
-
});
|
|
4518
|
-
process.on("SIGINT", () => {
|
|
4519
|
-
void initiateShutdown("SIGINT");
|
|
4520
|
-
});
|
|
4521
4714
|
}
|
|
4522
4715
|
var SESSION_DIR = ".skaile";
|
|
4523
4716
|
var SESSIONS_SUBDIR = "sessions";
|
|
@@ -4633,5 +4826,5 @@ function touchSession(state) {
|
|
|
4633
4826
|
}
|
|
4634
4827
|
|
|
4635
4828
|
export { CLAUDE_CODE_CREDENTIALS_KEY, COMPILE_MANIFEST_FILENAME, CapabilityRegistry, DEFAULT_CAPABILITY_CALL_TIMEOUT_MS, DEFAULT_COALESCE_MS, MarkdownStreamer, PreInitRingSink, agentDefinitionExists, bootstrapCapabilityRegistry, bootstrapRunnerLogStore, buildAgentResources, buildClientCapabilityHandler, buildContextSection, buildEnvironmentSection, builtinCapabilities, clearPreInitRingSink, clearSession, compileComposition, computeCapabilitySignature, createAgentSession, createSessionStimulusBus, defineCapability, deleteSession, dispatchRunnerCapabilityInvocation, emitSystemPromptComposed, ensureGitConfigInclude, extractClaudeAiOauthExpiresAt, getPreInitRingSink, handleMountResourceRequest, handleResourceRequest, installPreInitRingSink, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadSession, loadSessionById, newSession, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveMixin, runAgentChat, saveSession, setCurrentSession, startAgentServer, touchSession, writeClaudeCodeCredentialsFile };
|
|
4636
|
-
//# sourceMappingURL=chunk-
|
|
4637
|
-
//# sourceMappingURL=chunk-
|
|
4829
|
+
//# sourceMappingURL=chunk-XOAF5QAE.js.map
|
|
4830
|
+
//# sourceMappingURL=chunk-XOAF5QAE.js.map
|