@skaile/workspaces 0.29.1 → 0.31.0
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 +63 -0
- package/dist/{asset-feeds-2V7LZIG7.js → asset-feeds-6HZDZEIT.js} +14 -13
- package/dist/asset-feeds-6HZDZEIT.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 +354 -224
- 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 +68 -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-DVMSGGCM.js → chunk-45RKEHPX.js} +158 -108
- package/dist/chunk-45RKEHPX.js.map +1 -0
- package/dist/{chunk-2FS4FPZ7.js → chunk-4AGIEU7V.js} +44 -62
- package/dist/chunk-4AGIEU7V.js.map +1 -0
- package/dist/{chunk-SF4GWVR5.js → chunk-4CRKROS5.js} +86 -55
- package/dist/chunk-4CRKROS5.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-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-QMONOHXT.js → chunk-74UKG2LA.js} +3 -3
- package/dist/{chunk-QMONOHXT.js.map → chunk-74UKG2LA.js.map} +1 -1
- package/dist/{chunk-AKSR434V.js → chunk-7FLLPKX3.js} +79 -56
- package/dist/chunk-7FLLPKX3.js.map +1 -0
- package/dist/{chunk-BZ2JU7IP.js → chunk-7KOBITAP.js} +5 -5
- package/dist/{chunk-BZ2JU7IP.js.map → chunk-7KOBITAP.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-UIZKIUC6.js → chunk-BRBOC53X.js} +3 -3
- package/dist/chunk-BRBOC53X.js.map +1 -0
- package/dist/{chunk-FWHOU3CX.js → chunk-CBIPUBUS.js} +6 -6
- package/dist/{chunk-FWHOU3CX.js.map → chunk-CBIPUBUS.js.map} +1 -1
- package/dist/{chunk-3QQSSRYN.js → chunk-DKAV43NI.js} +430 -346
- package/dist/chunk-DKAV43NI.js.map +1 -0
- package/dist/{chunk-M4KLIEIJ.js → chunk-G247J4MC.js} +3 -3
- package/dist/chunk-G247J4MC.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-2NOE4AGD.js → chunk-HH62ECJ7.js} +3 -3
- package/dist/{chunk-2NOE4AGD.js.map → chunk-HH62ECJ7.js.map} +1 -1
- package/dist/{chunk-QDJKE557.js → chunk-HRKB2ATT.js} +64 -64
- package/dist/chunk-HRKB2ATT.js.map +1 -0
- package/dist/{chunk-MLJKA3S6.js → chunk-IENYMRZ6.js} +1711 -1497
- package/dist/chunk-IENYMRZ6.js.map +1 -0
- package/dist/{chunk-24UIWON4.js → chunk-JB35MVBN.js} +167 -156
- package/dist/chunk-JB35MVBN.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-72IA6BTP.js → chunk-MWAEOCU7.js} +9 -9
- package/dist/{chunk-72IA6BTP.js.map → chunk-MWAEOCU7.js.map} +1 -1
- package/dist/{chunk-5V5KZQIC.js → chunk-NGE6PALH.js} +4 -4
- package/dist/{chunk-5V5KZQIC.js.map → chunk-NGE6PALH.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-UBCZVONU.js → chunk-NQC7ZUXE.js} +3 -3
- package/dist/{chunk-UBCZVONU.js.map → chunk-NQC7ZUXE.js.map} +1 -1
- package/dist/{chunk-ZEKWHJBB.js → chunk-OEXA7BBK.js} +3 -3
- package/dist/{chunk-ZEKWHJBB.js.map → chunk-OEXA7BBK.js.map} +1 -1
- package/dist/{chunk-5RBPPBEK.js → chunk-P72K36PU.js} +4 -4
- package/dist/{chunk-5RBPPBEK.js.map → chunk-P72K36PU.js.map} +1 -1
- package/dist/{chunk-OTFG5Q7H.js → chunk-QF6LBS2C.js} +4594 -4480
- package/dist/chunk-QF6LBS2C.js.map +1 -0
- 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-CMG3GJ6B.js → chunk-VO7QQ76S.js} +4 -4
- package/dist/{chunk-CMG3GJ6B.js.map → chunk-VO7QQ76S.js.map} +1 -1
- 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-FQHHCICT.js → chunk-XVDXGQ55.js} +226 -226
- package/dist/chunk-XVDXGQ55.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-LD4QIVBY.js → chunk-ZM7UOJR2.js} +3 -3
- package/dist/{chunk-LD4QIVBY.js.map → chunk-ZM7UOJR2.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 +8 -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-JWBUVDW6.js +84 -0
- package/dist/ensure-sources-JWBUVDW6.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-T6RCRKCX.js +7 -0
- package/dist/{flows-L3GHQD7F.js.map → flows-T6RCRKCX.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 +2 -2
- 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-ZXTSUPBJ.js +23 -0
- package/dist/{open-library-3VJY74LQ.js.map → open-library-ZXTSUPBJ.js.map} +1 -1
- package/dist/paths-F7RHQUFL.js +15 -0
- package/dist/{paths-CE66L3AL.js.map → paths-F7RHQUFL.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-BC3I36HA.js} +11 -14
- package/dist/plugin-store-BC3I36HA.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/recipe-resolver.d.ts +32 -0
- package/dist/runner/src/recipe-resolver.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-FQ3DE6J6.js} +198 -175
- package/dist/setup-FQ3DE6J6.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-27YYEWMM.js +15 -0
- package/dist/{store-client-BCP5LCAZ.js.map → store-client-27YYEWMM.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-ZM7UOJR2.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-7FLLPKX3.js';
|
|
6
|
+
import { classifyClaudeSdkError } from './chunk-NGJ6E344.js';
|
|
7
|
+
import { createDriver } from './chunk-OEXA7BBK.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-QF6LBS2C.js';
|
|
10
|
+
import { loadConnectorDeclarations, PreMintedSecretProvider, InMemorySecretProvider } from './chunk-VO7QQ76S.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-G247J4MC.js';
|
|
13
|
+
import { resolveRuntimeAssets } from './chunk-CBIPUBUS.js';
|
|
14
|
+
import { resolveSkWorkspaceConfig, resolveAgentDir, stageMaterializedSkills, loadMcpServerDeclarations, COMPACTION_DEFAULTS, validateAssetRecipeAttr } from './chunk-DKAV43NI.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 = [
|
|
@@ -774,55 +774,73 @@ function loadRecipeMap() {
|
|
|
774
774
|
recipeMapMemo = { path: path5, mtimeMs, map };
|
|
775
775
|
return map;
|
|
776
776
|
}
|
|
777
|
-
|
|
778
|
-
|
|
777
|
+
var MapRecipeProducer = class {
|
|
778
|
+
constructor(load = loadRecipeMap) {
|
|
779
|
+
this.load = load;
|
|
780
|
+
}
|
|
781
|
+
load;
|
|
782
|
+
kind = "map";
|
|
783
|
+
resolve(_ref, attr) {
|
|
784
|
+
const map = this.load();
|
|
785
|
+
if (!map) return null;
|
|
786
|
+
const hit = attr.split(".").reduce(
|
|
787
|
+
(o, k) => o && typeof o === "object" ? o[k] : void 0,
|
|
788
|
+
map
|
|
789
|
+
);
|
|
790
|
+
return typeof hit === "string" && hit.startsWith("/nix/store/") ? hit : null;
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
var NixRecipeProducer = class {
|
|
794
|
+
kind = "nix";
|
|
795
|
+
resolve(ref, attr) {
|
|
796
|
+
const result = spawnSync("nix", ["path-info", "--offline", `${ref}#${attr}`], {
|
|
797
|
+
encoding: "utf8"
|
|
798
|
+
});
|
|
799
|
+
if (result.error) throw new RecipeUnavailableError(ref, attr, result.error);
|
|
800
|
+
if (result.status !== 0) {
|
|
801
|
+
throw new RecipeUnavailableError(
|
|
802
|
+
ref,
|
|
803
|
+
attr,
|
|
804
|
+
new Error(`nix path-info exited ${result.status}: ${result.stderr.trim()}`)
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
const outPath = result.stdout.trim().split("\n")[0];
|
|
808
|
+
if (!outPath?.startsWith("/nix/store/")) {
|
|
809
|
+
throw new RecipeUnavailableError(ref, attr, new Error(`Unexpected output: ${result.stdout}`));
|
|
810
|
+
}
|
|
811
|
+
return outPath;
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
function defaultRecipeProducers() {
|
|
815
|
+
return [new MapRecipeProducer(), new NixRecipeProducer()];
|
|
816
|
+
}
|
|
817
|
+
function resolveViaProducers(ref, attr, cache, producers = defaultRecipeProducers()) {
|
|
818
|
+
const key = `${ref}#${attr}`;
|
|
779
819
|
const cached = cache.get(key);
|
|
780
820
|
if (cached) return cached;
|
|
781
821
|
try {
|
|
782
822
|
validateAssetRecipeAttr(attr);
|
|
783
823
|
} catch (err) {
|
|
784
824
|
throw new RecipeUnavailableError(
|
|
785
|
-
|
|
825
|
+
ref,
|
|
786
826
|
attr,
|
|
787
827
|
err instanceof Error ? err : new Error(String(err))
|
|
788
828
|
);
|
|
789
829
|
}
|
|
790
830
|
const log = createLogger({ kind: "mcp", subkind: "recipe-resolve" });
|
|
791
|
-
log.debug("resolving recipe", { flakeRef, attr });
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
(
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (typeof hit === "string" && hit.startsWith("/nix/store/")) {
|
|
799
|
-
cache.set(key, hit);
|
|
800
|
-
log.info("resolved recipe (map)", { flakeRef, attr, outPath: hit });
|
|
801
|
-
return hit;
|
|
831
|
+
log.debug("resolving recipe", { flakeRef: ref, attr });
|
|
832
|
+
for (const producer of producers) {
|
|
833
|
+
const out = producer.resolve(ref, attr);
|
|
834
|
+
if (out) {
|
|
835
|
+
cache.set(key, out);
|
|
836
|
+
log.info("resolved recipe", { flakeRef: ref, attr, outPath: out, producer: producer.kind });
|
|
837
|
+
return out;
|
|
802
838
|
}
|
|
803
839
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
if (result.status !== 0) {
|
|
809
|
-
throw new RecipeUnavailableError(
|
|
810
|
-
flakeRef,
|
|
811
|
-
attr,
|
|
812
|
-
new Error(`nix path-info exited ${result.status}: ${result.stderr.trim()}`)
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
const outPath = result.stdout.trim().split("\n")[0];
|
|
816
|
-
if (!outPath?.startsWith("/nix/store/")) {
|
|
817
|
-
throw new RecipeUnavailableError(
|
|
818
|
-
flakeRef,
|
|
819
|
-
attr,
|
|
820
|
-
new Error(`Unexpected output: ${result.stdout}`)
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
cache.set(key, outPath);
|
|
824
|
-
log.info("resolved recipe", { flakeRef, attr, outPath });
|
|
825
|
-
return outPath;
|
|
840
|
+
throw new RecipeUnavailableError(ref, attr, new Error("no producer resolved the recipe"));
|
|
841
|
+
}
|
|
842
|
+
function resolveRecipePath(flakeRef, attr, cache) {
|
|
843
|
+
return resolveViaProducers(flakeRef, attr, cache);
|
|
826
844
|
}
|
|
827
845
|
var RECIPE_MARKER = /\$\{recipe:([a-z0-9_-]+)(?::([a-z0-9_/.-]+))?\}/g;
|
|
828
846
|
function substituteRecipeTemplating(value, recipeOutPaths) {
|
|
@@ -893,63 +911,75 @@ function resolveExternalMcpDeclarations(projectDir, sessionId) {
|
|
|
893
911
|
mcpLog.warn("MCP server skipped: reserved id", { id: decl.id });
|
|
894
912
|
continue;
|
|
895
913
|
}
|
|
896
|
-
|
|
897
|
-
if (
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
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
|
-
}
|
|
914
|
+
const wantsRecipe = decl.launcher === "recipe" || decl.launcher == null && decl.recipe != null;
|
|
915
|
+
if (!wantsRecipe) {
|
|
916
|
+
resolved.push(decl);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (!decl.recipe) {
|
|
920
|
+
mcpLog.warn("MCP server skipped: launcher 'recipe' without a recipe binding", {
|
|
921
|
+
id: decl.id
|
|
922
|
+
});
|
|
923
|
+
continue;
|
|
948
924
|
}
|
|
949
|
-
|
|
925
|
+
const resolvedDecl = resolveRecipeDeclaration(decl, nixFlakeRef, recipeCache);
|
|
926
|
+
if (resolvedDecl) resolved.push(resolvedDecl);
|
|
950
927
|
}
|
|
951
928
|
return resolved;
|
|
952
929
|
}
|
|
930
|
+
function resolveRecipeDeclaration(decl, nixFlakeRef, recipeCache) {
|
|
931
|
+
const recipe = decl.recipe;
|
|
932
|
+
const recipeLogKey = recipe.flake ? { flake: recipe.flake } : { attr: recipe.attr };
|
|
933
|
+
const recipeLog = createLogger({ kind: "mcp", subkind: decl.id, instance: "recipe-resolve" });
|
|
934
|
+
try {
|
|
935
|
+
const recipeKey = recipe.flake ? `mcps.${decl.id}` : recipe.attr ?? `mcps.${decl.id}`;
|
|
936
|
+
const outPath = resolveRecipePath(nixFlakeRef, recipeKey, recipeCache);
|
|
937
|
+
const outPaths = /* @__PURE__ */ new Map([[decl.id, outPath]]);
|
|
938
|
+
const resolvedCommand = decl.command ? substituteRecipeTemplating(decl.command, outPaths) : decl.command;
|
|
939
|
+
const resolvedArgs = decl.args?.map((a) => substituteRecipeTemplating(a, outPaths));
|
|
940
|
+
const resolvedEnv = substituteRecipeMap(decl.env, outPaths);
|
|
941
|
+
const resolvedDecl = {
|
|
942
|
+
...decl,
|
|
943
|
+
command: resolvedCommand,
|
|
944
|
+
args: resolvedArgs,
|
|
945
|
+
env: resolvedEnv
|
|
946
|
+
};
|
|
947
|
+
warnUnresolvedRecipeMarkers(recipeLog, decl.id, resolvedCommand, resolvedArgs, resolvedEnv);
|
|
948
|
+
const missingPaths = validateSubstitutedPaths(resolvedCommand, resolvedArgs);
|
|
949
|
+
if (missingPaths.length > 0) {
|
|
950
|
+
recipeLog.error("MCP server skipped: substituted path does not exist", void 0, {
|
|
951
|
+
id: decl.id,
|
|
952
|
+
...recipeLogKey,
|
|
953
|
+
missing: missingPaths
|
|
954
|
+
});
|
|
955
|
+
return void 0;
|
|
956
|
+
}
|
|
957
|
+
recipeLog.info("recipe substitution complete", { ...recipeLogKey, outPath });
|
|
958
|
+
return resolvedDecl;
|
|
959
|
+
} catch (err) {
|
|
960
|
+
recipeLog.error("MCP server skipped: recipe resolution failed", err, {
|
|
961
|
+
id: decl.id,
|
|
962
|
+
...recipeLogKey,
|
|
963
|
+
flakeRef: nixFlakeRef
|
|
964
|
+
});
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function warnUnresolvedRecipeMarkers(recipeLog, declId, resolvedCommand, resolvedArgs, resolvedEnv) {
|
|
969
|
+
const allResolved = [
|
|
970
|
+
resolvedCommand ?? "",
|
|
971
|
+
...resolvedArgs ?? [],
|
|
972
|
+
...Object.values(resolvedEnv ?? {})
|
|
973
|
+
];
|
|
974
|
+
for (const v of allResolved) {
|
|
975
|
+
if (v.includes("${recipe:")) {
|
|
976
|
+
recipeLog.warn("unresolved recipe marker after substitution", {
|
|
977
|
+
value: v,
|
|
978
|
+
declId
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
953
983
|
var PASSTHROUGH_INPUT_ZOD = {
|
|
954
984
|
parse: (v) => v
|
|
955
985
|
};
|
|
@@ -1140,122 +1170,50 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
|
|
|
1140
1170
|
for (const w of runtimeAssets.warnings) {
|
|
1141
1171
|
resLog.warn("runtime asset warning", { code: w.code, message: w.message });
|
|
1142
1172
|
}
|
|
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
|
-
}
|
|
1173
|
+
await maybeInstallNpmPeerDeps(runtimeAssets, projectDir, resLog);
|
|
1154
1174
|
const connectorDeclarations = loadConnectorDeclarations(projectDir);
|
|
1155
1175
|
let resourceManager = null;
|
|
1156
1176
|
if (connectorDeclarations.length > 0) {
|
|
1157
|
-
resourceManager =
|
|
1158
|
-
|
|
1159
|
-
|
|
1177
|
+
resourceManager = await connectAllConnectors(
|
|
1178
|
+
connectorDeclarations,
|
|
1179
|
+
projectDir,
|
|
1180
|
+
runtimeAssets,
|
|
1181
|
+
secretProvider,
|
|
1160
1182
|
tokenMediator,
|
|
1161
1183
|
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
|
-
}
|
|
1184
|
+
deferFilesystemMounts,
|
|
1185
|
+
watch,
|
|
1186
|
+
resLog
|
|
1187
|
+
);
|
|
1188
1188
|
}
|
|
1189
1189
|
const resourcePromptSection = resourceManager ? buildConnectorPromptSection(resourceManager) : "";
|
|
1190
1190
|
let mcpServers;
|
|
1191
1191
|
let workspacePluginToShutdown = null;
|
|
1192
1192
|
let externalMcpManager = null;
|
|
1193
1193
|
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({
|
|
1194
|
+
const inProcess = await buildInProcessMcpServers(
|
|
1195
|
+
resourceManager,
|
|
1212
1196
|
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;
|
|
1197
|
+
driverType,
|
|
1198
|
+
sessionMeta
|
|
1199
|
+
);
|
|
1200
|
+
mcpServers = inProcess.mcpServers;
|
|
1201
|
+
workspacePluginToShutdown = inProcess.workspacePluginToShutdown;
|
|
1223
1202
|
}
|
|
1224
1203
|
if (capabilityRegistry) {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
);
|
|
1232
|
-
await externalMcpManager.start(resolvedDecls);
|
|
1233
|
-
}
|
|
1204
|
+
externalMcpManager = await startExternalMcpServers(
|
|
1205
|
+
capabilityRegistry,
|
|
1206
|
+
projectDir,
|
|
1207
|
+
secretProvider,
|
|
1208
|
+
sessionMeta
|
|
1209
|
+
);
|
|
1234
1210
|
} 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
|
-
}
|
|
1211
|
+
mcpServers = wireExternalMcpServersForSdk(
|
|
1212
|
+
mcpServers,
|
|
1213
|
+
projectDir,
|
|
1214
|
+
secretProvider,
|
|
1215
|
+
sessionMeta
|
|
1216
|
+
);
|
|
1259
1217
|
}
|
|
1260
1218
|
if (!resourceManager && driverType !== "claude-sdk" && !externalMcpManager?.hasServers())
|
|
1261
1219
|
return noopResult;
|
|
@@ -1306,6 +1264,120 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
|
|
|
1306
1264
|
return noopResult;
|
|
1307
1265
|
}
|
|
1308
1266
|
}
|
|
1267
|
+
async function maybeInstallNpmPeerDeps(runtimeAssets, projectDir, resLog) {
|
|
1268
|
+
if (process.env.SKAILE_AUTO_INSTALL_DEPS === "true") {
|
|
1269
|
+
const required = runtimeAssets.requiredNpmDeps.required;
|
|
1270
|
+
if (required.length > 0) {
|
|
1271
|
+
const missing = findMissingPackages(required, projectDir);
|
|
1272
|
+
if (missing.length > 0) {
|
|
1273
|
+
resLog.info("installing missing npm peer deps", { packages: missing });
|
|
1274
|
+
const r = await installNpmPackages(missing, projectDir);
|
|
1275
|
+
if (!r.success) resLog.error("npm install failed", void 0, { output: r.output });
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
function logConnectorReport(results, resLog) {
|
|
1281
|
+
for (const r of results) {
|
|
1282
|
+
if (r.connected) {
|
|
1283
|
+
resLog.info("connector ok", { id: r.id, mountPath: r.mountPath });
|
|
1284
|
+
} else {
|
|
1285
|
+
resLog.error("connector failed", void 0, { id: r.id, error: r.error });
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
async function connectAllConnectors(connectorDeclarations, projectDir, runtimeAssets, secretProvider, tokenMediator, preMintedSecrets, deferFilesystemMounts, watch, resLog) {
|
|
1290
|
+
const resourceManager = new ConnectorManager(projectDir, {
|
|
1291
|
+
secrets: secretProvider,
|
|
1292
|
+
catalogEntries: runtimeAssets.catalogEntries,
|
|
1293
|
+
tokenMediator,
|
|
1294
|
+
preMintedSecrets,
|
|
1295
|
+
onStatusChange: watch?.onConnectorStatus
|
|
1296
|
+
});
|
|
1297
|
+
try {
|
|
1298
|
+
const report = await resourceManager.connectAll(connectorDeclarations, {
|
|
1299
|
+
failOnReadWriteError: true,
|
|
1300
|
+
deferMounts: deferFilesystemMounts
|
|
1301
|
+
});
|
|
1302
|
+
logConnectorReport(report.results, resLog);
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
if (err instanceof ConnectorStartupError) {
|
|
1305
|
+
logConnectorReport(err.report.results, resLog);
|
|
1306
|
+
}
|
|
1307
|
+
throw err;
|
|
1308
|
+
}
|
|
1309
|
+
return resourceManager;
|
|
1310
|
+
}
|
|
1311
|
+
async function buildInProcessMcpServers(resourceManager, projectDir, driverType, sessionMeta) {
|
|
1312
|
+
let mcpServers;
|
|
1313
|
+
if (resourceManager) {
|
|
1314
|
+
const connectorsLog = createLogger({ kind: "mcp", subkind: "skaile-connectors" });
|
|
1315
|
+
try {
|
|
1316
|
+
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
1317
|
+
if (server) {
|
|
1318
|
+
mcpServers = { "skaile-connectors": server };
|
|
1319
|
+
connectorsLog.info("server constructed", {
|
|
1320
|
+
connectorCount: resourceManager.listConnectors().length
|
|
1321
|
+
});
|
|
1322
|
+
} else {
|
|
1323
|
+
connectorsLog.debug("server skipped (no tools to expose)");
|
|
1324
|
+
}
|
|
1325
|
+
} catch (err) {
|
|
1326
|
+
connectorsLog.error("server construction failed", err);
|
|
1327
|
+
throw err;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const workspacePlugin = new WorkspacePlugin({
|
|
1331
|
+
projectDir,
|
|
1332
|
+
connectorManager: resourceManager ?? void 0,
|
|
1333
|
+
driver: driverType,
|
|
1334
|
+
provider: sessionMeta?.provider,
|
|
1335
|
+
model: sessionMeta?.model
|
|
1336
|
+
});
|
|
1337
|
+
const wsServer = await workspacePlugin.buildWorkspaceMcpServer();
|
|
1338
|
+
if (wsServer) {
|
|
1339
|
+
mcpServers = { ...mcpServers ?? {}, "skaile-workspace": wsServer };
|
|
1340
|
+
}
|
|
1341
|
+
return { mcpServers, workspacePluginToShutdown: workspacePlugin };
|
|
1342
|
+
}
|
|
1343
|
+
async function startExternalMcpServers(capabilityRegistry, projectDir, secretProvider, sessionMeta) {
|
|
1344
|
+
const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
|
|
1345
|
+
if (resolvedDecls.length === 0) return null;
|
|
1346
|
+
const externalMcpManager = new ExternalMcpManager(
|
|
1347
|
+
capabilityRegistry,
|
|
1348
|
+
secretProvider,
|
|
1349
|
+
sessionMeta?.sessionId
|
|
1350
|
+
);
|
|
1351
|
+
await externalMcpManager.start(resolvedDecls);
|
|
1352
|
+
return externalMcpManager;
|
|
1353
|
+
}
|
|
1354
|
+
function wireExternalMcpServersForSdk(mcpServers, projectDir, secretProvider, sessionMeta) {
|
|
1355
|
+
const mcpLog = createLogger({
|
|
1356
|
+
kind: "mcp",
|
|
1357
|
+
subkind: "wiring",
|
|
1358
|
+
instance: sessionMeta?.sessionId ?? "no-session"
|
|
1359
|
+
});
|
|
1360
|
+
const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
|
|
1361
|
+
for (const decl of resolvedDecls) {
|
|
1362
|
+
const serverConfig = toSdkMcpServerConfig(
|
|
1363
|
+
decl,
|
|
1364
|
+
secretProvider
|
|
1365
|
+
);
|
|
1366
|
+
if (serverConfig) {
|
|
1367
|
+
mcpServers = { ...mcpServers ?? {}, [decl.id]: serverConfig };
|
|
1368
|
+
createLogger({ kind: "mcp", subkind: decl.id }).info("MCP server registered", {
|
|
1369
|
+
transport: decl.transport ?? "stdio",
|
|
1370
|
+
...decl.transport === "stdio" || !decl.transport ? { command: decl.command, args: decl.args } : { url: decl.url }
|
|
1371
|
+
});
|
|
1372
|
+
} else {
|
|
1373
|
+
mcpLog.error("MCP server config invalid (translation returned null)", void 0, {
|
|
1374
|
+
id: decl.id,
|
|
1375
|
+
transport: decl.transport
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return mcpServers;
|
|
1380
|
+
}
|
|
1309
1381
|
function toSdkMcpServerConfig(decl, secrets) {
|
|
1310
1382
|
const transport = decl.transport ?? "stdio";
|
|
1311
1383
|
if (transport === "stdio") {
|
|
@@ -1401,47 +1473,19 @@ async function createAgentSession(config) {
|
|
|
1401
1473
|
"externalPromptFile is deprecated and no longer injected; use agent.prompt-extensions in skaile.yaml instead"
|
|
1402
1474
|
);
|
|
1403
1475
|
}
|
|
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
|
-
}
|
|
1476
|
+
const composition = await resolveSessionComposition(agentDir, manifest, projectDir, sessionLog);
|
|
1414
1477
|
const compositionBlocks = composition?.inlineBlocks ?? [];
|
|
1415
1478
|
const additionalSections = config.additionalPromptSections ?? [];
|
|
1416
|
-
const systemPromptSections =
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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
|
-
});
|
|
1443
|
-
});
|
|
1444
|
-
const systemPrompt = assembleSystemPrompt([
|
|
1479
|
+
const systemPromptSections = buildSystemPromptSections({
|
|
1480
|
+
contextSection,
|
|
1481
|
+
userPromptSection,
|
|
1482
|
+
environmentSection,
|
|
1483
|
+
resourcePromptSection,
|
|
1484
|
+
promptExtensions,
|
|
1485
|
+
compositionBlocks,
|
|
1486
|
+
additionalSections
|
|
1487
|
+
});
|
|
1488
|
+
const systemPrompt = assembleSystemPrompt([
|
|
1445
1489
|
contextSection,
|
|
1446
1490
|
userPromptSection,
|
|
1447
1491
|
environmentSection,
|
|
@@ -1450,36 +1494,26 @@ async function createAgentSession(config) {
|
|
|
1450
1494
|
...compositionBlocks.map((b) => b.content),
|
|
1451
1495
|
...additionalSections
|
|
1452
1496
|
]);
|
|
1453
|
-
|
|
1454
|
-
const length = systemPrompt?.length ?? 0;
|
|
1455
|
-
sessionLog.debug("assembled system prompt", { length });
|
|
1456
|
-
sessionLog.debug("system prompt content", { content: systemPrompt ?? "(empty)" });
|
|
1457
|
-
}
|
|
1497
|
+
logAssembledSystemPrompt(systemPrompt, sessionLog);
|
|
1458
1498
|
const thinking = config.thinking ?? wsConfig.agent_config?.default?.thinking;
|
|
1459
1499
|
const effort = config.effort ?? wsConfig.agent_config?.default?.effort;
|
|
1460
1500
|
const maxTurns = maxTurnsOverride ?? manifest?.runtime?.max_turns;
|
|
1461
|
-
const driver = await
|
|
1462
|
-
|
|
1501
|
+
const driver = await createSessionDriver({
|
|
1502
|
+
config,
|
|
1503
|
+
driverType,
|
|
1504
|
+
agentCwd,
|
|
1463
1505
|
provider,
|
|
1464
|
-
model
|
|
1506
|
+
model,
|
|
1507
|
+
manifest,
|
|
1465
1508
|
maxTurns,
|
|
1466
|
-
|
|
1509
|
+
apiKey,
|
|
1467
1510
|
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 } : {}
|
|
1511
|
+
thinking,
|
|
1512
|
+
effort,
|
|
1513
|
+
sessionId,
|
|
1514
|
+
resumeSessionId,
|
|
1515
|
+
mcpServers,
|
|
1516
|
+
agentDir
|
|
1483
1517
|
});
|
|
1484
1518
|
if (telemetry && trace) {
|
|
1485
1519
|
driver.setTelemetry(telemetry, trace);
|
|
@@ -1520,6 +1554,108 @@ function loadPromptExtensions(paths, projectDir, log) {
|
|
|
1520
1554
|
}
|
|
1521
1555
|
return result;
|
|
1522
1556
|
}
|
|
1557
|
+
async function resolveSessionComposition(agentDir, manifest, projectDir, log) {
|
|
1558
|
+
if (agentDir && manifest?.composes?.length) {
|
|
1559
|
+
return resolveAgentComposition({
|
|
1560
|
+
agentDir,
|
|
1561
|
+
manifest,
|
|
1562
|
+
log
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
if (manifest?.composes?.length && !agentDir) {
|
|
1566
|
+
log.debug("skipping composition resolution \u2014 no agentDir available");
|
|
1567
|
+
}
|
|
1568
|
+
return null;
|
|
1569
|
+
}
|
|
1570
|
+
function buildSystemPromptSections(parts) {
|
|
1571
|
+
const {
|
|
1572
|
+
contextSection,
|
|
1573
|
+
userPromptSection,
|
|
1574
|
+
environmentSection,
|
|
1575
|
+
resourcePromptSection,
|
|
1576
|
+
promptExtensions,
|
|
1577
|
+
compositionBlocks,
|
|
1578
|
+
additionalSections
|
|
1579
|
+
} = parts;
|
|
1580
|
+
const systemPromptSections = [];
|
|
1581
|
+
if (contextSection) systemPromptSections.push({ label: "context", value: contextSection });
|
|
1582
|
+
if (userPromptSection) systemPromptSections.push({ label: "prompt", value: userPromptSection });
|
|
1583
|
+
if (environmentSection)
|
|
1584
|
+
systemPromptSections.push({ label: "environment", value: environmentSection });
|
|
1585
|
+
if (resourcePromptSection)
|
|
1586
|
+
systemPromptSections.push({ label: "resources", value: resourcePromptSection });
|
|
1587
|
+
for (const ext of promptExtensions) {
|
|
1588
|
+
systemPromptSections.push({
|
|
1589
|
+
label: "prompt-extensions",
|
|
1590
|
+
value: ext.value,
|
|
1591
|
+
source: ext.source
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
for (const block of compositionBlocks) {
|
|
1595
|
+
systemPromptSections.push({
|
|
1596
|
+
label: "composition",
|
|
1597
|
+
value: block.content,
|
|
1598
|
+
source: block.ref
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
additionalSections.forEach((value, idx) => {
|
|
1602
|
+
systemPromptSections.push({
|
|
1603
|
+
label: "additional",
|
|
1604
|
+
value,
|
|
1605
|
+
source: `additional[${idx}]`
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
return systemPromptSections;
|
|
1609
|
+
}
|
|
1610
|
+
function logAssembledSystemPrompt(systemPrompt, log) {
|
|
1611
|
+
if (process.env.SKAILE_LOG_SYSTEM_PROMPT !== "0") {
|
|
1612
|
+
const length = systemPrompt?.length ?? 0;
|
|
1613
|
+
log.debug("assembled system prompt", { length });
|
|
1614
|
+
log.debug("system prompt content", { content: systemPrompt ?? "(empty)" });
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
function createSessionDriver(args) {
|
|
1618
|
+
const {
|
|
1619
|
+
config,
|
|
1620
|
+
driverType,
|
|
1621
|
+
agentCwd,
|
|
1622
|
+
provider,
|
|
1623
|
+
model,
|
|
1624
|
+
manifest,
|
|
1625
|
+
maxTurns,
|
|
1626
|
+
apiKey,
|
|
1627
|
+
systemPrompt,
|
|
1628
|
+
thinking,
|
|
1629
|
+
effort,
|
|
1630
|
+
sessionId,
|
|
1631
|
+
resumeSessionId,
|
|
1632
|
+
mcpServers,
|
|
1633
|
+
agentDir
|
|
1634
|
+
} = args;
|
|
1635
|
+
return createDriver(driverType, {
|
|
1636
|
+
cwd: agentCwd,
|
|
1637
|
+
provider,
|
|
1638
|
+
model: model ?? manifest?.model?.preferred,
|
|
1639
|
+
maxTurns,
|
|
1640
|
+
apiKeys: apiKey ? { [provider ?? "anthropic"]: apiKey } : void 0,
|
|
1641
|
+
systemPrompt,
|
|
1642
|
+
tools: manifest?.tools,
|
|
1643
|
+
...thinking ? { thinking } : {},
|
|
1644
|
+
...effort ? { effort } : {},
|
|
1645
|
+
...sessionId ? { sessionId } : {},
|
|
1646
|
+
...resumeSessionId ? { resumeSessionId } : {},
|
|
1647
|
+
...mcpServers ? { mcpServers } : {},
|
|
1648
|
+
// Protocol v2 capability dispatch hooks. When provided, the driver builds
|
|
1649
|
+
// a synthetic `skaile-capabilities` MCP server alongside any existing
|
|
1650
|
+
// mcpServers so capability invocations route through the registry's
|
|
1651
|
+
// `invoke()` (which logs + validates + handles fire-and-forget / render).
|
|
1652
|
+
...config.capabilities ? { capabilities: config.capabilities } : {},
|
|
1653
|
+
...config.aiProviderConfigId ? { aiProviderConfigId: config.aiProviderConfigId } : {},
|
|
1654
|
+
...config.onAuthError ? { onAuthError: config.onAuthError } : {},
|
|
1655
|
+
// Always forward agentDir/agentName for native identity handling
|
|
1656
|
+
...agentDir ? { agentDir, agentName: manifest?.name } : {}
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1523
1659
|
|
|
1524
1660
|
// runner/src/agent.ts
|
|
1525
1661
|
var PROMPT_AGENT = `${pc.magenta(pc.bold("agent"))} ${pc.dim("\u25B6")} `;
|
|
@@ -1601,287 +1737,76 @@ async function buildDriver(opts) {
|
|
|
1601
1737
|
onLog: opts.onLog
|
|
1602
1738
|
});
|
|
1603
1739
|
}
|
|
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;
|
|
1740
|
+
function keywordSearch(capabilities, query, limit) {
|
|
1741
|
+
const tokens = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
1742
|
+
if (tokens.length === 0) return capabilities.slice(0, limit);
|
|
1743
|
+
const scored = capabilities.map((cap) => {
|
|
1744
|
+
const haystack = [cap.name, cap.description, cap.promptFragment ?? ""].join(" ").toLowerCase();
|
|
1745
|
+
let score = 0;
|
|
1746
|
+
for (const token of tokens) {
|
|
1747
|
+
if (haystack.includes(token)) score += 1;
|
|
1633
1748
|
}
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
type: "resource_response",
|
|
1638
|
-
requestId,
|
|
1639
|
-
resourceId: connectorId,
|
|
1640
|
-
operation,
|
|
1641
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1749
|
+
return { cap, score };
|
|
1750
|
+
}).filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
|
|
1751
|
+
return scored.slice(0, limit).map((x) => x.cap);
|
|
1644
1752
|
}
|
|
1645
|
-
function
|
|
1646
|
-
|
|
1753
|
+
function builtinCapabilities(registry) {
|
|
1754
|
+
const listFilterSchema = z5.strictObject({
|
|
1755
|
+
side: z5.enum(["client", "agent"]).optional(),
|
|
1756
|
+
originKind: z5.enum(["framework", "client", "agent", "flow", "skill", "mcp", "connector", "mount"]).optional(),
|
|
1757
|
+
scope: z5.enum(["session", "turn"]).optional()
|
|
1758
|
+
});
|
|
1759
|
+
return [
|
|
1760
|
+
defineCapability({
|
|
1761
|
+
name: "__capabilities.list",
|
|
1762
|
+
displayName: "List capabilities",
|
|
1763
|
+
description: "List available capabilities in this session. Optionally filter by side, origin kind, or scope.",
|
|
1764
|
+
side: "agent",
|
|
1765
|
+
origin: { kind: "framework" },
|
|
1766
|
+
kind: "query",
|
|
1767
|
+
userInvokable: false,
|
|
1768
|
+
input: listFilterSchema,
|
|
1769
|
+
handler: async (args) => registry.list({
|
|
1770
|
+
side: args.side,
|
|
1771
|
+
origin: args.originKind,
|
|
1772
|
+
scope: args.scope
|
|
1773
|
+
})
|
|
1774
|
+
}),
|
|
1775
|
+
defineCapability({
|
|
1776
|
+
name: "__capabilities.search",
|
|
1777
|
+
displayName: "Search capabilities",
|
|
1778
|
+
description: "Keyword search across capability names, descriptions, and prompt fragments. Returns the top matches by score (substring match count).",
|
|
1779
|
+
side: "agent",
|
|
1780
|
+
origin: { kind: "framework" },
|
|
1781
|
+
kind: "query",
|
|
1782
|
+
userInvokable: false,
|
|
1783
|
+
input: z5.strictObject({
|
|
1784
|
+
query: z5.string(),
|
|
1785
|
+
limit: z5.number().int().positive().optional()
|
|
1786
|
+
}),
|
|
1787
|
+
handler: async ({ query, limit }) => keywordSearch(registry.list(), query, limit ?? 10)
|
|
1788
|
+
}),
|
|
1789
|
+
defineCapability({
|
|
1790
|
+
name: "__capabilities.describe",
|
|
1791
|
+
displayName: "Describe capability",
|
|
1792
|
+
description: "Return the full Capability descriptor for a single capability by name.",
|
|
1793
|
+
side: "agent",
|
|
1794
|
+
origin: { kind: "framework" },
|
|
1795
|
+
kind: "query",
|
|
1796
|
+
userInvokable: false,
|
|
1797
|
+
input: z5.strictObject({ name: z5.string() }),
|
|
1798
|
+
handler: async ({ name }) => {
|
|
1799
|
+
const cap = registry.resolve(name);
|
|
1800
|
+
if (!cap) return null;
|
|
1801
|
+
const wire = registry.list().find((c) => c.name === name);
|
|
1802
|
+
return wire ?? null;
|
|
1803
|
+
}
|
|
1804
|
+
})
|
|
1805
|
+
];
|
|
1647
1806
|
}
|
|
1648
|
-
function
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
const buf = Buffer.alloc(8192);
|
|
1652
|
-
const bytesRead = readSync(fd, buf, 0, 8192, 0);
|
|
1653
|
-
closeSync(fd);
|
|
1654
|
-
for (let i = 0; i < bytesRead; i++) {
|
|
1655
|
-
if (buf[i] === 0) return true;
|
|
1656
|
-
}
|
|
1657
|
-
return false;
|
|
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");
|
|
1807
|
+
function bootstrapCapabilityRegistry(registry) {
|
|
1808
|
+
for (const cap of builtinCapabilities(registry)) {
|
|
1809
|
+
registry.register(cap, "agent");
|
|
1885
1810
|
}
|
|
1886
1811
|
}
|
|
1887
1812
|
var DEFAULT_AUDIENCE = ["llm", "user"];
|
|
@@ -2228,25 +2153,218 @@ function rejectCapabilityOnApprovalDeny(pending, callId, feedback) {
|
|
|
2228
2153
|
entry.reject(new Error(feedback ?? "declined by user"));
|
|
2229
2154
|
return true;
|
|
2230
2155
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2156
|
+
async function handleResourceRequest(command, manager, emit) {
|
|
2157
|
+
const { requestId, connectorId, operation, path: path5, options, content } = command;
|
|
2158
|
+
try {
|
|
2159
|
+
let data;
|
|
2160
|
+
switch (operation) {
|
|
2161
|
+
case "list":
|
|
2162
|
+
data = await manager.list(
|
|
2163
|
+
connectorId,
|
|
2164
|
+
path5,
|
|
2165
|
+
options
|
|
2166
|
+
);
|
|
2167
|
+
break;
|
|
2168
|
+
case "read":
|
|
2169
|
+
data = await manager.read(connectorId, path5);
|
|
2170
|
+
break;
|
|
2171
|
+
case "write":
|
|
2172
|
+
await manager.write(connectorId, path5, content);
|
|
2173
|
+
data = true;
|
|
2174
|
+
break;
|
|
2175
|
+
case "delete":
|
|
2176
|
+
data = await manager.delete(connectorId, path5);
|
|
2177
|
+
break;
|
|
2178
|
+
case "search":
|
|
2179
|
+
data = await manager.search(
|
|
2180
|
+
connectorId,
|
|
2181
|
+
path5,
|
|
2182
|
+
options
|
|
2183
|
+
);
|
|
2184
|
+
break;
|
|
2245
2185
|
}
|
|
2246
|
-
|
|
2186
|
+
emit({ type: "resource_response", requestId, resourceId: connectorId, operation, data });
|
|
2187
|
+
} catch (err) {
|
|
2188
|
+
emit({
|
|
2189
|
+
type: "resource_response",
|
|
2190
|
+
requestId,
|
|
2191
|
+
resourceId: connectorId,
|
|
2192
|
+
operation,
|
|
2193
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2194
|
+
});
|
|
2247
2195
|
}
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2196
|
+
}
|
|
2197
|
+
function getMimeType(filename) {
|
|
2198
|
+
return mime.getType(filename) ?? "application/octet-stream";
|
|
2199
|
+
}
|
|
2200
|
+
function isBinaryFile(filePath) {
|
|
2201
|
+
try {
|
|
2202
|
+
const fd = openSync(filePath, "r");
|
|
2203
|
+
const buf = Buffer.alloc(8192);
|
|
2204
|
+
const bytesRead = readSync(fd, buf, 0, 8192, 0);
|
|
2205
|
+
closeSync(fd);
|
|
2206
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
2207
|
+
if (buf[i] === 0) return true;
|
|
2208
|
+
}
|
|
2209
|
+
return false;
|
|
2210
|
+
} catch {
|
|
2211
|
+
return false;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
function handleMountResourceRequest(command, manager, emit) {
|
|
2215
|
+
const { requestId, connectorId: mountId, operation, path: reqPath, content } = command;
|
|
2216
|
+
try {
|
|
2217
|
+
const entry = manager.get(mountId);
|
|
2218
|
+
const mountPath = entry.handle.mountPath;
|
|
2219
|
+
if (!mountPath) {
|
|
2220
|
+
throw new Error(`Connector "${mountId}" has no projected mount directory`);
|
|
2221
|
+
}
|
|
2222
|
+
const targetPath = reqPath ? resolve(mountPath, reqPath) : mountPath;
|
|
2223
|
+
const rel = relative(mountPath, targetPath);
|
|
2224
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
2225
|
+
throw new Error("Path traversal outside mount");
|
|
2226
|
+
}
|
|
2227
|
+
let data;
|
|
2228
|
+
switch (operation) {
|
|
2229
|
+
case "list": {
|
|
2230
|
+
const entries = readdirSync(targetPath, { withFileTypes: true });
|
|
2231
|
+
data = entries.filter((e) => !e.name.startsWith(".")).map((e) => {
|
|
2232
|
+
const entryPath = reqPath ? join(reqPath, e.name) : e.name;
|
|
2233
|
+
const result = {
|
|
2234
|
+
name: e.name,
|
|
2235
|
+
path: entryPath,
|
|
2236
|
+
type: e.isDirectory() ? "directory" : "file"
|
|
2237
|
+
};
|
|
2238
|
+
if (!e.isDirectory()) {
|
|
2239
|
+
try {
|
|
2240
|
+
result.size = statSync(join(targetPath, e.name)).size;
|
|
2241
|
+
} catch {
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
return result;
|
|
2245
|
+
});
|
|
2246
|
+
break;
|
|
2247
|
+
}
|
|
2248
|
+
case "read": {
|
|
2249
|
+
const contentType = getMimeType(basename(targetPath));
|
|
2250
|
+
if (isBinaryFile(targetPath)) {
|
|
2251
|
+
const buf = readFileSync(targetPath);
|
|
2252
|
+
data = {
|
|
2253
|
+
data: buf.toString("base64"),
|
|
2254
|
+
contentType,
|
|
2255
|
+
encoding: "binary"
|
|
2256
|
+
};
|
|
2257
|
+
} else {
|
|
2258
|
+
data = {
|
|
2259
|
+
data: readFileSync(targetPath, "utf-8"),
|
|
2260
|
+
contentType,
|
|
2261
|
+
encoding: "utf-8"
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
break;
|
|
2265
|
+
}
|
|
2266
|
+
case "write": {
|
|
2267
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
2268
|
+
const writeContent = typeof content === "string" ? content : content?.data ?? "";
|
|
2269
|
+
const encoding = typeof content === "string" ? void 0 : content?.encoding;
|
|
2270
|
+
if (encoding === "binary") {
|
|
2271
|
+
writeFileSync(targetPath, Buffer.from(writeContent, "base64"));
|
|
2272
|
+
} else {
|
|
2273
|
+
writeFileSync(targetPath, writeContent, "utf-8");
|
|
2274
|
+
}
|
|
2275
|
+
data = true;
|
|
2276
|
+
break;
|
|
2277
|
+
}
|
|
2278
|
+
case "delete": {
|
|
2279
|
+
unlinkSync(targetPath);
|
|
2280
|
+
data = true;
|
|
2281
|
+
break;
|
|
2282
|
+
}
|
|
2283
|
+
default:
|
|
2284
|
+
throw new Error(`Unsupported operation for mount: ${operation}`);
|
|
2285
|
+
}
|
|
2286
|
+
emit({ type: "resource_response", requestId, resourceId: mountId, operation, data });
|
|
2287
|
+
} catch (err) {
|
|
2288
|
+
emit({
|
|
2289
|
+
type: "resource_response",
|
|
2290
|
+
requestId,
|
|
2291
|
+
resourceId: mountId,
|
|
2292
|
+
operation,
|
|
2293
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
// runner/src/ai-credential-refresh.ts
|
|
2299
|
+
var PROACTIVE_AI_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
2300
|
+
function createAiCredentialRefreshScheduler(deps) {
|
|
2301
|
+
const now = deps.now ?? (() => Date.now());
|
|
2302
|
+
const setTimeoutImpl = deps.setTimeoutImpl ?? ((handler, ms) => {
|
|
2303
|
+
const handle = setTimeout(handler, ms);
|
|
2304
|
+
return handle;
|
|
2305
|
+
});
|
|
2306
|
+
const clearTimeoutImpl = deps.clearTimeoutImpl ?? ((handle) => {
|
|
2307
|
+
clearTimeout(handle);
|
|
2308
|
+
});
|
|
2309
|
+
const marginMs = deps.marginMs ?? PROACTIVE_AI_REFRESH_MARGIN_MS;
|
|
2310
|
+
let timer = null;
|
|
2311
|
+
function cancel() {
|
|
2312
|
+
if (timer !== null) {
|
|
2313
|
+
clearTimeoutImpl(timer);
|
|
2314
|
+
timer = null;
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
async function fire() {
|
|
2318
|
+
timer = null;
|
|
2319
|
+
let next;
|
|
2320
|
+
try {
|
|
2321
|
+
next = await deps.refresh();
|
|
2322
|
+
} catch (err) {
|
|
2323
|
+
deps.log(
|
|
2324
|
+
`[ai-credential-refresh] refresh threw: ${err instanceof Error ? err.message : String(err)}; reactive 401 path remains as safety net`
|
|
2325
|
+
);
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
if (next === void 0) {
|
|
2329
|
+
deps.log(
|
|
2330
|
+
"[ai-credential-refresh] refresh returned no expiry; reactive 401 path remains as safety net"
|
|
2331
|
+
);
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
schedule(next);
|
|
2335
|
+
}
|
|
2336
|
+
function schedule(expiresAtIso) {
|
|
2337
|
+
cancel();
|
|
2338
|
+
if (!expiresAtIso) {
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
const expiresAtMs = Date.parse(expiresAtIso);
|
|
2342
|
+
if (!Number.isFinite(expiresAtMs)) return;
|
|
2343
|
+
const delayMs = expiresAtMs - now() - marginMs;
|
|
2344
|
+
if (delayMs <= 0) {
|
|
2345
|
+
deps.log(
|
|
2346
|
+
`[ai-credential-refresh] token within ${marginMs / 1e3}s of expiry; refreshing now`
|
|
2347
|
+
);
|
|
2348
|
+
timer = setTimeoutImpl(() => {
|
|
2349
|
+
void fire();
|
|
2350
|
+
}, 0);
|
|
2351
|
+
} else {
|
|
2352
|
+
deps.log(
|
|
2353
|
+
`[ai-credential-refresh] scheduled in ${Math.round(delayMs / 1e3)}s (expiresAt=${expiresAtIso})`
|
|
2354
|
+
);
|
|
2355
|
+
timer = setTimeoutImpl(() => {
|
|
2356
|
+
void fire();
|
|
2357
|
+
}, delayMs);
|
|
2358
|
+
}
|
|
2359
|
+
timer.unref?.();
|
|
2360
|
+
}
|
|
2361
|
+
return {
|
|
2362
|
+
schedule,
|
|
2363
|
+
cancel,
|
|
2364
|
+
isPending: () => timer !== null
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2250
2368
|
// runner/src/compaction/prompt.ts
|
|
2251
2369
|
var DEFAULT_COMPACTION_PROMPT = `You are being asked to compact this conversation into a structured summary.
|
|
2252
2370
|
This summary will replace the full conversation history when the session
|
|
@@ -2325,6 +2443,24 @@ ${opts.focus}`);
|
|
|
2325
2443
|
return parts.join("");
|
|
2326
2444
|
}
|
|
2327
2445
|
|
|
2446
|
+
// runner/src/compaction/strategy.ts
|
|
2447
|
+
var TokenThresholdStrategy = class {
|
|
2448
|
+
thresholdPercent;
|
|
2449
|
+
minCooldownMs;
|
|
2450
|
+
constructor(opts) {
|
|
2451
|
+
this.thresholdPercent = opts.thresholdPercent;
|
|
2452
|
+
this.minCooldownMs = opts.minCooldownMs;
|
|
2453
|
+
}
|
|
2454
|
+
shouldCompact(context) {
|
|
2455
|
+
const fillPercent = context.estimatedTokens / context.contextWindow * 100;
|
|
2456
|
+
if (fillPercent < this.thresholdPercent) return false;
|
|
2457
|
+
if (context.timeSinceLastCompaction !== null && context.timeSinceLastCompaction < this.minCooldownMs) {
|
|
2458
|
+
return false;
|
|
2459
|
+
}
|
|
2460
|
+
return true;
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2463
|
+
|
|
2328
2464
|
// runner/src/compaction/orchestrator.ts
|
|
2329
2465
|
var MIN_SUMMARY_CHARS = 200;
|
|
2330
2466
|
var MAX_COMPRESSION_RATIO = 0.9;
|
|
@@ -2370,9 +2506,6 @@ var CompactionOrchestrator = class {
|
|
|
2370
2506
|
minCooldownMs: this.config.minCooldownMs
|
|
2371
2507
|
});
|
|
2372
2508
|
}
|
|
2373
|
-
get hasNewMessagesSinceCompaction() {
|
|
2374
|
-
return this.messagesSinceCompaction > 0;
|
|
2375
|
-
}
|
|
2376
2509
|
trackMessage(seq) {
|
|
2377
2510
|
this.currentSeq = seq;
|
|
2378
2511
|
this.messagesSinceCompaction++;
|
|
@@ -2406,6 +2539,17 @@ var CompactionOrchestrator = class {
|
|
|
2406
2539
|
const driverProvider = this.driver.driverInfo?.id ?? null;
|
|
2407
2540
|
const coversFromSeq = this.lastCompactionSeq ?? 0;
|
|
2408
2541
|
const capabilitySignatureAtCompaction = this.opts.getCurrentCapabilitySignature?.() ?? null;
|
|
2542
|
+
const attemptBase = {
|
|
2543
|
+
trigger,
|
|
2544
|
+
model: driverModel,
|
|
2545
|
+
provider: driverProvider,
|
|
2546
|
+
inputTokens: tokensBefore,
|
|
2547
|
+
coversFromSeq,
|
|
2548
|
+
coversToSeq: this.currentSeq,
|
|
2549
|
+
driverSessionIdAtCompaction: driverSessionId,
|
|
2550
|
+
modelAtCompaction: driverModel,
|
|
2551
|
+
capabilitySignatureAtCompaction
|
|
2552
|
+
};
|
|
2409
2553
|
this.opts.onCompactionStart?.(trigger);
|
|
2410
2554
|
this.opts.onLog?.(`[compaction] Starting (trigger=${trigger}, tokens=${tokensBefore})`);
|
|
2411
2555
|
const prompt = buildCompactionPrompt({
|
|
@@ -2414,6 +2558,33 @@ var CompactionOrchestrator = class {
|
|
|
2414
2558
|
mcpDirectives: this.opts.mcpDirectives,
|
|
2415
2559
|
focus
|
|
2416
2560
|
});
|
|
2561
|
+
const { summaryText, promptError } = await this._runCompactionPrompt(prompt);
|
|
2562
|
+
if (promptError !== null) {
|
|
2563
|
+
this._emitThrownFailure(attemptBase, promptError);
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
const actualUsage = this.driver.getTokenUsage();
|
|
2567
|
+
const tokensAfter = actualUsage?.outputTokens ?? Math.ceil(summaryText.length / 4);
|
|
2568
|
+
const validation = validateCompactionOutput(summaryText, tokensBefore, tokensAfter);
|
|
2569
|
+
if (!validation.ok) {
|
|
2570
|
+
this._emitValidationFailure(attemptBase, validation.errorCode, tokensAfter);
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
return await this._completeCompaction(
|
|
2574
|
+
attemptBase,
|
|
2575
|
+
trigger,
|
|
2576
|
+
summaryText,
|
|
2577
|
+
tokensBefore,
|
|
2578
|
+
tokensAfter,
|
|
2579
|
+
coversFromSeq
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
/**
|
|
2583
|
+
* Drive the compaction `prompt()` call, capturing summary text from the
|
|
2584
|
+
* driver's raw bridge-level events and any thrown error. The listener is
|
|
2585
|
+
* always removed in `finally`.
|
|
2586
|
+
*/
|
|
2587
|
+
async _runCompactionPrompt(prompt) {
|
|
2417
2588
|
let summaryText = "";
|
|
2418
2589
|
const captureHandler = (raw) => {
|
|
2419
2590
|
const event = raw;
|
|
@@ -2444,59 +2615,55 @@ var CompactionOrchestrator = class {
|
|
|
2444
2615
|
} finally {
|
|
2445
2616
|
this.driver.removeListener("agent-event", captureHandler);
|
|
2446
2617
|
}
|
|
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
|
-
}
|
|
2618
|
+
return { summaryText, promptError };
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Record a `failed` attempt for a thrown `prompt()` error: classify the
|
|
2622
|
+
* error, map it to an error code, log, emit the attempt event, and signal
|
|
2623
|
+
* compaction end.
|
|
2624
|
+
*/
|
|
2625
|
+
_emitThrownFailure(attemptBase, promptError) {
|
|
2626
|
+
const classified = classifyClaudeSdkError(
|
|
2627
|
+
promptError instanceof Error ? promptError.message : String(promptError)
|
|
2628
|
+
);
|
|
2629
|
+
const errorCode = classified.category === "rate_limit" ? "rate_limit" : classified.category === "auth" ? "auth" : classified.category === "network" ? "timeout" : classified.category === "validation" ? "parse_error" : classified.category;
|
|
2630
|
+
this.opts.onLog?.(`[compaction] Failed: ${classified.message} (${errorCode})`);
|
|
2631
|
+
this.opts.onCompactionAttempt?.({
|
|
2632
|
+
type: "compaction_attempt",
|
|
2633
|
+
status: "failed",
|
|
2634
|
+
errorCode,
|
|
2635
|
+
summary: "",
|
|
2636
|
+
outputTokens: 0,
|
|
2637
|
+
validatedAt: null,
|
|
2638
|
+
...attemptBase
|
|
2639
|
+
});
|
|
2640
|
+
this.opts.onCompactionEnd?.();
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Record a `failed` attempt for output that passed `prompt()` but failed
|
|
2644
|
+
* structural validation. Logs, emits the attempt event, and signals
|
|
2645
|
+
* compaction end.
|
|
2646
|
+
*/
|
|
2647
|
+
_emitValidationFailure(attemptBase, errorCode, tokensAfter) {
|
|
2648
|
+
this.opts.onLog?.(
|
|
2649
|
+
`[compaction] Output failed validation (${errorCode}); recording failed attempt`
|
|
2650
|
+
);
|
|
2651
|
+
this.opts.onCompactionAttempt?.({
|
|
2652
|
+
type: "compaction_attempt",
|
|
2653
|
+
status: "failed",
|
|
2654
|
+
errorCode,
|
|
2655
|
+
summary: "",
|
|
2656
|
+
outputTokens: tokensAfter,
|
|
2657
|
+
validatedAt: null,
|
|
2658
|
+
...attemptBase
|
|
2659
|
+
});
|
|
2660
|
+
this.opts.onCompactionEnd?.();
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Success path: emit the attempt + snapshot events, update internal state,
|
|
2664
|
+
* and (unless hibernating) reset the driver and inject the snapshot.
|
|
2665
|
+
*/
|
|
2666
|
+
async _completeCompaction(attemptBase, trigger, summaryText, tokensBefore, tokensAfter, coversFromSeq) {
|
|
2500
2667
|
const validatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2501
2668
|
const snapshot = {
|
|
2502
2669
|
type: "snapshot",
|
|
@@ -2511,18 +2678,10 @@ var CompactionOrchestrator = class {
|
|
|
2511
2678
|
type: "compaction_attempt",
|
|
2512
2679
|
status: "success",
|
|
2513
2680
|
errorCode: null,
|
|
2514
|
-
trigger,
|
|
2515
2681
|
summary: summaryText,
|
|
2516
|
-
model: driverModel,
|
|
2517
|
-
provider: driverProvider,
|
|
2518
|
-
inputTokens: tokensBefore,
|
|
2519
2682
|
outputTokens: tokensAfter,
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
driverSessionIdAtCompaction: driverSessionId,
|
|
2523
|
-
modelAtCompaction: driverModel,
|
|
2524
|
-
capabilitySignatureAtCompaction,
|
|
2525
|
-
validatedAt
|
|
2683
|
+
validatedAt,
|
|
2684
|
+
...attemptBase
|
|
2526
2685
|
});
|
|
2527
2686
|
this.opts.onSnapshot?.(snapshot);
|
|
2528
2687
|
this.lastCompactionSeq = this.currentSeq + 1;
|
|
@@ -2545,11 +2704,6 @@ The conversation has been compacted. The above snapshot contains the full contex
|
|
|
2545
2704
|
);
|
|
2546
2705
|
return snapshot;
|
|
2547
2706
|
}
|
|
2548
|
-
initFromSnapshot(lastSnapshotSeq, lastSnapshotTime) {
|
|
2549
|
-
this.lastCompactionSeq = lastSnapshotSeq;
|
|
2550
|
-
this.lastCompactionTime = lastSnapshotTime;
|
|
2551
|
-
this.messagesSinceCompaction = 0;
|
|
2552
|
-
}
|
|
2553
2707
|
};
|
|
2554
2708
|
function rewriteFileChangedPath(event, cwd, mounts) {
|
|
2555
2709
|
if (event.type !== "file_changed") return event;
|
|
@@ -2782,6 +2936,54 @@ function buildRunnerCapabilities(handlers) {
|
|
|
2782
2936
|
})
|
|
2783
2937
|
];
|
|
2784
2938
|
}
|
|
2939
|
+
var CLAUDE_CODE_CREDENTIALS_KEY = "CLAUDE_CODE_CREDENTIALS_JSON";
|
|
2940
|
+
async function writeClaudeCodeCredentialsFile(home, json) {
|
|
2941
|
+
const claudeDir = join(home, ".claude");
|
|
2942
|
+
await mkdir(claudeDir, { recursive: true, mode: 448 });
|
|
2943
|
+
const finalPath = join(claudeDir, ".credentials.json");
|
|
2944
|
+
const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
2945
|
+
await writeFile(tmpPath, json, { mode: 384 });
|
|
2946
|
+
await rename(tmpPath, finalPath);
|
|
2947
|
+
return finalPath;
|
|
2948
|
+
}
|
|
2949
|
+
async function mergeAndRewriteCredentialsFile(home, accessToken, expiresAt) {
|
|
2950
|
+
const credPath = join(home, ".claude", ".credentials.json");
|
|
2951
|
+
let merged = {};
|
|
2952
|
+
if (existsSync(credPath)) {
|
|
2953
|
+
try {
|
|
2954
|
+
merged = JSON.parse(readFileSync(credPath, "utf-8"));
|
|
2955
|
+
} catch {
|
|
2956
|
+
merged = {};
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
const oauthBlock = merged.claudeAiOauth ?? {};
|
|
2960
|
+
oauthBlock.accessToken = accessToken;
|
|
2961
|
+
if (expiresAt) oauthBlock.expiresAt = expiresAt;
|
|
2962
|
+
merged.claudeAiOauth = oauthBlock;
|
|
2963
|
+
merged.accessToken = accessToken;
|
|
2964
|
+
return writeClaudeCodeCredentialsFile(home, JSON.stringify(merged));
|
|
2965
|
+
}
|
|
2966
|
+
function extractClaudeAiOauthExpiresAt(json) {
|
|
2967
|
+
let parsed;
|
|
2968
|
+
try {
|
|
2969
|
+
parsed = JSON.parse(json);
|
|
2970
|
+
} catch {
|
|
2971
|
+
return null;
|
|
2972
|
+
}
|
|
2973
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2974
|
+
const obj = parsed;
|
|
2975
|
+
const oauth = obj.claudeAiOauth;
|
|
2976
|
+
const raw = oauth?.expiresAt ?? obj.expiresAt;
|
|
2977
|
+
if (raw == null) return null;
|
|
2978
|
+
if (typeof raw === "string") {
|
|
2979
|
+
const t = Date.parse(raw);
|
|
2980
|
+
return Number.isFinite(t) ? new Date(t).toISOString() : null;
|
|
2981
|
+
}
|
|
2982
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
2983
|
+
return new Date(raw).toISOString();
|
|
2984
|
+
}
|
|
2985
|
+
return null;
|
|
2986
|
+
}
|
|
2785
2987
|
|
|
2786
2988
|
// runner/src/session-resource-handlers.ts
|
|
2787
2989
|
function asDeclaration(declaration) {
|
|
@@ -2852,54 +3054,6 @@ function buildSessionResourceHandlers(deps) {
|
|
|
2852
3054
|
}
|
|
2853
3055
|
};
|
|
2854
3056
|
}
|
|
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
3057
|
|
|
2904
3058
|
// runner/src/session-stimulus.ts
|
|
2905
3059
|
var DEFAULT_COALESCE_MS = 50;
|
|
@@ -3086,6 +3240,17 @@ function ensureGitConfigInclude(managedGitconfigPath) {
|
|
|
3086
3240
|
`;
|
|
3087
3241
|
writeFileSync(gitconfigPath, block, { mode: 420 });
|
|
3088
3242
|
}
|
|
3243
|
+
function setupManagedGitconfig(projectDir, serverLog) {
|
|
3244
|
+
const managedGitconfigPath = join(projectDir, ".skaile", "managed-gitconfig");
|
|
3245
|
+
process.env.GIT_CONFIG_GLOBAL = managedGitconfigPath;
|
|
3246
|
+
try {
|
|
3247
|
+
ensureGitConfigInclude(managedGitconfigPath);
|
|
3248
|
+
} catch (err) {
|
|
3249
|
+
serverLog.warn("failed to write ~/.gitconfig include \u2014 relying on env only", {
|
|
3250
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3089
3254
|
async function startAgentServer(opts) {
|
|
3090
3255
|
const port = opts.port ?? 8080;
|
|
3091
3256
|
const sessionId = opts.sessionId ?? process.env.SKAILE_SESSION_ID ?? "session-unknown";
|
|
@@ -3105,15 +3270,7 @@ async function startAgentServer(opts) {
|
|
|
3105
3270
|
const log = (line) => serverLog.info(line);
|
|
3106
3271
|
const profile = wsConfig.agent_config?.default;
|
|
3107
3272
|
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
|
-
}
|
|
3273
|
+
setupManagedGitconfig(opts.projectDir, serverLog);
|
|
3117
3274
|
const agentDir = opts.agentDir ?? resolveAgentDir(opts.projectDir);
|
|
3118
3275
|
if (agentDir) {
|
|
3119
3276
|
serverLog.info("agent dir resolved", { agentDir });
|
|
@@ -3352,26 +3509,32 @@ async function startAgentServer(opts) {
|
|
|
3352
3509
|
let secretProvider;
|
|
3353
3510
|
let runtimeIdentity;
|
|
3354
3511
|
let preMintedSecrets;
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3512
|
+
stageMaterializedSkillsForSession();
|
|
3513
|
+
function stageMaterializedSkillsForSession() {
|
|
3514
|
+
try {
|
|
3515
|
+
const { skillsDir } = resolveDriverPaths({
|
|
3516
|
+
driver: opts.driver ?? wsConfig.agent_config?.default?.driver ?? "claude-sdk"
|
|
3517
|
+
});
|
|
3518
|
+
const strict = process.env.SKAILE_STRICT_YAML === "true";
|
|
3519
|
+
const declared = strict ? (wsConfig.dependencies ?? []).flatMap((dep) => {
|
|
3520
|
+
try {
|
|
3521
|
+
const ref = parseAssetRef(dep);
|
|
3522
|
+
return ref.kind === "skill" ? [ref.name] : [];
|
|
3523
|
+
} catch {
|
|
3524
|
+
return [];
|
|
3525
|
+
}
|
|
3526
|
+
}) : [];
|
|
3527
|
+
const stagedSkills = stageMaterializedSkills(opts.projectDir, skillsDir, {
|
|
3528
|
+
strict,
|
|
3529
|
+
declared
|
|
3530
|
+
});
|
|
3531
|
+
if (stagedSkills.length > 0) {
|
|
3532
|
+
log(
|
|
3533
|
+
`[serve] staged ${stagedSkills.length} materialized skill(s): ${stagedSkills.join(", ")}`
|
|
3534
|
+
);
|
|
3366
3535
|
}
|
|
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
|
-
);
|
|
3536
|
+
} catch {
|
|
3373
3537
|
}
|
|
3374
|
-
} catch {
|
|
3375
3538
|
}
|
|
3376
3539
|
if (secretsMode === "provisioned") {
|
|
3377
3540
|
log("[serve] secrets mode: provisioned \u2014 waiting for provision_secrets command");
|
|
@@ -3398,7 +3561,11 @@ async function startAgentServer(opts) {
|
|
|
3398
3561
|
}
|
|
3399
3562
|
}
|
|
3400
3563
|
if (secretsMode === "env" && agentSession.runtimeAssets) {
|
|
3401
|
-
|
|
3564
|
+
emitRuntimeAssetWarnings(agentSession.runtimeAssets);
|
|
3565
|
+
}
|
|
3566
|
+
validateSkaileYamlDependencies();
|
|
3567
|
+
function emitRuntimeAssetWarnings(runtimeAssets) {
|
|
3568
|
+
for (const w of runtimeAssets.warnings) {
|
|
3402
3569
|
const asset = w.code === "missing_driver" && w.declarationId ? `${w.declarationId} \u2192 ${w.name ?? "unknown"}` : w.name ?? w.code;
|
|
3403
3570
|
log(`[serve] validation_warning: ${w.code}: ${w.message}`);
|
|
3404
3571
|
sendEvent({
|
|
@@ -3409,29 +3576,31 @@ async function startAgentServer(opts) {
|
|
|
3409
3576
|
});
|
|
3410
3577
|
}
|
|
3411
3578
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3579
|
+
function validateSkaileYamlDependencies() {
|
|
3580
|
+
try {
|
|
3581
|
+
const wsConfig2 = resolveSkWorkspaceConfig(opts.projectDir);
|
|
3582
|
+
for (const dep of wsConfig2.dependencies ?? []) {
|
|
3583
|
+
let ref;
|
|
3584
|
+
try {
|
|
3585
|
+
const parsed = parseAssetRef(dep);
|
|
3586
|
+
if (parsed.kind !== "skill") continue;
|
|
3587
|
+
ref = parsed.name;
|
|
3588
|
+
} catch {
|
|
3589
|
+
continue;
|
|
3590
|
+
}
|
|
3591
|
+
const skillDir = join(opts.projectDir, ".claude", "skills", ref);
|
|
3592
|
+
if (!existsSync(skillDir)) {
|
|
3593
|
+
log(`[serve] validation_warning: dependency "${dep}" not found \u2014 continuing without it`);
|
|
3594
|
+
sendEvent({
|
|
3595
|
+
type: "validation_warning",
|
|
3596
|
+
asset: dep,
|
|
3597
|
+
reason: "not_found",
|
|
3598
|
+
suggestion: "Sync the catalog and re-add this asset"
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3432
3601
|
}
|
|
3602
|
+
} catch {
|
|
3433
3603
|
}
|
|
3434
|
-
} catch {
|
|
3435
3604
|
}
|
|
3436
3605
|
const normalizer = new EventNormalizer();
|
|
3437
3606
|
function forwardNormalizedEvent(event) {
|
|
@@ -3706,505 +3875,731 @@ async function startAgentServer(opts) {
|
|
|
3706
3875
|
}
|
|
3707
3876
|
}
|
|
3708
3877
|
}
|
|
3709
|
-
async function
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3878
|
+
async function awaitSessionReadyOrFail() {
|
|
3879
|
+
try {
|
|
3880
|
+
await sessionReady;
|
|
3881
|
+
return true;
|
|
3882
|
+
} catch (err) {
|
|
3883
|
+
sendEvent({
|
|
3884
|
+
type: "error",
|
|
3885
|
+
message: `Session not available: ${err.message}`,
|
|
3886
|
+
fatal: true
|
|
3887
|
+
});
|
|
3888
|
+
return false;
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
function registerInitClientCapabilities(incoming) {
|
|
3892
|
+
for (const wire of incoming) {
|
|
3893
|
+
if (wire.audience && !wire.audience.includes("llm") && !wire.audience.includes("user")) {
|
|
3894
|
+
continue;
|
|
3895
|
+
}
|
|
3896
|
+
const stub = {
|
|
3897
|
+
...wire,
|
|
3898
|
+
inputZod: { parse: (v) => v },
|
|
3899
|
+
outputZod: void 0,
|
|
3900
|
+
handler: buildClientCapabilityHandler2(wire)
|
|
3901
|
+
};
|
|
3902
|
+
capabilityRegistry.register(stub, "client");
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
async function provisionInitSecrets(secrets) {
|
|
3906
|
+
let initialAiCredentialExpiresAt = null;
|
|
3907
|
+
if (secrets) {
|
|
3908
|
+
const ccdValue = secrets[CLAUDE_CODE_CREDENTIALS_KEY];
|
|
3909
|
+
if (ccdValue) {
|
|
3910
|
+
const home = process.env.HOME ?? homedir();
|
|
3911
|
+
if (home) {
|
|
3912
|
+
try {
|
|
3913
|
+
await writeClaudeCodeCredentialsFile(home, ccdValue);
|
|
3914
|
+
log(
|
|
3915
|
+
`[serve] wrote Claude Code credentials file to ${home}/.claude/.credentials.json (mode 0600)`
|
|
3916
|
+
);
|
|
3917
|
+
} catch (err) {
|
|
3918
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3919
|
+
log(`[serve] failed to write Claude Code credentials file: ${errMsg}`);
|
|
3920
|
+
}
|
|
3717
3921
|
}
|
|
3922
|
+
initialAiCredentialExpiresAt = extractClaudeAiOauthExpiresAt(ccdValue);
|
|
3923
|
+
}
|
|
3924
|
+
const otherSecrets = Object.fromEntries(
|
|
3925
|
+
Object.entries(secrets).filter(([k]) => k !== CLAUDE_CODE_CREDENTIALS_KEY)
|
|
3718
3926
|
);
|
|
3719
|
-
|
|
3927
|
+
const provider = new InMemorySecretProvider();
|
|
3928
|
+
provider.provision(otherSecrets);
|
|
3929
|
+
secretProvider = provider;
|
|
3930
|
+
secretsProvisioned = true;
|
|
3720
3931
|
}
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3932
|
+
return initialAiCredentialExpiresAt;
|
|
3933
|
+
}
|
|
3934
|
+
async function buildSessionFromInit(secrets) {
|
|
3935
|
+
const effectiveProvider = opts.provider ?? "anthropic";
|
|
3936
|
+
const envKey = providerEnvKey(effectiveProvider);
|
|
3937
|
+
const apiKeyFromSecrets = secrets?.[envKey];
|
|
3938
|
+
sessionConfig = buildSessionConfig(secretProvider, apiKeyFromSecrets);
|
|
3939
|
+
if (!agentSession) {
|
|
3940
|
+
agentSession = await createAgentSession(sessionConfig);
|
|
3941
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3942
|
+
resourceManager = agentSession.resourceManager;
|
|
3943
|
+
driver = agentSession.driver;
|
|
3944
|
+
rebuildCompactionOrchestrator();
|
|
3945
|
+
registerSessionComposition(agentSession);
|
|
3946
|
+
sendEvent({
|
|
3947
|
+
type: "resources_available",
|
|
3948
|
+
...splitConnectorsForWire(resourceManager)
|
|
3949
|
+
});
|
|
3950
|
+
agentSession.startWatching();
|
|
3951
|
+
capabilityRegistry.setRemoteInvoker(remoteCapabilityInvoker);
|
|
3952
|
+
const runnerCaps = buildRunnerCapabilities({
|
|
3953
|
+
// Resource hot-plug handlers, wired to the live session's
|
|
3954
|
+
// ConnectorManager + PreMintedSecretProvider via getters because
|
|
3955
|
+
// both are lazily (re)assigned in this scope.
|
|
3956
|
+
...buildSessionResourceHandlers({
|
|
3957
|
+
getResourceManager: () => resourceManager,
|
|
3958
|
+
getPreMintedSecrets: () => preMintedSecrets,
|
|
3959
|
+
emitResourcesAvailable: () => {
|
|
3960
|
+
sendEvent({
|
|
3961
|
+
type: "resources_available",
|
|
3962
|
+
...splitConnectorsForWire(resourceManager)
|
|
3963
|
+
});
|
|
3964
|
+
},
|
|
3965
|
+
log
|
|
3966
|
+
})
|
|
3967
|
+
// setLogLevel left undefined — the cap's built-in fallback calls
|
|
3968
|
+
// getLogStore()?.setLevel(...) directly. setState / lifecycle
|
|
3969
|
+
// remain stubs (not resource hot-plug; out of Sprint 2 scope).
|
|
3970
|
+
});
|
|
3971
|
+
for (const cap of runnerCaps) {
|
|
3972
|
+
capabilityRegistry.register(cap, "agent");
|
|
3973
|
+
}
|
|
3974
|
+
log("[serve] session built from session_init envelope");
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
async function recreateSessionForInit(pendingResumeSessionId) {
|
|
3978
|
+
await agentSession.dispose();
|
|
3979
|
+
agentSession = await createAgentSession({
|
|
3980
|
+
...sessionConfig,
|
|
3981
|
+
...aiProviderConfigId ? { aiProviderConfigId } : {},
|
|
3982
|
+
...pendingResumeSessionId ? { resumeSessionId: pendingResumeSessionId } : {}
|
|
3983
|
+
});
|
|
3984
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
3985
|
+
driver = agentSession.driver;
|
|
3986
|
+
normalizer.reset();
|
|
3987
|
+
driverStarted = false;
|
|
3988
|
+
rebuildCompactionOrchestrator();
|
|
3989
|
+
}
|
|
3990
|
+
async function applyInitSharedState(sharedState) {
|
|
3991
|
+
if (!ensureResourceManagerForSharedState()) {
|
|
3992
|
+
sendEvent({ type: "configured" });
|
|
3993
|
+
return false;
|
|
3994
|
+
}
|
|
3995
|
+
let storesRegistered = 0;
|
|
3996
|
+
for (const stateConfig of sharedState) {
|
|
3997
|
+
if (await registerSharedStateStore(stateConfig)) storesRegistered++;
|
|
3998
|
+
}
|
|
3999
|
+
await rebuildMcpToolsForNewStores(storesRegistered);
|
|
4000
|
+
return true;
|
|
4001
|
+
}
|
|
4002
|
+
function ensureResourceManagerForSharedState() {
|
|
4003
|
+
if (resourceManager) return true;
|
|
4004
|
+
try {
|
|
4005
|
+
registerBuiltinConnectors();
|
|
4006
|
+
resourceManager = new ConnectorManager(opts.projectDir, {
|
|
4007
|
+
catalogEntries: getSessionCatalogEntries(agentSession)
|
|
4008
|
+
});
|
|
4009
|
+
log("[serve] created ConnectorManager for sharedState (no connectors were declared)");
|
|
4010
|
+
return true;
|
|
4011
|
+
} catch (err) {
|
|
4012
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4013
|
+
log(`[serve] failed to create ConnectorManager: ${errMsg}`);
|
|
4014
|
+
return false;
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
async function registerSharedStateStore(stateConfig) {
|
|
4018
|
+
try {
|
|
4019
|
+
if (resourceManager.has(stateConfig.id)) {
|
|
4020
|
+
try {
|
|
4021
|
+
const snapshot = await resourceManager.executeOp(stateConfig.id, "get", {});
|
|
4022
|
+
const state = snapshot ? JSON.parse(snapshot) : {};
|
|
3727
4023
|
sendEvent({
|
|
3728
|
-
type: "
|
|
3729
|
-
|
|
3730
|
-
|
|
4024
|
+
type: "state_changed",
|
|
4025
|
+
store: stateConfig.id,
|
|
4026
|
+
state
|
|
3731
4027
|
});
|
|
3732
|
-
await cleanup();
|
|
3733
|
-
disposeLogStore();
|
|
3734
|
-
return;
|
|
3735
|
-
}
|
|
3736
|
-
if (runtimeIdentity && runtimeIdentity.containerId !== cmd.identity.containerId) {
|
|
3737
4028
|
log(
|
|
3738
|
-
`[serve]
|
|
4029
|
+
`[serve] sharedState ${stateConfig.id} already registered \u2014 re-emitted current state`
|
|
3739
4030
|
);
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
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)
|
|
4031
|
+
} catch {
|
|
4032
|
+
log(
|
|
4033
|
+
`[serve] sharedState ${stateConfig.id} already registered \u2014 skipping (could not read state)`
|
|
3785
4034
|
);
|
|
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
4035
|
}
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
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
|
-
}
|
|
4036
|
+
return false;
|
|
4037
|
+
}
|
|
4038
|
+
const handle = await resourceManager.connect({
|
|
4039
|
+
id: stateConfig.id,
|
|
4040
|
+
driver: stateConfig.adapter,
|
|
4041
|
+
access: "read-write",
|
|
4042
|
+
options: {
|
|
4043
|
+
...stateConfig.definition ?? {},
|
|
4044
|
+
initial: stateConfig.initialState ?? {}
|
|
3881
4045
|
}
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
4046
|
+
});
|
|
4047
|
+
const entry = resourceManager.get(stateConfig.id);
|
|
4048
|
+
if (typeof entry.connector.onStoreChange === "function") {
|
|
4049
|
+
entry.connector.onStoreChange(handle, (snapshot) => {
|
|
4050
|
+
sendEvent({
|
|
4051
|
+
type: "state_changed",
|
|
4052
|
+
store: stateConfig.id,
|
|
4053
|
+
state: snapshot
|
|
3888
4054
|
});
|
|
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
4055
|
});
|
|
4056
|
-
break;
|
|
4057
4056
|
}
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4057
|
+
log(`[serve] sharedState registered: ${stateConfig.id} (${stateConfig.adapter})`);
|
|
4058
|
+
sendEvent({
|
|
4059
|
+
type: "state_changed",
|
|
4060
|
+
store: stateConfig.id,
|
|
4061
|
+
state: stateConfig.initialState ?? {}
|
|
4062
|
+
});
|
|
4063
|
+
return true;
|
|
4064
|
+
} catch (err) {
|
|
4065
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4066
|
+
log(`[serve] sharedState registration failed for ${stateConfig.id}: ${errMsg}`);
|
|
4067
|
+
sendEvent({
|
|
4068
|
+
type: "error",
|
|
4069
|
+
message: `sharedState ${stateConfig.id}: ${errMsg}`,
|
|
4070
|
+
fatal: false
|
|
4071
|
+
});
|
|
4072
|
+
return false;
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
async function rebuildMcpToolsForNewStores(storesRegistered) {
|
|
4076
|
+
if (!(storesRegistered > 0 && !driverStarted && agentSession?.driverType === "claude-sdk")) {
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
try {
|
|
4080
|
+
const server = await buildSdkConnectorTools(resourceManager, { z: z });
|
|
4081
|
+
if (server) {
|
|
4082
|
+
driver.config.mcpServers = { "skaile-connectors": server };
|
|
4083
|
+
log(`[serve] rebuilt MCP connector tools (${storesRegistered} new store(s))`);
|
|
4084
|
+
} else {
|
|
4085
|
+
log(`[serve] buildSdkConnectorTools returned null`);
|
|
4086
|
+
}
|
|
4087
|
+
} catch (err) {
|
|
4088
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4089
|
+
log(`[serve] failed to rebuild MCP tools: ${errMsg}`);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
async function applyInitAiResources(aiResources) {
|
|
4093
|
+
const catalogActions = aiResources.filter((a) => a.action);
|
|
4094
|
+
if (catalogActions.length === 0) return;
|
|
4095
|
+
log(`[serve] aiResources catalog actions: ${catalogActions.length}`);
|
|
4096
|
+
try {
|
|
4097
|
+
const skillsDir = join(sessionConfig.projectDir ?? process.cwd(), ".claude", "skills");
|
|
4098
|
+
const assetsRoot = process.env.SKAILE_ASSETS_ROOT ?? findAiAssetsRoot(sessionConfig.projectDir ?? process.cwd()) ?? "/app/ai-assets";
|
|
4099
|
+
for (const res of catalogActions) {
|
|
4100
|
+
applyCatalogAction(res, { assetsRoot, skillsDir });
|
|
4101
|
+
}
|
|
4102
|
+
await restartDriverForSkillChanges(catalogActions);
|
|
4103
|
+
} catch (err) {
|
|
4104
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4105
|
+
log(`[serve] aiResources handling failed: ${errMsg}`);
|
|
4106
|
+
sendEvent({
|
|
4107
|
+
type: "error",
|
|
4108
|
+
message: `aiResources: ${errMsg}`,
|
|
4109
|
+
fatal: false
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
function applyCatalogAction(res, paths) {
|
|
4114
|
+
if (res.action === "add" && res.entry) {
|
|
4115
|
+
const entry = res.entry;
|
|
4116
|
+
const result = deployCatalogEntry(entry, {
|
|
4117
|
+
assetsRoot: paths.assetsRoot,
|
|
4118
|
+
skillsDir: paths.skillsDir,
|
|
4119
|
+
context: res.context
|
|
4120
|
+
});
|
|
4121
|
+
if (result.success) {
|
|
4122
|
+
log(`[serve] deployed skill: ${entry.name ?? res.name}`);
|
|
4123
|
+
} else {
|
|
4124
|
+
log(
|
|
4125
|
+
`[serve] failed to deploy skill ${entry.name ?? res.name}: ${result.error}`
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
4128
|
+
} else if (res.action === "remove") {
|
|
4129
|
+
const removed = undeployCatalogEntry(res.name, paths.skillsDir);
|
|
4130
|
+
log(`[serve] ${removed ? "removed" : "not found"} skill: ${res.name}`);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
async function restartDriverForSkillChanges(catalogActions) {
|
|
4134
|
+
if (!driverStarted) {
|
|
4135
|
+
log("[serve] aiResources applied before driver start \u2014 skills discovered on first prompt");
|
|
4136
|
+
return;
|
|
4137
|
+
}
|
|
4138
|
+
const prevSessionId = driver.runtimeSessionId;
|
|
4139
|
+
log(`[serve] restarting driver to pick up skill changes (resume=${prevSessionId ?? "none"})`);
|
|
4140
|
+
await agentSession.dispose();
|
|
4141
|
+
agentSession = await createAgentSession({
|
|
4142
|
+
...sessionConfig,
|
|
4143
|
+
resumeSessionId: prevSessionId
|
|
4144
|
+
});
|
|
4145
|
+
emitSystemPromptComposed(agentSession, sendEvent);
|
|
4146
|
+
driver = agentSession.driver;
|
|
4147
|
+
normalizer.reset();
|
|
4148
|
+
driverStarted = false;
|
|
4149
|
+
rebuildCompactionOrchestrator();
|
|
4150
|
+
sendEvent({
|
|
4151
|
+
type: "skills_changed",
|
|
4152
|
+
skills: catalogActions.map((a) => ({
|
|
4153
|
+
name: a.entry?.name ?? a.name,
|
|
4154
|
+
action: a.action ?? "add"
|
|
4155
|
+
}))
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
async function handleSessionInit(cmd) {
|
|
4159
|
+
if (cmd.protocolVersion.major !== PROTOCOL_VERSION.major) {
|
|
4160
|
+
log(
|
|
4161
|
+
`[serve] incompatible protocol: agent=${PROTOCOL_VERSION.major} platform=${cmd.protocolVersion.major}`
|
|
4162
|
+
);
|
|
4163
|
+
sendEvent({
|
|
4164
|
+
type: "incompatible_protocol",
|
|
4165
|
+
agentVersion: PROTOCOL_VERSION,
|
|
4166
|
+
platformVersion: cmd.protocolVersion
|
|
4167
|
+
});
|
|
4168
|
+
await cleanup();
|
|
4169
|
+
disposeLogStore();
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
if (runtimeIdentity && runtimeIdentity.containerId !== cmd.identity.containerId) {
|
|
4173
|
+
log(
|
|
4174
|
+
`[serve] session_init_mismatch: expected=${runtimeIdentity.containerId} received=${cmd.identity.containerId}`
|
|
4175
|
+
);
|
|
4176
|
+
sendEvent({
|
|
4177
|
+
type: "session_init_mismatch",
|
|
4178
|
+
expectedContainerId: runtimeIdentity.containerId,
|
|
4179
|
+
receivedContainerId: cmd.identity.containerId
|
|
4180
|
+
});
|
|
4181
|
+
await cleanup();
|
|
4182
|
+
disposeLogStore();
|
|
4183
|
+
return;
|
|
4184
|
+
}
|
|
4185
|
+
runtimeIdentity = cmd.identity;
|
|
4186
|
+
registerInitClientCapabilities(cmd.capabilities ?? []);
|
|
4187
|
+
sendEvent({ type: "protocol_info", version: PROTOCOL_VERSION });
|
|
4188
|
+
preMintedSecrets = new PreMintedSecretProvider(cmd.credentials);
|
|
4189
|
+
const initialAiCredentialExpiresAt = await provisionInitSecrets(cmd.secrets);
|
|
4190
|
+
if (cmd.resolvedConfig.aiProviderConfigId) {
|
|
4191
|
+
aiProviderConfigId = cmd.resolvedConfig.aiProviderConfigId;
|
|
4192
|
+
log(`[serve] stashed aiProviderConfigId=${aiProviderConfigId}`);
|
|
4193
|
+
}
|
|
4194
|
+
if (aiProviderConfigId && initialAiCredentialExpiresAt) {
|
|
4195
|
+
aiCredentialRefreshScheduler.schedule(initialAiCredentialExpiresAt);
|
|
4196
|
+
}
|
|
4197
|
+
sendEvent({
|
|
4198
|
+
type: "session_init_ack",
|
|
4199
|
+
capabilitySignature: computeCapabilitySignature(capabilityRegistry.list())
|
|
4200
|
+
});
|
|
4201
|
+
const buildSession = async () => {
|
|
4202
|
+
await buildSessionFromInit(cmd.secrets);
|
|
4203
|
+
const cfg = {
|
|
4204
|
+
aiResources: cmd.resolvedConfig.aiResources,
|
|
4205
|
+
subagents: cmd.resolvedConfig.subagents,
|
|
4206
|
+
sharedState: cmd.sharedState,
|
|
4207
|
+
resumeSessionId: cmd.resume?.sessionId,
|
|
4208
|
+
expectedCapabilitySignature: cmd.resume?.expectedCapabilitySignature,
|
|
4209
|
+
aiProviderConfigId: cmd.resolvedConfig.aiProviderConfigId
|
|
4210
|
+
};
|
|
4211
|
+
log(
|
|
4212
|
+
`[serve] configure cfg keys=${Object.keys(cfg).join(",")} sharedState=${cfg.sharedState ? cfg.sharedState.length : "undefined"} resourceManager=${resourceManager ? "set" : "null"}`
|
|
4213
|
+
);
|
|
4214
|
+
let pendingResumeSessionId;
|
|
4215
|
+
const cfgResumeSessionId = cfg.resumeSessionId;
|
|
4216
|
+
const cfgExpectedSig = cfg.expectedCapabilitySignature;
|
|
4217
|
+
if (cfgResumeSessionId) {
|
|
4218
|
+
const currentSig = computeCapabilitySignature(capabilityRegistry.list());
|
|
4219
|
+
if (cfgExpectedSig && cfgExpectedSig !== currentSig) {
|
|
4220
|
+
log(
|
|
4221
|
+
`[serve] tier-1 resume rejected: signature mismatch (expected=${cfgExpectedSig.slice(0, 12)} current=${currentSig.slice(0, 12)})`
|
|
4222
|
+
);
|
|
4062
4223
|
sendEvent({
|
|
4063
|
-
type: "
|
|
4064
|
-
|
|
4065
|
-
|
|
4224
|
+
type: "resume_failed",
|
|
4225
|
+
resumeSessionId: cfgResumeSessionId,
|
|
4226
|
+
reason: "signature_mismatch"
|
|
4066
4227
|
});
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
if (!secretsProvisioned) {
|
|
4070
|
-
log("[serve] prompt received before secrets provisioned \u2014 rejecting");
|
|
4228
|
+
} else {
|
|
4229
|
+
log(`[serve] tier-1 resume attempted: resumeSessionId=${cfgResumeSessionId}`);
|
|
4071
4230
|
sendEvent({
|
|
4072
|
-
type: "
|
|
4073
|
-
|
|
4074
|
-
|
|
4231
|
+
type: "resume_attempted",
|
|
4232
|
+
resumeSessionId: cfgResumeSessionId,
|
|
4233
|
+
capabilitySignature: currentSig
|
|
4075
4234
|
});
|
|
4076
|
-
|
|
4235
|
+
pendingResumeSessionId = cfgResumeSessionId;
|
|
4077
4236
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4237
|
+
}
|
|
4238
|
+
if (pendingResumeSessionId || cfg.subagents) {
|
|
4239
|
+
await recreateSessionForInit(pendingResumeSessionId);
|
|
4240
|
+
}
|
|
4241
|
+
if (cfg.sharedState) {
|
|
4242
|
+
const handled = await applyInitSharedState(cfg.sharedState);
|
|
4243
|
+
if (!handled) throw new Error("session_init: shared-state setup failed");
|
|
4244
|
+
}
|
|
4245
|
+
if (cfg.aiResources) {
|
|
4246
|
+
await applyInitAiResources(cfg.aiResources);
|
|
4247
|
+
}
|
|
4248
|
+
resolveSessionReady();
|
|
4249
|
+
};
|
|
4250
|
+
void buildSession().catch((err) => {
|
|
4251
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4252
|
+
log(`[serve] session_init failed to build session: ${errMsg}`);
|
|
4253
|
+
sendEvent({ type: "error", message: errMsg, fatal: true });
|
|
4254
|
+
rejectSessionReady(err instanceof Error ? err : new Error(errMsg));
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
async function handlePrompt(cmd) {
|
|
4258
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4259
|
+
if (!secretsProvisioned) {
|
|
4260
|
+
log("[serve] prompt received before secrets provisioned \u2014 rejecting");
|
|
4261
|
+
sendEvent({
|
|
4262
|
+
type: "error",
|
|
4263
|
+
message: "Secrets not yet provisioned \u2014 send provision_secrets first",
|
|
4264
|
+
fatal: false
|
|
4265
|
+
});
|
|
4266
|
+
return;
|
|
4267
|
+
}
|
|
4268
|
+
log(`[serve] prompt: ${cmd.prompt.slice(0, 80)}...`);
|
|
4269
|
+
const activeFlow = getOnlyActiveFlow();
|
|
4270
|
+
if (activeFlow) {
|
|
4271
|
+
sendEvent({ type: "status", phase: "thinking" });
|
|
4272
|
+
try {
|
|
4273
|
+
const bus = getOrCreateStimulusBus();
|
|
4274
|
+
const userStimulus = {
|
|
4275
|
+
kind: "user_message",
|
|
4276
|
+
text: cmd.prompt,
|
|
4277
|
+
senderId: "user"
|
|
4278
|
+
};
|
|
4279
|
+
await bus.signal(activeFlow.connectorId, {
|
|
4280
|
+
promptFragment: renderStimulusPrompt(userStimulus),
|
|
4281
|
+
meta: {
|
|
4282
|
+
kind: "user_message",
|
|
4283
|
+
runId: activeFlow.runId,
|
|
4284
|
+
text: cmd.prompt,
|
|
4285
|
+
senderId: "user"
|
|
4102
4286
|
}
|
|
4103
|
-
|
|
4287
|
+
});
|
|
4288
|
+
} catch (err) {
|
|
4289
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4290
|
+
log(`[serve] flow user-message turn failed: ${errMsg}`);
|
|
4291
|
+
sendEvent({ type: "error", message: `Flow turn failed: ${errMsg}`, fatal: false });
|
|
4292
|
+
}
|
|
4293
|
+
return;
|
|
4294
|
+
}
|
|
4295
|
+
if (!driverStarted) {
|
|
4296
|
+
try {
|
|
4297
|
+
await driver.start();
|
|
4298
|
+
driverStarted = true;
|
|
4299
|
+
} catch (err) {
|
|
4300
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4301
|
+
log(`[serve] driver start failed: ${errMsg}`);
|
|
4302
|
+
sendEvent({ type: "error", message: `Driver start failed: ${errMsg}`, fatal: true });
|
|
4303
|
+
return;
|
|
4304
|
+
}
|
|
4305
|
+
driver.on("agent-event", (event) => {
|
|
4306
|
+
const normalized = normalizer.normalize(event);
|
|
4307
|
+
for (const e of normalized) {
|
|
4308
|
+
forwardNormalizedEvent(e);
|
|
4104
4309
|
}
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4310
|
+
});
|
|
4311
|
+
driver.on("error", (err) => {
|
|
4312
|
+
log(`[serve] driver error: ${err.message}`);
|
|
4313
|
+
sendEvent({ type: "error", message: err.message, fatal: true });
|
|
4314
|
+
});
|
|
4315
|
+
}
|
|
4316
|
+
sendEvent({ type: "status", phase: "thinking" });
|
|
4317
|
+
try {
|
|
4318
|
+
await driver.prompt(cmd.prompt);
|
|
4319
|
+
} catch (err) {
|
|
4320
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4321
|
+
log(`[serve] prompt failed: ${errMsg}`);
|
|
4322
|
+
sendEvent({ type: "error", message: `Prompt failed: ${errMsg}`, fatal: true });
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
async function handleReply(cmd) {
|
|
4326
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4327
|
+
log(`[serve] reply: ${cmd.answer.slice(0, 80)}...`);
|
|
4328
|
+
try {
|
|
4329
|
+
await driver.prompt(cmd.answer);
|
|
4330
|
+
} catch (err) {
|
|
4331
|
+
sendEvent({
|
|
4332
|
+
type: "error",
|
|
4333
|
+
message: `Reply failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4334
|
+
fatal: false
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
async function handleCancel() {
|
|
4339
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4340
|
+
log("[serve] cancel");
|
|
4341
|
+
try {
|
|
4342
|
+
await driver.abort();
|
|
4343
|
+
} catch (err) {
|
|
4344
|
+
log(`[serve] cancel failed: ${err}`);
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
async function handleResourceRequestCommand(cmd) {
|
|
4348
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4349
|
+
if (resourceManager?.has(cmd.connectorId)) {
|
|
4350
|
+
handleMountResourceRequest(cmd, resourceManager, sendEvent);
|
|
4351
|
+
return;
|
|
4352
|
+
}
|
|
4353
|
+
if (!resourceManager) {
|
|
4354
|
+
sendEvent({
|
|
4355
|
+
type: "resource_response",
|
|
4356
|
+
requestId: cmd.requestId,
|
|
4357
|
+
resourceId: cmd.connectorId,
|
|
4358
|
+
operation: cmd.operation,
|
|
4359
|
+
error: "No resources available \u2014 no mounts or connectors configured"
|
|
4360
|
+
});
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
4363
|
+
void handleResourceRequest(cmd, resourceManager, sendEvent);
|
|
4364
|
+
}
|
|
4365
|
+
async function handleConnectorMutate(cmd) {
|
|
4366
|
+
try {
|
|
4367
|
+
await sessionReady;
|
|
4368
|
+
} catch (err) {
|
|
4369
|
+
sendEvent({
|
|
4370
|
+
type: "error",
|
|
4371
|
+
message: `Session not available: ${err.message}`,
|
|
4372
|
+
fatal: true
|
|
4373
|
+
});
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
4376
|
+
try {
|
|
4377
|
+
if (!resourceManager) throw new Error("connector_mutate before resources ready");
|
|
4378
|
+
const targetEntry = resourceManager.has?.(cmd.id) ? resourceManager.get(cmd.id) : null;
|
|
4379
|
+
const isExistingFlow = targetEntry?.connector?.name === "flow";
|
|
4380
|
+
const isBareFlowId = cmd.id === "flow";
|
|
4381
|
+
if (cmd.op === "start") {
|
|
4382
|
+
if (isExistingFlow || isBareFlowId) {
|
|
4383
|
+
const payload = cmd.payload ?? {};
|
|
4384
|
+
const flowDef = payload.flow;
|
|
4385
|
+
const seed = payload.seed;
|
|
4386
|
+
if (flowDef?.id && seed?.runId && seed?.startedBy) {
|
|
4387
|
+
if (activeFlows.size > 0) {
|
|
4388
|
+
serverLog.warn("connector_mutate flow.start: another flow already active", {
|
|
4389
|
+
activeRunIds: Array.from(activeFlows.keys())
|
|
4390
|
+
});
|
|
4391
|
+
return;
|
|
4119
4392
|
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
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 });
|
|
4393
|
+
const entry = await createActiveFlow(flowDef, { seed });
|
|
4394
|
+
serverLog.info("connector_mutate flow.start", {
|
|
4395
|
+
runId: entry.runId,
|
|
4396
|
+
flowId: flowDef.id
|
|
4397
|
+
});
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4133
4400
|
}
|
|
4134
|
-
break;
|
|
4135
4401
|
}
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
type: "error",
|
|
4142
|
-
message: `Session not available: ${err.message}`,
|
|
4143
|
-
fatal: true
|
|
4144
|
-
});
|
|
4145
|
-
break;
|
|
4402
|
+
let dispatchId = cmd.id;
|
|
4403
|
+
if (!targetEntry && isBareFlowId) {
|
|
4404
|
+
const onlyActive = getOnlyActiveFlow();
|
|
4405
|
+
if (onlyActive) {
|
|
4406
|
+
dispatchId = onlyActive.connectorId;
|
|
4146
4407
|
}
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4408
|
+
}
|
|
4409
|
+
await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4410
|
+
} catch (err) {
|
|
4411
|
+
serverLog.warn("connector_mutate failed", {
|
|
4412
|
+
id: cmd.id,
|
|
4413
|
+
op: cmd.op,
|
|
4414
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4415
|
+
});
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
async function handleConnectorQuery(cmd) {
|
|
4419
|
+
try {
|
|
4420
|
+
await sessionReady;
|
|
4421
|
+
} catch (err) {
|
|
4422
|
+
sendEvent({
|
|
4423
|
+
type: "error",
|
|
4424
|
+
message: `Session not available: ${err.message}`,
|
|
4425
|
+
fatal: true
|
|
4426
|
+
});
|
|
4427
|
+
return;
|
|
4428
|
+
}
|
|
4429
|
+
let ok = true;
|
|
4430
|
+
let result;
|
|
4431
|
+
let error;
|
|
4432
|
+
try {
|
|
4433
|
+
if (!resourceManager) throw new Error("connector_query before resources ready");
|
|
4434
|
+
let dispatchId = cmd.id;
|
|
4435
|
+
if (!resourceManager.has?.(cmd.id) && cmd.id === "flow") {
|
|
4436
|
+
const onlyActive = getOnlyActiveFlow();
|
|
4437
|
+
if (onlyActive) {
|
|
4438
|
+
dispatchId = onlyActive.connectorId;
|
|
4156
4439
|
}
|
|
4157
|
-
break;
|
|
4158
4440
|
}
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4441
|
+
result = await resourceManager.executeOp(dispatchId, cmd.op, cmd.payload ?? {});
|
|
4442
|
+
} catch (err) {
|
|
4443
|
+
ok = false;
|
|
4444
|
+
error = err instanceof Error ? err.message : String(err);
|
|
4445
|
+
}
|
|
4446
|
+
sendEvent({
|
|
4447
|
+
type: "connector_query_response",
|
|
4448
|
+
requestId: cmd.requestId,
|
|
4449
|
+
ok,
|
|
4450
|
+
...result !== void 0 ? { result } : {},
|
|
4451
|
+
...error !== void 0 ? { error } : {}
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
async function computeDebugResult(cmd) {
|
|
4455
|
+
switch (cmd.query) {
|
|
4456
|
+
case "tools": {
|
|
4457
|
+
const mcpServers = Object.keys(driver?.config?.mcpServers ?? {});
|
|
4458
|
+
const allowedTools = driver?.config?.allowedTools ?? null;
|
|
4459
|
+
return { mcpServers, allowedTools, driverStarted };
|
|
4460
|
+
}
|
|
4461
|
+
case "connectors": {
|
|
4462
|
+
return resourceManager?.listConnectors?.() ?? [];
|
|
4463
|
+
}
|
|
4464
|
+
case "mcp": {
|
|
4465
|
+
const servers = Object.entries(driver?.config?.mcpServers ?? {}).map(([name]) => ({
|
|
4466
|
+
name
|
|
4467
|
+
}));
|
|
4468
|
+
return { servers };
|
|
4469
|
+
}
|
|
4470
|
+
case "config": {
|
|
4471
|
+
const liveModel = driver?.getModel() ?? agentSession?.model;
|
|
4472
|
+
return {
|
|
4473
|
+
driver: agentSession?.driverType,
|
|
4474
|
+
model: liveModel,
|
|
4475
|
+
provider: agentSession?.provider,
|
|
4476
|
+
maxTurns,
|
|
4477
|
+
secretsMode,
|
|
4478
|
+
driverStarted,
|
|
4479
|
+
projectDir: opts.projectDir,
|
|
4480
|
+
versions: getFrameworkVersions()
|
|
4481
|
+
};
|
|
4482
|
+
}
|
|
4483
|
+
case "state": {
|
|
4484
|
+
const storeId = cmd.args?.store;
|
|
4485
|
+
if (storeId) {
|
|
4486
|
+
try {
|
|
4487
|
+
const snapshot = await resourceManager?.executeOp(storeId, "get", {});
|
|
4488
|
+
return snapshot ? JSON.parse(snapshot) : null;
|
|
4489
|
+
} catch (err) {
|
|
4490
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
4491
|
+
}
|
|
4169
4492
|
}
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4493
|
+
const all = resourceManager?.listConnectors?.() ?? [];
|
|
4494
|
+
return { stores: all.map((c) => c.id) };
|
|
4495
|
+
}
|
|
4496
|
+
case "commands": {
|
|
4497
|
+
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4498
|
+
return { commands, driverType: agentSession?.driverType };
|
|
4499
|
+
}
|
|
4500
|
+
default:
|
|
4501
|
+
return { error: `Unknown debug query: ${cmd.query}` };
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
async function handleDebug(cmd) {
|
|
4505
|
+
if (!await awaitSessionReadyOrFail()) return;
|
|
4506
|
+
log(`[serve] debug query=${cmd.query}`);
|
|
4507
|
+
let result;
|
|
4508
|
+
try {
|
|
4509
|
+
result = await computeDebugResult(cmd);
|
|
4510
|
+
} catch (err) {
|
|
4511
|
+
result = { error: err instanceof Error ? err.message : String(err) };
|
|
4512
|
+
}
|
|
4513
|
+
sendEvent({ type: "debug_result", query: cmd.query, data: result });
|
|
4514
|
+
}
|
|
4515
|
+
async function handleShutdown() {
|
|
4516
|
+
log("[serve] shutdown requested");
|
|
4517
|
+
await cleanup();
|
|
4518
|
+
if (transport instanceof WebSocketServerTransport) {
|
|
4519
|
+
await transport.close();
|
|
4520
|
+
}
|
|
4521
|
+
disposeLogStore();
|
|
4522
|
+
process.exit(0);
|
|
4523
|
+
}
|
|
4524
|
+
function handleCapabilityResult(cmd) {
|
|
4525
|
+
const matched = resolveCapabilityResult(pendingCapabilityCalls, cmd);
|
|
4526
|
+
if (!matched) {
|
|
4527
|
+
log(
|
|
4528
|
+
`[serve] capability_result for unknown callId=${cmd.callId} (timed out or already resolved); dropping`
|
|
4529
|
+
);
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
function handleCapabilityRegister(cmd) {
|
|
4533
|
+
for (const raw of cmd.capabilities) {
|
|
4534
|
+
const wire = raw;
|
|
4535
|
+
capabilityRegistry.register(
|
|
4536
|
+
{
|
|
4537
|
+
...wire,
|
|
4538
|
+
inputZod: { parse: (v) => v },
|
|
4539
|
+
outputZod: void 0,
|
|
4540
|
+
handler: buildClientCapabilityHandler2(wire)
|
|
4541
|
+
},
|
|
4542
|
+
"client"
|
|
4543
|
+
);
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
function handleCapabilityDeregister(cmd) {
|
|
4547
|
+
for (const name of cmd.names) {
|
|
4548
|
+
capabilityRegistry.deregister(name);
|
|
4549
|
+
}
|
|
4550
|
+
}
|
|
4551
|
+
function handleCapabilityApprove(cmd) {
|
|
4552
|
+
if (cmd.decision === "rejected") {
|
|
4553
|
+
const matched = rejectCapabilityOnApprovalDeny(
|
|
4554
|
+
pendingCapabilityCalls,
|
|
4555
|
+
cmd.callId,
|
|
4556
|
+
cmd.feedback
|
|
4557
|
+
);
|
|
4558
|
+
if (!matched) {
|
|
4559
|
+
log(
|
|
4560
|
+
`[serve] capability_approve(rejected) for unknown callId=${cmd.callId} (already resolved or timed out); dropping`
|
|
4561
|
+
);
|
|
4562
|
+
} else {
|
|
4563
|
+
log(
|
|
4564
|
+
`[serve] capability_approve(rejected) callId=${cmd.callId} decidedBy=${cmd.decidedBy}${cmd.feedback ? ` feedback=${JSON.stringify(cmd.feedback)}` : ""}`
|
|
4565
|
+
);
|
|
4566
|
+
}
|
|
4567
|
+
} else {
|
|
4568
|
+
log(
|
|
4569
|
+
`[serve] capability_approve(approved) callId=${cmd.callId} decidedBy=${cmd.decidedBy} (awaiting capability_result)`
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
async function handleCommand(cmd) {
|
|
4574
|
+
const inbound = cmd;
|
|
4575
|
+
if (inbound.type === "capability_invoked") {
|
|
4576
|
+
await dispatchRunnerCapabilityInvocation(
|
|
4577
|
+
{ callId: inbound.callId ?? "", name: inbound.name ?? "", input: inbound.input },
|
|
4578
|
+
{
|
|
4579
|
+
invoke: (name, input) => capabilityRegistry.invoke(name, input, { sessionId }),
|
|
4580
|
+
emit: (event) => sendEvent(event)
|
|
4175
4581
|
}
|
|
4582
|
+
);
|
|
4583
|
+
return;
|
|
4584
|
+
}
|
|
4585
|
+
switch (cmd.type) {
|
|
4586
|
+
case "session_init":
|
|
4587
|
+
await handleSessionInit(cmd);
|
|
4588
|
+
break;
|
|
4589
|
+
case "prompt":
|
|
4590
|
+
await handlePrompt(cmd);
|
|
4591
|
+
break;
|
|
4592
|
+
case "reply":
|
|
4593
|
+
await handleReply(cmd);
|
|
4594
|
+
break;
|
|
4595
|
+
case "cancel":
|
|
4596
|
+
await handleCancel();
|
|
4176
4597
|
break;
|
|
4177
|
-
}
|
|
4178
4598
|
// v3: compact + lifecycle removed — reachable via runner.lifecycle capability
|
|
4179
4599
|
// (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);
|
|
4600
|
+
case "resource_request":
|
|
4601
|
+
await handleResourceRequestCommand(cmd);
|
|
4206
4602
|
break;
|
|
4207
|
-
}
|
|
4208
4603
|
// v3: state_action removed — reachable via the runner.set_state capability.
|
|
4209
4604
|
// ── Generic connector dispatch (Protocol 2.2) ──────────────────────
|
|
4210
4605
|
// The 6 typed flow commands (`start_flow`, `approve_flow_node`,
|
|
@@ -4213,152 +4608,15 @@ async function startAgentServer(opts) {
|
|
|
4213
4608
|
// extraction. Hosts now drive flow execution exclusively through
|
|
4214
4609
|
// `connector_mutate` against the `flow` connector. See
|
|
4215
4610
|
// `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
|
-
}
|
|
4611
|
+
case "connector_mutate":
|
|
4612
|
+
await handleConnectorMutate(cmd);
|
|
4258
4613
|
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
|
-
});
|
|
4614
|
+
case "connector_query":
|
|
4615
|
+
await handleConnectorQuery(cmd);
|
|
4285
4616
|
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 });
|
|
4617
|
+
case "debug":
|
|
4618
|
+
await handleDebug(cmd);
|
|
4360
4619
|
break;
|
|
4361
|
-
}
|
|
4362
4620
|
// -----------------------------------------------------------------------
|
|
4363
4621
|
// -----------------------------------------------------------------------
|
|
4364
4622
|
// v3: add_resource, remove_resource, remount_mount, set_log_level,
|
|
@@ -4367,75 +4625,27 @@ async function startAgentServer(opts) {
|
|
|
4367
4625
|
// capability invocations (audience: ['runtime']) — the platform calls
|
|
4368
4626
|
// them through invokeRunnerCapability on the gateway. Handler bodies
|
|
4369
4627
|
// 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);
|
|
4628
|
+
case "shutdown":
|
|
4629
|
+
await handleShutdown();
|
|
4378
4630
|
break;
|
|
4379
|
-
}
|
|
4380
4631
|
// 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
|
-
}
|
|
4632
|
+
case "capability_result":
|
|
4633
|
+
handleCapabilityResult(cmd);
|
|
4388
4634
|
break;
|
|
4389
|
-
}
|
|
4390
4635
|
// Protocol v2 — additional capability lifecycle commands. The platform
|
|
4391
4636
|
// can register / deregister capabilities mid-session (e.g. when a user
|
|
4392
4637
|
// installs an extension after `configure`). Approval routing is reserved
|
|
4393
4638
|
// for a later phase; we accept the command shape for forward
|
|
4394
4639
|
// 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
|
-
}
|
|
4640
|
+
case "capability_register":
|
|
4641
|
+
handleCapabilityRegister(cmd);
|
|
4408
4642
|
break;
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
for (const name of cmd.names) {
|
|
4412
|
-
capabilityRegistry.deregister(name);
|
|
4413
|
-
}
|
|
4643
|
+
case "capability_deregister":
|
|
4644
|
+
handleCapabilityDeregister(cmd);
|
|
4414
4645
|
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
|
-
}
|
|
4646
|
+
case "capability_approve":
|
|
4647
|
+
handleCapabilityApprove(cmd);
|
|
4437
4648
|
break;
|
|
4438
|
-
}
|
|
4439
4649
|
}
|
|
4440
4650
|
}
|
|
4441
4651
|
transport.onCommand((cmd) => {
|
|
@@ -4447,21 +4657,22 @@ async function startAgentServer(opts) {
|
|
|
4447
4657
|
if (secretsMode === "env" && agentSession) {
|
|
4448
4658
|
agentSession.startWatching();
|
|
4449
4659
|
}
|
|
4660
|
+
function emitAvailabilityOnConnect() {
|
|
4661
|
+
if (resourceManager) {
|
|
4662
|
+
const wire = splitConnectorsForWire(resourceManager);
|
|
4663
|
+
log(
|
|
4664
|
+
`[serve] emitting resources_available: ${wire.mounts.length} mount(s), ${wire.connectors.length} connector(s)`
|
|
4665
|
+
);
|
|
4666
|
+
sendEvent({ type: "resources_available", ...wire });
|
|
4667
|
+
}
|
|
4668
|
+
const commands = driver?.getSlashCommands?.() ?? [];
|
|
4669
|
+
if (commands.length > 0) {
|
|
4670
|
+
log(`[serve] emitting commands_available: ${commands.length} command(s)`);
|
|
4671
|
+
sendEvent({ type: "commands_available", commands });
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4450
4674
|
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
|
-
});
|
|
4675
|
+
transport.onConnect(emitAvailabilityOnConnect);
|
|
4465
4676
|
}
|
|
4466
4677
|
if (secretsMode === "env") {
|
|
4467
4678
|
log(
|
|
@@ -4471,53 +4682,56 @@ async function startAgentServer(opts) {
|
|
|
4471
4682
|
log(`[serve] secrets=provisioned \u2014 session will initialize after provision_secrets`);
|
|
4472
4683
|
}
|
|
4473
4684
|
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();
|
|
4685
|
+
installProcessHandlers();
|
|
4686
|
+
function installProcessHandlers() {
|
|
4687
|
+
process.on("uncaughtException", (err) => {
|
|
4688
|
+
log(
|
|
4689
|
+
`[serve] uncaughtException (non-fatal): ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
4690
|
+
);
|
|
4691
|
+
});
|
|
4692
|
+
process.on("unhandledRejection", (reason) => {
|
|
4693
|
+
log(
|
|
4694
|
+
`[serve] unhandledRejection (non-fatal): ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}`
|
|
4695
|
+
);
|
|
4696
|
+
});
|
|
4697
|
+
const GRACEFUL_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
4698
|
+
let shuttingDown = false;
|
|
4699
|
+
async function initiateShutdown(signal) {
|
|
4700
|
+
if (shuttingDown) {
|
|
4701
|
+
log(`[serve] ${signal} received again \u2014 forcing exit`);
|
|
4702
|
+
process.exit(130);
|
|
4506
4703
|
}
|
|
4507
|
-
|
|
4508
|
-
log(`[serve]
|
|
4704
|
+
shuttingDown = true;
|
|
4705
|
+
log(`[serve] ${signal} received \u2014 shutting down (press Ctrl-C again to force)`);
|
|
4706
|
+
const forceExitTimer = setTimeout(() => {
|
|
4707
|
+
log(`[serve] graceful shutdown exceeded ${GRACEFUL_SHUTDOWN_TIMEOUT_MS}ms \u2014 forcing exit`);
|
|
4708
|
+
process.exit(130);
|
|
4709
|
+
}, GRACEFUL_SHUTDOWN_TIMEOUT_MS);
|
|
4710
|
+
forceExitTimer.unref?.();
|
|
4711
|
+
try {
|
|
4712
|
+
await cleanup();
|
|
4713
|
+
} catch (err) {
|
|
4714
|
+
log(`[serve] cleanup error: ${err.message}`);
|
|
4715
|
+
}
|
|
4716
|
+
try {
|
|
4717
|
+
if (transport instanceof WebSocketServerTransport) {
|
|
4718
|
+
await transport.close();
|
|
4719
|
+
}
|
|
4720
|
+
} catch (err) {
|
|
4721
|
+
log(`[serve] transport close error: ${err.message}`);
|
|
4722
|
+
}
|
|
4723
|
+
clearTimeout(forceExitTimer);
|
|
4724
|
+
log("[serve] shutdown complete");
|
|
4725
|
+
disposeLogStore();
|
|
4726
|
+
process.exit(0);
|
|
4509
4727
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
process.
|
|
4728
|
+
process.on("SIGTERM", () => {
|
|
4729
|
+
void initiateShutdown("SIGTERM");
|
|
4730
|
+
});
|
|
4731
|
+
process.on("SIGINT", () => {
|
|
4732
|
+
void initiateShutdown("SIGINT");
|
|
4733
|
+
});
|
|
4514
4734
|
}
|
|
4515
|
-
process.on("SIGTERM", () => {
|
|
4516
|
-
void initiateShutdown("SIGTERM");
|
|
4517
|
-
});
|
|
4518
|
-
process.on("SIGINT", () => {
|
|
4519
|
-
void initiateShutdown("SIGINT");
|
|
4520
|
-
});
|
|
4521
4735
|
}
|
|
4522
4736
|
var SESSION_DIR = ".skaile";
|
|
4523
4737
|
var SESSIONS_SUBDIR = "sessions";
|
|
@@ -4633,5 +4847,5 @@ function touchSession(state) {
|
|
|
4633
4847
|
}
|
|
4634
4848
|
|
|
4635
4849
|
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-
|
|
4850
|
+
//# sourceMappingURL=chunk-IENYMRZ6.js.map
|
|
4851
|
+
//# sourceMappingURL=chunk-IENYMRZ6.js.map
|