@skaile/workspaces 1.3.0 → 1.5.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 +395 -0
- package/README.md +1 -1
- package/dist/app-sdk/index.d.ts +2 -0
- package/dist/app-sdk/index.js +481 -0
- package/dist/app-sdk/index.js.map +1 -0
- package/dist/app-sdk/src/app.d.ts +152 -0
- package/dist/app-sdk/src/app.d.ts.map +1 -0
- package/dist/app-sdk/src/connection-params.d.ts +117 -0
- package/dist/app-sdk/src/connection-params.d.ts.map +1 -0
- package/dist/app-sdk/src/define-app-capability.d.ts +139 -0
- package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -0
- package/dist/app-sdk/src/index.d.ts +17 -0
- package/dist/app-sdk/src/index.d.ts.map +1 -0
- package/dist/app-sdk/src/logger.d.ts +26 -0
- package/dist/app-sdk/src/logger.d.ts.map +1 -0
- package/dist/app-sdk/src/transport.d.ts +93 -0
- package/dist/app-sdk/src/transport.d.ts.map +1 -0
- package/dist/app-sdk/src/ws-transport.d.ts +79 -0
- package/dist/app-sdk/src/ws-transport.d.ts.map +1 -0
- package/dist/{asset-feeds-NEZKG6YS.js → asset-feeds-T35ELX2O.js} +11 -11
- package/dist/{asset-feeds-NEZKG6YS.js.map → asset-feeds-T35ELX2O.js.map} +1 -1
- package/dist/asset-manager/index.js +15 -10
- package/dist/asset-manager/installer.js +9 -9
- package/dist/asset-manager/renderers.js +2 -2
- package/dist/asset-manager/src/index.d.ts +42 -6
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/bridge/drivers/claude-sdk.js +2 -2
- package/dist/bridge/drivers/codex.js +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +1 -1
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +3 -3
- package/dist/{chunk-YOPSHV7V.js → chunk-2DWXGUZA.js} +3 -3
- package/dist/{chunk-YOPSHV7V.js.map → chunk-2DWXGUZA.js.map} +1 -1
- package/dist/{chunk-XBG2YDQQ.js → chunk-2ZMO4CFG.js} +3 -3
- package/dist/{chunk-XBG2YDQQ.js.map → chunk-2ZMO4CFG.js.map} +1 -1
- package/dist/{chunk-OBSSORCE.js → chunk-3CHARYD4.js} +84 -12
- package/dist/chunk-3CHARYD4.js.map +1 -0
- package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
- package/dist/chunk-4N5XR5DQ.js.map +1 -0
- package/dist/{chunk-WFEPI5LA.js → chunk-6NR2BEIY.js} +6 -6
- package/dist/{chunk-WFEPI5LA.js.map → chunk-6NR2BEIY.js.map} +1 -1
- package/dist/{chunk-7CNIRF42.js → chunk-BGIC25VK.js} +26 -15
- package/dist/chunk-BGIC25VK.js.map +1 -0
- package/dist/{chunk-YXEPZ7LP.js → chunk-CNSNN7N6.js} +6 -6
- package/dist/{chunk-YXEPZ7LP.js.map → chunk-CNSNN7N6.js.map} +1 -1
- package/dist/{chunk-PSMW5HLX.js → chunk-DMG6NJH7.js} +5 -5
- package/dist/{chunk-PSMW5HLX.js.map → chunk-DMG6NJH7.js.map} +1 -1
- package/dist/{chunk-P3MJYPZE.js → chunk-FDE7XRQA.js} +57 -9
- package/dist/chunk-FDE7XRQA.js.map +1 -0
- package/dist/{chunk-K5SD7FSF.js → chunk-HKX5VSWQ.js} +3 -3
- package/dist/{chunk-K5SD7FSF.js.map → chunk-HKX5VSWQ.js.map} +1 -1
- package/dist/{chunk-AZAS3FKN.js → chunk-HY33RMEA.js} +49 -3
- package/dist/chunk-HY33RMEA.js.map +1 -0
- package/dist/{chunk-XKARS6EE.js → chunk-IEX5YV4R.js} +3 -3
- package/dist/{chunk-XKARS6EE.js.map → chunk-IEX5YV4R.js.map} +1 -1
- package/dist/{chunk-GB4SEEK4.js → chunk-IRORTHGA.js} +10 -4
- package/dist/chunk-IRORTHGA.js.map +1 -0
- package/dist/chunk-J5TI55E7.js +203 -0
- package/dist/chunk-J5TI55E7.js.map +1 -0
- package/dist/chunk-JIDKX7GC.js +122 -0
- package/dist/chunk-JIDKX7GC.js.map +1 -0
- package/dist/{chunk-BDUANIAR.js → chunk-KEQ4GLVM.js} +3 -3
- package/dist/{chunk-BDUANIAR.js.map → chunk-KEQ4GLVM.js.map} +1 -1
- package/dist/{chunk-5XS3BFJS.js → chunk-KMY7HVQ6.js} +3 -3
- package/dist/{chunk-5XS3BFJS.js.map → chunk-KMY7HVQ6.js.map} +1 -1
- package/dist/{chunk-BZMTR5WW.js → chunk-MZV6WCF3.js} +83 -51
- package/dist/chunk-MZV6WCF3.js.map +1 -0
- package/dist/{chunk-QK5JDAFF.js → chunk-NKTFEV7Q.js} +4 -4
- package/dist/{chunk-QK5JDAFF.js.map → chunk-NKTFEV7Q.js.map} +1 -1
- package/dist/{chunk-DZCK7GRW.js → chunk-P4VHQA4N.js} +22 -6
- package/dist/chunk-P4VHQA4N.js.map +1 -0
- package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
- package/dist/chunk-PK7Q7MC7.js.map +1 -0
- package/dist/{chunk-ETYDLXI5.js → chunk-RDYOU6N6.js} +2 -2
- package/dist/{chunk-ETYDLXI5.js.map → chunk-RDYOU6N6.js.map} +1 -1
- package/dist/{chunk-RHMQPLIK.js → chunk-RSYV7XEN.js} +14 -8
- package/dist/chunk-RSYV7XEN.js.map +1 -0
- package/dist/{chunk-SUOGVHGJ.js → chunk-TNSCHS4I.js} +2 -2
- package/dist/{chunk-SUOGVHGJ.js.map → chunk-TNSCHS4I.js.map} +1 -1
- package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
- package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
- package/dist/{chunk-RJ3HUSBC.js → chunk-UQ5FK2AB.js} +3 -3
- package/dist/{chunk-RJ3HUSBC.js.map → chunk-UQ5FK2AB.js.map} +1 -1
- package/dist/{chunk-OUWNTFDG.js → chunk-UQKYT4V6.js} +38 -6
- package/dist/chunk-UQKYT4V6.js.map +1 -0
- package/dist/chunk-WYGXYGJH.js +688 -0
- package/dist/chunk-WYGXYGJH.js.map +1 -0
- package/dist/{chunk-TJRWGMD2.js → chunk-ZBZQC5R7.js} +3 -3
- package/dist/{chunk-TJRWGMD2.js.map → chunk-ZBZQC5R7.js.map} +1 -1
- package/dist/cli/index.js +1022 -374
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/catalog.d.ts +18 -0
- package/dist/cli/src/commands/catalog.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts +2 -2
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-publish.d.ts +53 -0
- package/dist/cli/src/commands/library-publish.d.ts.map +1 -0
- package/dist/cli/src/commands/preset-apply.d.ts +59 -0
- package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
- package/dist/cli/src/commands/preset-cmd.d.ts +8 -2
- package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/preset-export.d.ts +102 -0
- package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
- package/dist/cli/src/commands/project.d.ts.map +1 -1
- package/dist/cli/src/commands/publish-core.d.ts +75 -0
- package/dist/cli/src/commands/publish-core.d.ts.map +1 -0
- package/dist/cli/src/commands/run.d.ts.map +1 -1
- package/dist/cli/src/commands/source-manifest-publish.d.ts +82 -0
- package/dist/cli/src/commands/source-manifest-publish.d.ts.map +1 -0
- package/dist/cli/src/commands/source-manifest.d.ts +65 -6
- package/dist/cli/src/commands/source-manifest.d.ts.map +1 -1
- package/dist/cli/src/commands/source.d.ts +13 -0
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/store.d.ts.map +1 -1
- package/dist/cli/src/commands/why.d.ts +18 -0
- package/dist/cli/src/commands/why.d.ts.map +1 -1
- package/dist/cli/src/driver-error.d.ts +12 -0
- package/dist/cli/src/driver-error.d.ts.map +1 -0
- package/dist/cli/src/open-library.d.ts +4 -0
- package/dist/cli/src/open-library.d.ts.map +1 -1
- package/dist/cli/src/store-auth.d.ts +42 -0
- package/dist/cli/src/store-auth.d.ts.map +1 -0
- package/dist/cli/src/store-client.d.ts +20 -22
- package/dist/cli/src/store-client.d.ts.map +1 -1
- package/dist/cli/src/store-publisher.d.ts +91 -0
- package/dist/cli/src/store-publisher.d.ts.map +1 -0
- package/dist/connectors/config.js +6 -6
- package/dist/connectors/index.js +8 -8
- package/dist/core/index.js +5 -5
- package/dist/core/publish-manifest.js +1 -1
- package/dist/core/runtime-assets.js +3 -3
- package/dist/core/src/cache-paths.d.ts +17 -0
- package/dist/core/src/cache-paths.d.ts.map +1 -1
- package/dist/core/src/index.d.ts +2 -2
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/publish-manifest.d.ts.map +1 -1
- package/dist/core/src/repo-manager.d.ts +13 -2
- package/dist/core/src/repo-manager.d.ts.map +1 -1
- package/dist/core/src/walker.d.ts +10 -0
- package/dist/core/src/walker.d.ts.map +1 -1
- package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
- package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
- package/dist/core/workspace-config.js +2 -2
- package/dist/deploy/index.js +5 -5
- package/dist/discovery/index.js +3 -3
- package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
- package/dist/discovery/src/discover.d.ts +17 -4
- package/dist/discovery/src/discover.d.ts.map +1 -1
- package/dist/discovery/src/index.d.ts +1 -1
- package/dist/discovery/src/index.d.ts.map +1 -1
- package/dist/discovery/src/reference-lint.d.ts +7 -0
- package/dist/discovery/src/reference-lint.d.ts.map +1 -1
- package/dist/discovery/src/source-config.d.ts +28 -0
- package/dist/discovery/src/source-config.d.ts.map +1 -1
- package/dist/discovery/src/tree-entries.d.ts.map +1 -1
- package/dist/{ensure-sources-4BAJGJRK.js → ensure-sources-QDVFUIUP.js} +18 -13
- package/dist/ensure-sources-QDVFUIUP.js.map +1 -0
- package/dist/factory-assets/connectors/box.js +8 -8
- package/dist/factory-assets/connectors/deploy.js +8 -8
- package/dist/factory-assets/connectors/devserver.js +8 -8
- package/dist/factory-assets/connectors/flow/adapter.js +8 -8
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
- package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
- package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +6 -6
- package/dist/factory-assets/connectors/flow/run-flow.js +9 -9
- package/dist/factory-assets/connectors/flow.js +8 -8
- package/dist/factory-assets/connectors/git.js +8 -8
- package/dist/factory-assets/connectors/gmail.js +8 -8
- package/dist/factory-assets/connectors/googledrive.js +8 -8
- package/dist/factory-assets/connectors/local.js +8 -8
- package/dist/factory-assets/connectors/mattermost.js +8 -8
- package/dist/factory-assets/connectors/memory.js +8 -8
- package/dist/factory-assets/connectors/minio.js +8 -8
- package/dist/factory-assets/connectors/postgres.js +8 -8
- package/dist/factory-assets/connectors/s3.js +8 -8
- package/dist/factory-assets/connectors/sharepoint.js +8 -8
- package/dist/factory-assets/connectors/sqlite.js +8 -8
- package/dist/factory-assets/connectors/static-server.js +8 -8
- package/dist/factory-assets/connectors/tunnel.js +8 -8
- package/dist/factory-assets/connectors/webdav.js +8 -8
- package/dist/factory-assets/connectors/xstate-store.js +8 -8
- package/dist/factory-assets/connectors/xstate.js +8 -8
- package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
- package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
- package/dist/{flows-UFCOGAAM.js → flows-MRVXTYWC.js} +4 -4
- package/dist/{flows-UFCOGAAM.js.map → flows-MRVXTYWC.js.map} +1 -1
- package/dist/helpers-TF7GCBRQ.js +27 -0
- package/dist/{helpers-2OL4FRFB.js.map → helpers-TF7GCBRQ.js.map} +1 -1
- package/dist/library/index.js +9 -9
- package/dist/library/install.js +5 -5
- package/dist/library/src/index.d.ts +2 -2
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/local/local-catalog-source.d.ts +12 -1
- package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
- package/dist/library/src/sync/manifest-writeback.d.ts +36 -0
- package/dist/library/src/sync/manifest-writeback.d.ts.map +1 -1
- package/dist/library-publish-K5IBE44D.js +249 -0
- package/dist/library-publish-K5IBE44D.js.map +1 -0
- package/dist/open-library-4HMIDK3N.js +29 -0
- package/dist/{open-library-RPV2LIKL.js.map → open-library-4HMIDK3N.js.map} +1 -1
- package/dist/{paths-ML33B5SB.js → paths-P3DWFUPS.js} +8 -8
- package/dist/{paths-ML33B5SB.js.map → paths-P3DWFUPS.js.map} +1 -1
- package/dist/{plugin-store-G4HKWCTU.js → plugin-store-S7OMGI7N.js} +7 -7
- package/dist/{plugin-store-G4HKWCTU.js.map → plugin-store-S7OMGI7N.js.map} +1 -1
- package/dist/runner/index.js +14 -14
- package/dist/runner/src/agent.d.ts.map +1 -1
- package/dist/runner/src/capability-registry.d.ts +15 -4
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +15 -10
- package/dist/sdk/bridge.js +3 -3
- package/dist/sdk/core.js +5 -5
- package/dist/sdk/flow.js +6 -6
- package/dist/sdk/index.js +14 -14
- package/dist/sdk/runner.js +14 -14
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-WS6PF6JQ.js → setup-2CCS63L6.js} +10 -10
- package/dist/{setup-WS6PF6JQ.js.map → setup-2CCS63L6.js.map} +1 -1
- package/dist/source-LOAM7IIQ.js +30 -0
- package/dist/{source-22U5NOY4.js.map → source-LOAM7IIQ.js.map} +1 -1
- package/dist/source-manifest-publish-TGVZ6KMC.js +228 -0
- package/dist/source-manifest-publish-TGVZ6KMC.js.map +1 -0
- package/dist/tui/index.js +14 -14
- package/dist/types/index.js +1 -1
- package/dist/types/manifests.js +1 -1
- package/dist/types/src/capabilities.d.ts +8 -2
- package/dist/types/src/capabilities.d.ts.map +1 -1
- package/dist/types/src/logging.d.ts +3 -2
- package/dist/types/src/logging.d.ts.map +1 -1
- package/dist/types/src/manifests/_shared.d.ts +4 -4
- package/dist/types/src/manifests/index.d.ts +2 -2
- package/dist/types/src/manifests/index.d.ts.map +1 -1
- package/dist/types/src/manifests/preset.d.ts +10 -0
- package/dist/types/src/manifests/preset.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +11 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +2 -2
- package/dist/workspace-plugin/adapters/omp.js +3 -3
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +7 -1
- package/dist/chunk-7CNIRF42.js.map +0 -1
- package/dist/chunk-AZAS3FKN.js.map +0 -1
- package/dist/chunk-BZMTR5WW.js.map +0 -1
- package/dist/chunk-DZCK7GRW.js.map +0 -1
- package/dist/chunk-EVZKWY5J.js.map +0 -1
- package/dist/chunk-GB4SEEK4.js.map +0 -1
- package/dist/chunk-JQMIG7D3.js +0 -93
- package/dist/chunk-JQMIG7D3.js.map +0 -1
- package/dist/chunk-OBSSORCE.js.map +0 -1
- package/dist/chunk-OUWNTFDG.js.map +0 -1
- package/dist/chunk-P3MJYPZE.js.map +0 -1
- package/dist/chunk-RHMQPLIK.js.map +0 -1
- package/dist/chunk-RYRJEVQ4.js +0 -377
- package/dist/chunk-RYRJEVQ4.js.map +0 -1
- package/dist/chunk-SKADPHAM.js.map +0 -1
- package/dist/ensure-sources-4BAJGJRK.js.map +0 -1
- package/dist/helpers-2OL4FRFB.js +0 -22
- package/dist/open-library-RPV2LIKL.js +0 -29
- package/dist/source-22U5NOY4.js +0 -30
- package/dist/store-client-YLMBH5MF.js +0 -15
- package/dist/store-client-YLMBH5MF.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../bridge/src/logger.ts","../bridge/src/types.ts","../bridge/src/registry.ts"],"names":[],"mappings":"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","file":"chunk-SUOGVHGJ.js","sourcesContent":["/**\n * Bridge-layer logger factory.\n *\n * Each driver creates its own Logger via `getBridgeLogger(subkind)` at\n * construction time. Falls back to an off-mode Logger (stdout only) when\n * no LogStore is registered — see @skaile/workspaces/core/logging for details.\n */\n\nimport { createLogger } from \"@skaile/workspaces/core/logging\";\nimport type { Logger } from \"@skaile/workspaces/types\";\n\n/**\n * Construct a bridge-scoped Logger for a given driver subkind.\n *\n * Creates a `Logger` with `kind: \"bridge\"` and the specified `subkind`. Falls back to\n * an off-mode Logger (stdout only) when no `LogStore` is registered — see\n * `@skaile/workspaces/core/logging` for details. Each driver calls this once at construction\n * time; never re-construct per call.\n *\n * @param subkind - Driver id, e.g. `\"claude-sdk\"`, `\"omp\"`, `\"echo\"`, `\"codex\"`.\n * @returns A `Logger` scoped to `bridge:<subkind>`.\n * @docLink packages/bridge/api-reference#logger\n */\nexport function getBridgeLogger(subkind: string): Logger {\n return createLogger({ kind: \"bridge\", subkind });\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Span, TelemetryProvider, Trace } from \"@skaile/workspaces/telemetry\";\nimport type {\n Capability,\n CredentialMint,\n RenderInvokedEvent,\n TokenUsage,\n} from \"@skaile/workspaces/types\";\nimport type { ModelEntry } from \"./models.js\";\n\n/**\n * Minimal LLM tool descriptor used by the capability dispatch path. Mirrors\n * the runner's `LLMTool` shape so drivers can consume registry output without\n * importing `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-tool\n */\nexport interface BridgeCapabilityTool {\n /** Capability name; used as the LLM-visible tool name. */\n name: string;\n /** Human/LLM-readable description. */\n description: string;\n /** JSON Schema for the tool's input parameters. */\n parameters: Record<string, unknown>;\n}\n\n/**\n * Render-cap event emission callback used by the bridge when the LLM invokes\n * a capability that carries a `render` spec. The runner provides the actual\n * sink (transport.send / sendEvent); the bridge only emits.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-render-emit\n */\nexport type BridgeRenderEmit = (event: RenderInvokedEvent) => void;\n\n/**\n * Text-event emission callback for render-capability fallbacks. When a\n * `RenderCapability` carries `render.fallback` and the LLM invokes it, the\n * bridge substitutes `{{prop}}` placeholders (top-level keys of `props`,\n * optional dotted-path traversal `{{user.name}}`) and pushes the rendered\n * string through this callback so clients without a render layer still see\n * a textual representation. No-op when `fallback` is absent.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-text-emit\n */\nexport type BridgeTextEmit = (text: string) => void;\n\n/**\n * Bundle of capability dispatch hooks. Registered into the driver via\n * `AgentConfig.capabilities`; the driver routes registered LLM tool calls\n * back into `invoke()` instead of using its native dispatch path.\n *\n * The runner constructs this from a `CapabilityRegistry` instance and threads\n * it through `createAgentSession()`. Drivers that don't yet implement\n * capability dispatch ignore the field — legacy v1 paths stay intact.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-hooks\n */\nexport interface BridgeCapabilityHooks {\n /** Build the LLM tool descriptor list from the registry. Called per turn so registration changes take effect. */\n composeTools(): BridgeCapabilityTool[];\n /** Resolve a wire-format Capability descriptor by name. Used by the bridge to inspect `fireAndForget` / `render`. */\n resolve(name: string): Capability | null;\n /** Validate input + dispatch through the registry. Logging is wired by the registry. */\n invoke(name: string, input: unknown): Promise<unknown>;\n /** Emit a render-invoked event. The runner forwards via the WebSocket transport. */\n emitRender?: BridgeRenderEmit;\n /** Emit a text fallback for render capabilities with `render.fallback`. */\n emitText?: BridgeTextEmit;\n /** Session id passed through to the runner's per-handler logger. */\n sessionId: string;\n}\n\n/**\n * Static metadata for a registered agent driver backend.\n *\n * Returned by {@link listDrivers} and exposed on every {@link AgentDriver} instance\n * via `driverInfo`.\n *\n * @docLink packages/bridge/concepts#driver-info\n */\nexport interface DriverInfo {\n /** Stable machine identifier used to look up the driver in the registry (e.g. `\"omp\"`, `\"claude-sdk\"`). */\n id: string;\n /** Human-readable display name shown in UIs and logs. */\n name: string;\n /** `true` when the driver can target any LLM provider; `false` for Anthropic-only drivers. */\n modelAgnostic: boolean;\n /**\n * `true` when the driver supports mid-stream abort via `abort()` without killing the process.\n * Drivers that return `false` can only be stopped via `kill()`.\n */\n supportsInBandAbort: boolean;\n}\n\n/**\n * Codex-specific tuning options passed through `DriverOptions.codex`.\n *\n * @remarks These are forwarded verbatim to the Codex driver and have no effect on other drivers.\n * @docLink packages/bridge/concepts#codex-driver-options\n */\nexport interface CodexDriverOptions {\n /** Controls whether Codex may auto-apply edits without user approval. */\n approvalPolicy?: \"never\" | \"on-request\" | \"on-failure\" | \"untrusted\";\n /** Filesystem sandbox level for the Codex process. */\n sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n /** Reasoning budget passed to the model. */\n reasoningEffort?: \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n /** When `true`, the Codex sandbox is allowed to make outbound network requests. */\n networkAccessEnabled?: boolean;\n /** Extra directories made accessible to the Codex sandbox in addition to `cwd`. */\n additionalDirectories?: string[];\n}\n\n/**\n * Driver-specific configuration bag.\n *\n * Fields are keyed by driver ID so that callers can pass driver-specific options\n * through the common {@link AgentConfig} without breaking other drivers.\n *\n * @docLink packages/bridge/concepts#driver-options\n */\nexport interface DriverOptions {\n /** Codex-specific options. Ignored by all other drivers. */\n codex?: CodexDriverOptions;\n}\n\n/**\n * Configuration passed to a driver when it is created via {@link createDriver}.\n *\n * All fields are shared across drivers; driver-specific behaviour is documented\n * per field. Fields that are silently ignored by a driver are marked accordingly.\n *\n * @docLink packages/bridge/concepts#agent-config\n */\nexport interface AgentConfig {\n /** Absolute path to the working directory the agent operates in. */\n cwd: string;\n /**\n * LLM provider name (e.g. `\"anthropic\"`, `\"openai\"`, `\"google\"`).\n * Combined with `model` as `provider/model` for omp's `--model` flag.\n * Ignored by claude-sdk (always Anthropic).\n */\n provider?: string;\n /**\n * LLM model identifier (e.g. `\"claude-sonnet-4-5\"`, `\"gpt-4o\"`).\n * For omp: passed as `--model [provider/]model`.\n * For claude-sdk: passed as the `model` query option.\n */\n model?: string;\n /**\n * Provider API keys keyed by provider name (e.g. `{ anthropic: \"sk-...\" }`).\n * Drivers inject the relevant key into the environment or SDK options.\n */\n apiKeys?: Record<string, string>;\n /**\n * Additional environment variables merged into the child process environment.\n * For omp: merged with `process.env` before spawn. For claude-sdk: accessed\n * via `env.ANTHROPIC_API_KEY` as an alternative to `apiKeys.anthropic`.\n */\n env?: Record<string, string>;\n /**\n * AI cloud transport for claude-sdk: `default | bedrock | vertex | azure |\n * gateway`. Absent (or `\"default\"`) preserves today's behavior — the\n * provider's native API. Typed as `string` for forward compatibility; the\n * driver falls back to `default` on unrecognized values (with a warning).\n * Ignored by all other drivers.\n *\n * @since 1.3.0\n */\n cloud?: string;\n /**\n * Non-secret transport settings for the selected `cloud` (camelCase — the\n * skaile.yaml `cloud_config` wire shape is snake_case and converted once at\n * the settings/serve boundary). Ignored when `cloud` is absent/default.\n *\n * @since 1.3.0\n */\n cloudConfig?: { region?: string; projectId?: string; resource?: string; baseUrl?: string };\n /**\n * Resolved credential env bundle for the selected `cloud` — runner-provided\n * (platform `session_init` secrets, or runner-materialized file paths such\n * as the Vertex service-account file). Values are secrets: never logged.\n *\n * @since 1.3.0\n */\n cloudSecrets?: Record<string, string>;\n /** Inline system prompt — written to .omp/system.md and passed via --append-system-prompt (omp driver) */\n systemPrompt?: string;\n /** Path to project .omp/ directory (PI_CODING_AGENT_DIR for omp driver) */\n agentDir?: string;\n /** SSH key path — injected as GIT_SSH_COMMAND */\n sshKeyPath?: string;\n /** Pre-assign a UUID as the session ID for a new session (claude-sdk: passed as sessionId option) */\n sessionId?: string;\n /** Resume this specific past session by UUID instead of starting fresh (claude-sdk: passed as resume option) */\n resumeSessionId?: string;\n /** Maximum agentic turns per query (claude-sdk: passed as maxTurns option) */\n maxTurns?: number;\n /** Agent name — selects a deployed sub-agent definition as the main agent identity.\n * claude-sdk: passed as `agent` option to query() → reads .claude/agents/<name>.md natively.\n * omp: ignored (omp uses agentDir/PI_CODING_AGENT_DIR instead). */\n agentName?: string;\n /** In-process SDK MCP servers for custom tool injection (Claude SDK only, ignored by other drivers) */\n mcpServers?: Record<string, unknown>;\n /** Tool restrictions from agent.yaml — applied to the main agent session */\n tools?: {\n /** Tool names that the agent is allowed to invoke. An empty array means no restriction. */\n allowed?: string[];\n /** Tool names that the agent is explicitly forbidden from invoking. */\n denied?: string[];\n };\n /** Thinking mode for Claude models: adaptive (Claude decides), enabled (always think), disabled (no thinking). */\n thinking?: \"adaptive\" | \"enabled\" | \"disabled\";\n /** Reasoning effort level for Claude models. */\n effort?: \"low\" | \"medium\" | \"high\" | \"max\";\n /** Driver-specific configuration bag. */\n driverOptions?: DriverOptions;\n /**\n * Protocol v2 capability dispatch hooks. When present, the driver uses the\n * registry as the source of LLM tool definitions and routes invocations\n * through `capabilities.invoke()`. Absent → legacy v1 path (existing\n * mcpServers / native dispatch).\n *\n * @since 2.0.0\n */\n capabilities?: BridgeCapabilityHooks;\n /**\n * The platform's `AIProviderConfig.id` for the AI credential currently\n * provisioned into this driver. The runner reads this off `AgentConfig`\n * during 401 mediation and passes it as `configId` in\n * `request_access_token { kind: 'ai-credentials' }`. The driver itself\n * does not consume this field — only the runner uses it.\n *\n * Set by the platform agent-gateway in `ConfigureCommandV2` for mediated\n * sessions. Standalone runners (CLI / forge / Claude plugin) leave it\n * undefined; the runner falls back to surfacing the auth error to the\n * user without mediating.\n *\n * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md`\n * § \"Wire `aiProviderConfigId` end-to-end\".\n *\n * @since 3.3.0\n */\n aiProviderConfigId?: string;\n /**\n * Optional callback invoked by the driver when the underlying agent\n * surfaces an `authentication_error`. In Protocol v3 the runner mediates\n * the refresh via the `host.refresh_credential` capability and returns a\n * typed {@link CredentialMint}. The driver inspects the discriminator:\n *\n * - `mint.ok === true`: a fresh credential is now provisioned. The\n * driver re-attempts the in-flight prompt once.\n * - `mint.ok === false`: refresh failed (`code` carries a stable reason\n * such as `revoked`, `not-configured`, `provider-error`,\n * `backend-error`). The driver surfaces the original `AuthError` to\n * the caller.\n *\n * `args.configId` carries the platform `AIProviderConfig.id` the driver\n * was provisioned with (mirrors {@link AgentConfig.aiProviderConfigId}).\n * The runner uses it to scope the refresh to the correct AI credential.\n *\n * When omitted, the driver throws `AuthError` immediately as before\n * (standalone CLI / forge mode — there is no platform mediator to ask).\n *\n * Centralising auth-retry inside the driver means every consumer of\n * `driver.prompt(...)` (serve handler, compaction orchestrator, flow\n * orchestrator, …) gets self-healing for free — there is no per-call-site\n * wrapping.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"host.refresh_credential capability\".\n *\n * @since 3.4.0\n */\n onAuthError?: (args: { configId: string }) => Promise<CredentialMint>;\n}\n\n/**\n * A single message in an agent conversation.\n *\n * Emitted as part of {@link AgentEvent} variants (`message_start`, `message_update`, `message_end`).\n *\n * @docLink packages/bridge/concepts#agent-message\n */\nexport interface AgentMessage {\n /** Originator of the message. */\n role: \"user\" | \"assistant\" | \"tool\";\n /** Message body — either a plain string or a list of typed content blocks. */\n content: string | ContentBlock[];\n /** Tool invocations made by the assistant in this message. */\n toolCalls?: ToolCall[];\n /** For `role === \"tool\"`: the name of the tool whose result this message carries. */\n toolName?: string;\n /** For `role === \"tool\"`: `true` when the tool returned an error. */\n isError?: boolean;\n /** Optional structured payload for tool results. */\n data?: unknown;\n}\n\n/**\n * A typed block within an {@link AgentMessage}'s content array.\n *\n * Mirrors the Anthropic content block schema but is driver-agnostic.\n *\n * @docLink packages/bridge/concepts#content-block\n */\nexport interface ContentBlock {\n /** Block discriminant. */\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"thinking\";\n /** Present for `text` and `thinking` blocks. */\n text?: string;\n /** Tool use / tool result correlation ID. */\n id?: string;\n /** Tool name (present on `tool_use` blocks). */\n name?: string;\n /** Tool input arguments (present on `tool_use` blocks). */\n input?: any;\n /** Serialised tool result content (present on `tool_result` blocks). */\n content?: string;\n}\n\n/**\n * A single tool invocation made by the agent within a message.\n *\n * @docLink packages/bridge/concepts#tool-call\n */\nexport interface ToolCall {\n /** Correlation ID used to match the call with its result. */\n id: string;\n /** Name of the tool being invoked. */\n name: string;\n /** Arguments passed to the tool. */\n input: any;\n}\n\n/**\n * Error classification for agent failures.\n *\n * Used to determine whether a failure is retryable and to surface actionable\n * hints to the user. See `bridge/CLAUDE.md` for the full category-to-behaviour matrix.\n *\n * @see AgentError\n * @docLink packages/bridge/concepts#error-category\n */\nexport type ErrorCategory =\n | \"auth\"\n | \"account\"\n | \"rate_limit\"\n | \"model\"\n | \"network\"\n | \"config\"\n | \"process\"\n | \"validation\"\n | \"unknown\";\n\n/**\n * Structured error payload emitted inside the `error` {@link AgentEvent}.\n *\n * Consumers should inspect `retryable` before deciding to surface a retry button,\n * and `hint` to provide an actionable message to the user.\n *\n * @docLink packages/bridge/concepts#agent-error\n */\nexport interface AgentError {\n /** Human-readable error description. */\n message: string;\n /** Coarse failure classification used for retry logic and telemetry. */\n category: ErrorCategory;\n /** HTTP status code if the error originated from an API response. */\n statusCode?: number;\n /** `true` when the caller may safely retry the same operation. */\n retryable: boolean;\n /** Short actionable advice suitable for display in the UI. */\n hint?: string;\n}\n\n/**\n * Metadata for a slash command exposed by the active agent runtime.\n *\n * @docLink packages/bridge/concepts#slash-command-info\n */\nexport interface SlashCommandInfo {\n /** Command name without the leading slash (e.g. `\"compact\"`). */\n name: string;\n /** One-line description shown in command pickers. */\n description: string;\n /** Placeholder text describing what argument the command expects. */\n argumentHint?: string;\n}\n\n/**\n * Discriminated union of all events emitted by a driver on the `'agent-event'` channel.\n *\n * @remarks\n * Consumers listen via `driver.on('agent-event', (event: AgentEvent) => ...)`.\n * The `[k: string]: any` index signature on most variants allows drivers to attach\n * driver-specific fields (e.g. `_textDelta`) without breaking the union.\n *\n * @docLink packages/bridge/concepts#agent-event\n */\nexport type AgentEvent =\n | { type: \"message_start\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_update\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_end\"; message: AgentMessage; [k: string]: any }\n | { type: \"turn_end\"; toolResults?: AgentMessage[]; [k: string]: any }\n | { type: \"agent_end\"; [k: string]: any }\n | {\n type: \"result\";\n subtype: string;\n summary?: string;\n costUsd?: number;\n /** Per-turn token usage when the driver tracks it. Added in 3.1.0. */\n tokens?: TokenUsage;\n errors?: string[];\n [k: string]: any;\n }\n | { type: \"error\"; error: string; detail?: AgentError; fatal?: boolean; [k: string]: any }\n | { type: \"tool_call\"; name?: string; tool?: { name: string }; [k: string]: any }\n | { type: \"tool_execution_end\"; toolName?: string; [k: string]: any }\n | { type: \"commands_available\"; commands: SlashCommandInfo[] }\n | { type: \"session_info\"; driverSessionId: string; sessionFile?: string }\n | { type: \"ui_render\"; [k: string]: any }\n | { type: \"ui_render_update\"; [k: string]: any }\n | { type: \"ui_clear\"; [k: string]: any }\n // Phase 3 (resume cascade) — driver-emitted resume outcome.\n | {\n type: \"resume_failed\";\n resumeSessionId: string;\n reason: \"signature_mismatch\" | \"model_mismatch\" | \"jsonl_lost\" | \"jsonl_poisoned\";\n [k: string]: any;\n };\n\n/**\n * Abstract base class for agent driver backends.\n *\n * Wraps an LLM coding agent behind a single `prompt()` interface.\n * All drivers emit `'agent-event'` with {@link AgentEvent} payloads.\n *\n * @remarks\n * Subclasses must implement `start`, `prompt`, `abort`, `kill`, and `isRunning`.\n * Implementations should never emit an event name other than `'agent-event'` as\n * the public streaming channel — internal events (`'ready'`, `'exit'`, etc.) are\n * driver-private.\n *\n * @example\n * ```ts\n * const driver = createDriver('omp', config);\n * driver.on('agent-event', (event) => console.log(event.type));\n * await driver.start();\n * await driver.prompt('Refactor the auth module');\n * driver.kill();\n * ```\n *\n * @docLink packages/bridge/concepts#agent-driver\n */\nexport abstract class AgentDriver extends EventEmitter {\n /** Static metadata describing this driver's capabilities. */\n abstract readonly driverInfo: DriverInfo;\n\n /**\n * Initialises the driver backend — spawns the child process (omp) or loads\n * the SDK module (claude-sdk). Resolves when the backend is ready to accept\n * prompts. Idempotent: calling `start()` on an already-running driver is a no-op.\n *\n * @throws {Error} When the backend binary is missing or the SDK cannot be loaded.\n */\n abstract start(): Promise<void>;\n\n /**\n * Sends a user message to the agent and resolves when the agent's turn completes\n * (i.e. after the `agent_end` event has been emitted).\n *\n * @param message - Plain-text user prompt to send to the agent.\n * @throws {Error} When the driver is not running or the underlying backend reports a fatal error.\n */\n abstract prompt(message: string): Promise<void>;\n\n /**\n * Sends an in-band abort signal to the agent, requesting it to stop the current\n * turn without terminating the process. The driver remains usable after `abort()`.\n *\n * @remarks Only meaningful when `driverInfo.supportsInBandAbort` is `true`.\n * For other drivers, prefer `kill()` followed by creating a new driver instance.\n */\n abstract abort(): Promise<void>;\n\n /**\n * Terminates the agent backend immediately (SIGTERM for subprocess drivers,\n * `close()` for in-process drivers). The driver instance must not be reused\n * after `kill()`.\n */\n abstract kill(): void;\n\n /** `true` when the backend is alive and able to accept new prompts. */\n abstract get isRunning(): boolean;\n\n /** Optional provider-native session/thread identifier for resume support. */\n get runtimeSessionId(): string | undefined {\n return undefined;\n }\n\n /** Slash commands discovered from the agent runtime. Override in drivers that support introspection. */\n getSlashCommands(): SlashCommandInfo[] {\n return [];\n }\n\n // ── Interactive questions (AskUserQuestion) ──────────────────────────────\n\n /**\n * `true` while the driver is paused on an interactive question and waiting\n * for the user's answer rather than a new prompt. The runner routes a\n * `reply` command to {@link answerQuestion} instead of {@link prompt} when\n * this returns `true`. Drivers without interactive-question support return\n * `false` (the default), so a `reply` falls back to `prompt`.\n */\n hasPendingQuestion(): boolean {\n return false;\n }\n\n /**\n * Deliver the user's answer to the question the driver is currently paused\n * on. This resumes the in-flight turn — it does NOT start a new one. When a\n * single tool call asks several questions, replies arrive one per `question`\n * event in order and the driver resolves the underlying call only once every\n * sub-question has an answer.\n *\n * @param answer - The user's reply text (an option label, free text, or for\n * multi-select a comma-joined list).\n * @returns `true` when the answer was consumed by a pending question; `false`\n * when no question was pending (caller should treat the reply as a prompt).\n */\n answerQuestion(answer: string): boolean {\n void answer;\n return false;\n }\n\n // ── Live reconfiguration ────────────────────────────────────────────────\n\n /**\n * Live-update driver configuration. The new values take effect on the next\n * prompt() call. Only model/thinking/effort are reconfigurable mid-session.\n *\n * Subclasses that store config locally should override this to apply the\n * patch to their own config field. The base implementation is a no-op.\n */\n reconfigure(patch: Partial<Pick<AgentConfig, \"model\" | \"thinking\" | \"effort\">>): void {\n // No-op — subclasses override to apply to their own config.\n void patch;\n }\n\n /**\n * Returns the model identifier currently configured on this driver.\n * Subclasses that store config locally should override this.\n */\n getModel(): string | undefined {\n return undefined;\n }\n\n // ── Compaction support ──────────────────────────────────────────────────\n\n /**\n * Returns the token usage from the most recent completed turn.\n * Returns `null` if usage data is unavailable (e.g. driver doesn't track it).\n *\n * Widened in 3.1.0 to return the full `TokenUsage` shape (input / output /\n * cache-read / cache-creation / reasoning). All fields are optional so\n * drivers can populate just what their provider reports. Existing callers\n * that read only `inputTokens` / `outputTokens` keep working — those fields\n * are still present and carry the same semantics as before.\n */\n getTokenUsage(): TokenUsage | null {\n return null;\n }\n\n /**\n * Returns the model's context window size in tokens.\n * Returns `null` if unknown.\n */\n getContextWindow(): number | null {\n return null;\n }\n\n /**\n * Returns the list of models available to this driver without starting a session.\n * Drivers that can enumerate their own models override this method.\n * Returns an empty array by default.\n */\n async listModels(): Promise<ModelEntry[]> {\n return [];\n }\n\n /**\n * Closes the current conversation session and prepares for a fresh start.\n * The next `prompt()` call will begin a new session without prior history.\n * The driver remains usable after `resetSession()`.\n */\n abstract resetSession(): Promise<void>;\n\n // ── Telemetry ─────────────────────────────────────────────────────────────\n\n protected _telemetry?: TelemetryProvider;\n protected _activeTrace?: Trace;\n protected _activeTurnSpan?: Span;\n private _turnStartTime?: number;\n\n /**\n * Attach a telemetry provider and active trace to this driver.\n * Called by the runner after creating the driver but before start().\n */\n setTelemetry(provider: TelemetryProvider, trace: Trace): void {\n this._telemetry = provider;\n this._activeTrace = trace;\n }\n\n /**\n * Begin a turn span. Called by the runner before prompt().\n * Pass a parent span (e.g. the turn span from the orchestrator) for nesting.\n * Returns the span so the runner can annotate it further.\n */\n beginTurnSpan(\n parent?: Trace | Span,\n attrs?: Record<string, string | number | boolean>,\n ): Span | undefined {\n if (!this._telemetry || !this._activeTrace) return undefined;\n this._turnStartTime = Date.now();\n this._activeTurnSpan = this._telemetry.startSpan(parent ?? this._activeTrace, {\n name: \"llm_generation\",\n kind: \"llm_generation\",\n attributes: attrs,\n });\n return this._activeTurnSpan;\n }\n\n /**\n * End the current turn span with generation data.\n * Called by the runner after agent_end. Model and provider are passed\n * explicitly — the driver base class does not access config directly.\n */\n endTurnSpan(result?: {\n model?: string;\n provider?: string;\n inputTokens?: number;\n outputTokens?: number;\n stopReason?: string;\n error?: string;\n }): void {\n if (!this._telemetry || !this._activeTurnSpan) return;\n const durationMs = this._turnStartTime ? Date.now() - this._turnStartTime : 0;\n\n if (result?.inputTokens !== undefined || result?.outputTokens !== undefined) {\n this._telemetry.logGeneration(this._activeTurnSpan, {\n model: result?.model ?? \"unknown\",\n provider: result?.provider ?? \"unknown\",\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs,\n stopReason: result.stopReason,\n });\n }\n\n this._telemetry.endSpan(this._activeTurnSpan, {\n status: result?.error ? \"error\" : \"ok\",\n error: result?.error,\n attributes: { \"skaile.turn.duration_ms\": durationMs },\n });\n\n this._activeTurnSpan = undefined;\n this._turnStartTime = undefined;\n }\n}\n\n/**\n * Factory function signature registered in the driver registry.\n *\n * @param config - Configuration for the new driver instance.\n * @returns A freshly constructed (not yet started) {@link AgentDriver}.\n * @docLink packages/bridge/concepts#driver-factory\n */\nexport type DriverFactory = (config: AgentConfig) => AgentDriver;\n","import type { DriverContext } from \"@skaile/workspaces/plugin-registry\";\nimport { type PluginRegistry, pluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport type { ModelEntry } from \"./models.js\";\nimport type { AgentConfig, AgentDriver, DriverInfo } from \"./types.js\";\n\n// Build-time backend gating. `bun build --compile --define:__INCLUDE_*__=...`\n// folds these to literal booleans so an excluded backend's driver module and\n// its SDK are dead-code-stripped from the binary. Under tsup / vitest / bun\n// run-on-source no `--define` runs and the `typeof` guard defaults to `true` —\n// every backend is available (the SDKs stay optional peers).\ndeclare const __INCLUDE_CLAUDE_SDK__: boolean;\ndeclare const __INCLUDE_CODEX__: boolean;\nconst INCLUDE_CLAUDE_SDK =\n typeof __INCLUDE_CLAUDE_SDK__ === \"undefined\" ? true : __INCLUDE_CLAUDE_SDK__;\nconst INCLUDE_CODEX = typeof __INCLUDE_CODEX__ === \"undefined\" ? true : __INCLUDE_CODEX__;\n\n/**\n * Static capability metadata for every built-in backend.\n *\n * Pure data — this object imports no driver module and references no backend\n * SDK. {@link listDrivers} returns it (merged with any third-party drivers)\n * so selection UIs can enumerate every backend without loading a single\n * driver module. The matching driver module's `DriverTarget` is registered\n * into the plugin registry by {@link registerBuiltinDrivers}.\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport const BUILTIN_DRIVER_CATALOG: Record<string, DriverInfo> = {\n omp: {\n id: \"omp\",\n name: \"omp (oh-my-pi)\",\n modelAgnostic: true,\n supportsInBandAbort: true,\n },\n \"claude-sdk\": {\n id: \"claude-sdk\",\n name: \"Claude Agent SDK\",\n modelAgnostic: false,\n supportsInBandAbort: true,\n },\n codex: {\n id: \"codex\",\n name: \"OpenAI Codex\",\n modelAgnostic: false,\n supportsInBandAbort: true,\n },\n echo: {\n id: \"echo\",\n name: \"Echo (E2E mock)\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n gemini: {\n id: \"gemini\",\n name: \"Gemini CLI\",\n modelAgnostic: false,\n supportsInBandAbort: false,\n },\n qwen: {\n id: \"qwen\",\n name: \"Qwen Code\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n opencode: {\n id: \"opencode\",\n name: \"OpenCode\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n goose: {\n id: \"goose\",\n name: \"Goose\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n continue: {\n id: \"continue\",\n name: \"Continue CLI\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n};\n\n/**\n * Back-compat alias for {@link BUILTIN_DRIVER_CATALOG}. Consumers and tests that\n * predate the plugin-registry refactor still import `DRIVER_CATALOG`.\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport const DRIVER_CATALOG = BUILTIN_DRIVER_CATALOG;\n\nlet _driversRegistered = false;\n\n/**\n * Register every built-in driver's `DriverTarget` into the plugin registry.\n *\n * This is the single id → module map in bridge core. Every specifier is a\n * literal string so `bun --compile` can statically bundle the driver module\n * (and, transitively, its SDK). The in-process-SDK backends (`claude-sdk`,\n * `codex`) are wrapped in build-constant guards so an excluded backend's\n * `await import` folds to dead code in the compiled binary. `omp` and `echo`\n * carry no SDK weight and are always available.\n *\n * **Static import discipline.** Every `import()` specifier below must be a\n * string literal — never aliased through a variable. Indirection defeats\n * `bun --compile`'s static analysis and silently drops the module from the\n * binary.\n *\n * Idempotent: guarded by a module flag so a second call is a no-op (a duplicate\n * `register` would otherwise throw `RegistrationError`).\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function registerBuiltinDrivers(\n registry: PluginRegistry = pluginRegistry,\n): Promise<void> {\n if (registry === pluginRegistry && _driversRegistered) return;\n\n if (!registry.get(\"driver\", \"omp\")) {\n const m = await import(\"@skaile/workspaces/drivers/omp\");\n registry.register(\"driver\", m.ompDriverTarget);\n }\n if (!registry.get(\"driver\", \"echo\")) {\n const m = await import(\"@skaile/workspaces/drivers/echo\");\n registry.register(\"driver\", m.echoDriverTarget);\n }\n if (INCLUDE_CLAUDE_SDK && !registry.get(\"driver\", \"claude-sdk\")) {\n const m = await import(\"@skaile/workspaces/drivers/claude-sdk\");\n registry.register(\"driver\", m.claudeSdkDriverTarget);\n }\n if (INCLUDE_CODEX && !registry.get(\"driver\", \"codex\")) {\n const m = await import(\"@skaile/workspaces/drivers/codex\");\n registry.register(\"driver\", m.codexDriverTarget);\n }\n // Subprocess CLI backends spawn an external binary the user installs\n // separately — no bundled SDK to dead-code-strip, so (like omp/echo) they\n // carry no build constant and are always registered.\n if (!registry.get(\"driver\", \"gemini\")) {\n const m = await import(\"@skaile/workspaces/drivers/gemini\");\n registry.register(\"driver\", m.geminiDriverTarget);\n }\n if (!registry.get(\"driver\", \"qwen\")) {\n const m = await import(\"@skaile/workspaces/drivers/qwen\");\n registry.register(\"driver\", m.qwenDriverTarget);\n }\n if (!registry.get(\"driver\", \"opencode\")) {\n const m = await import(\"@skaile/workspaces/drivers/opencode\");\n registry.register(\"driver\", m.opencodeDriverTarget);\n }\n if (!registry.get(\"driver\", \"goose\")) {\n const m = await import(\"@skaile/workspaces/drivers/goose\");\n registry.register(\"driver\", m.gooseDriverTarget);\n }\n if (!registry.get(\"driver\", \"continue\")) {\n const m = await import(\"@skaile/workspaces/drivers/continue\");\n registry.register(\"driver\", m.continueDriverTarget);\n }\n\n if (registry === pluginRegistry) _driversRegistered = true;\n}\n\n/**\n * Creates a new driver instance for the given backend id.\n *\n * Ensures the built-in driver targets are registered (idempotent), then looks\n * up the target in the plugin registry and calls its `create()`. The returned\n * driver has not been started yet — call `driver.start()` before sending\n * prompts.\n *\n * @param id - Registered driver identifier (e.g. `\"omp\"`, `\"claude-sdk\"`).\n * @param config - Configuration forwarded to the driver constructor.\n * @param ctx - Optional driver context (logger / abort signal). Defaults to `{}`.\n * @returns A freshly constructed (not yet started) {@link AgentDriver}.\n * @throws {Error} When `id` is a known backend excluded from this build, or\n * when `id` does not match any registered driver at all.\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function createDriver(\n id: string,\n config: AgentConfig,\n ctx?: DriverContext,\n): Promise<AgentDriver> {\n const target = await resolveDriverTarget(id);\n return target.create(config, ctx ?? {});\n}\n\n/**\n * Resolve a registered driver target by id, ensuring the built-ins are\n * registered first. Throws the canonical \"not bundled\" / \"unknown driver\"\n * errors so every caller (`createDriver`, `listModelsForDriver`) reports an\n * unrecognized id the same way instead of silently degrading.\n *\n * @throws {Error} When `id` is a known backend excluded from this build, or\n * when `id` does not match any registered driver at all.\n */\nasync function resolveDriverTarget(id: string) {\n await registerBuiltinDrivers();\n const target = pluginRegistry.get(\"driver\", id);\n if (!target) {\n if (id in BUILTIN_DRIVER_CATALOG) {\n throw new Error(`Agent driver \"${id}\" is not bundled in this build.`);\n }\n throw new Error(\n `Unknown agent driver: \"${id}\". Available: ${listDrivers()\n .map((d) => d.id)\n .join(\", \")}`,\n );\n }\n return target;\n}\n\n/**\n * Returns the capability metadata for every available driver.\n *\n * Enumerates every built-in backend from {@link BUILTIN_DRIVER_CATALOG} without\n * loading a single driver module or backend SDK, then merges in any third-party\n * driver targets registered into the plugin registry that are not already in\n * the catalog. Use this to populate driver selection UIs.\n *\n * @returns Array of {@link DriverInfo} objects, one per available driver.\n * @docLink packages/bridge/api-reference#registry\n */\nexport function listDrivers(): DriverInfo[] {\n const merged = new Map<string, DriverInfo>();\n for (const info of Object.values(BUILTIN_DRIVER_CATALOG)) {\n merged.set(info.id, info);\n }\n for (const meta of pluginRegistry.list(\"driver\")) {\n if (!merged.has(meta.id)) {\n // Third-party drivers carry no DriverInfo row; read the capability flags\n // off the registered target (defaulting to conservative false) so a\n // model-agnostic / in-band-abort backend can still advertise them.\n const target = pluginRegistry.get(\"driver\", meta.id);\n merged.set(meta.id, {\n id: meta.id,\n name: meta.displayName,\n modelAgnostic: target?.modelAgnostic ?? false,\n supportsInBandAbort: target?.supportsInBandAbort ?? false,\n });\n }\n }\n return [...merged.values()];\n}\n\n/**\n * List models available to a specific driver without starting a full agent session.\n *\n * Ensures the built-in driver targets are registered (idempotent), then\n * delegates to the target's optional `listModels()`. API keys are forwarded so\n * drivers can authenticate against provider REST APIs if needed. Throws on an\n * unknown / not-bundled id (same contract as {@link createDriver}); returns `[]`\n * only when the target exists but exposes no `listModels`.\n *\n * @param driverId - Registered driver id (e.g. `\"omp\"`, `\"claude-sdk\"`, `\"codex\"`).\n * @param apiKeys - Provider API keys keyed by provider name (e.g. `{ anthropic: \"sk-...\" }`).\n * Pass `settings.apiKeys ?? {}` from forge-assistant's resolved settings.\n * @throws {Error} When `driverId` is unknown or excluded from this build.\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function listModelsForDriver(\n driverId: string,\n apiKeys: Record<string, string>,\n): Promise<ModelEntry[]> {\n const target = await resolveDriverTarget(driverId);\n return (await target.listModels?.(apiKeys)) ?? [];\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../bridge/src/logger.ts","../bridge/src/types.ts","../bridge/src/registry.ts"],"names":[],"mappings":"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","file":"chunk-TNSCHS4I.js","sourcesContent":["/**\n * Bridge-layer logger factory.\n *\n * Each driver creates its own Logger via `getBridgeLogger(subkind)` at\n * construction time. Falls back to an off-mode Logger (stdout only) when\n * no LogStore is registered — see @skaile/workspaces/core/logging for details.\n */\n\nimport { createLogger } from \"@skaile/workspaces/core/logging\";\nimport type { Logger } from \"@skaile/workspaces/types\";\n\n/**\n * Construct a bridge-scoped Logger for a given driver subkind.\n *\n * Creates a `Logger` with `kind: \"bridge\"` and the specified `subkind`. Falls back to\n * an off-mode Logger (stdout only) when no `LogStore` is registered — see\n * `@skaile/workspaces/core/logging` for details. Each driver calls this once at construction\n * time; never re-construct per call.\n *\n * @param subkind - Driver id, e.g. `\"claude-sdk\"`, `\"omp\"`, `\"echo\"`, `\"codex\"`.\n * @returns A `Logger` scoped to `bridge:<subkind>`.\n * @docLink packages/bridge/api-reference#logger\n */\nexport function getBridgeLogger(subkind: string): Logger {\n return createLogger({ kind: \"bridge\", subkind });\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Span, TelemetryProvider, Trace } from \"@skaile/workspaces/telemetry\";\nimport type {\n Capability,\n CredentialMint,\n RenderInvokedEvent,\n TokenUsage,\n} from \"@skaile/workspaces/types\";\nimport type { ModelEntry } from \"./models.js\";\n\n/**\n * Minimal LLM tool descriptor used by the capability dispatch path. Mirrors\n * the runner's `LLMTool` shape so drivers can consume registry output without\n * importing `@skaile/workspaces/runner` (which would create a circular dep).\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-tool\n */\nexport interface BridgeCapabilityTool {\n /** Capability name; used as the LLM-visible tool name. */\n name: string;\n /** Human/LLM-readable description. */\n description: string;\n /** JSON Schema for the tool's input parameters. */\n parameters: Record<string, unknown>;\n}\n\n/**\n * Render-cap event emission callback used by the bridge when the LLM invokes\n * a capability that carries a `render` spec. The runner provides the actual\n * sink (transport.send / sendEvent); the bridge only emits.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-render-emit\n */\nexport type BridgeRenderEmit = (event: RenderInvokedEvent) => void;\n\n/**\n * Text-event emission callback for render-capability fallbacks. When a\n * `RenderCapability` carries `render.fallback` and the LLM invokes it, the\n * bridge substitutes `{{prop}}` placeholders (top-level keys of `props`,\n * optional dotted-path traversal `{{user.name}}`) and pushes the rendered\n * string through this callback so clients without a render layer still see\n * a textual representation. No-op when `fallback` is absent.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-text-emit\n */\nexport type BridgeTextEmit = (text: string) => void;\n\n/**\n * Bundle of capability dispatch hooks. Registered into the driver via\n * `AgentConfig.capabilities`; the driver routes registered LLM tool calls\n * back into `invoke()` instead of using its native dispatch path.\n *\n * The runner constructs this from a `CapabilityRegistry` instance and threads\n * it through `createAgentSession()`. Drivers that don't yet implement\n * capability dispatch ignore the field — legacy v1 paths stay intact.\n *\n * @category Capabilities\n * @since 2.0.0\n * @docLink packages/bridge/concepts#bridge-capability-hooks\n */\nexport interface BridgeCapabilityHooks {\n /** Build the LLM tool descriptor list from the registry. Called per turn so registration changes take effect. */\n composeTools(): BridgeCapabilityTool[];\n /** Resolve a wire-format Capability descriptor by name. Used by the bridge to inspect `fireAndForget` / `render`. */\n resolve(name: string): Capability | null;\n /** Validate input + dispatch through the registry. Logging is wired by the registry. */\n invoke(name: string, input: unknown): Promise<unknown>;\n /** Emit a render-invoked event. The runner forwards via the WebSocket transport. */\n emitRender?: BridgeRenderEmit;\n /** Emit a text fallback for render capabilities with `render.fallback`. */\n emitText?: BridgeTextEmit;\n /** Session id passed through to the runner's per-handler logger. */\n sessionId: string;\n}\n\n/**\n * Static metadata for a registered agent driver backend.\n *\n * Returned by {@link listDrivers} and exposed on every {@link AgentDriver} instance\n * via `driverInfo`.\n *\n * @docLink packages/bridge/concepts#driver-info\n */\nexport interface DriverInfo {\n /** Stable machine identifier used to look up the driver in the registry (e.g. `\"omp\"`, `\"claude-sdk\"`). */\n id: string;\n /** Human-readable display name shown in UIs and logs. */\n name: string;\n /** `true` when the driver can target any LLM provider; `false` for Anthropic-only drivers. */\n modelAgnostic: boolean;\n /**\n * `true` when the driver supports mid-stream abort via `abort()` without killing the process.\n * Drivers that return `false` can only be stopped via `kill()`.\n */\n supportsInBandAbort: boolean;\n}\n\n/**\n * Codex-specific tuning options passed through `DriverOptions.codex`.\n *\n * @remarks These are forwarded verbatim to the Codex driver and have no effect on other drivers.\n * @docLink packages/bridge/concepts#codex-driver-options\n */\nexport interface CodexDriverOptions {\n /** Controls whether Codex may auto-apply edits without user approval. */\n approvalPolicy?: \"never\" | \"on-request\" | \"on-failure\" | \"untrusted\";\n /** Filesystem sandbox level for the Codex process. */\n sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n /** Reasoning budget passed to the model. */\n reasoningEffort?: \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n /** When `true`, the Codex sandbox is allowed to make outbound network requests. */\n networkAccessEnabled?: boolean;\n /** Extra directories made accessible to the Codex sandbox in addition to `cwd`. */\n additionalDirectories?: string[];\n}\n\n/**\n * Driver-specific configuration bag.\n *\n * Fields are keyed by driver ID so that callers can pass driver-specific options\n * through the common {@link AgentConfig} without breaking other drivers.\n *\n * @docLink packages/bridge/concepts#driver-options\n */\nexport interface DriverOptions {\n /** Codex-specific options. Ignored by all other drivers. */\n codex?: CodexDriverOptions;\n}\n\n/**\n * Configuration passed to a driver when it is created via {@link createDriver}.\n *\n * All fields are shared across drivers; driver-specific behaviour is documented\n * per field. Fields that are silently ignored by a driver are marked accordingly.\n *\n * @docLink packages/bridge/concepts#agent-config\n */\nexport interface AgentConfig {\n /** Absolute path to the working directory the agent operates in. */\n cwd: string;\n /**\n * LLM provider name (e.g. `\"anthropic\"`, `\"openai\"`, `\"google\"`).\n * Combined with `model` as `provider/model` for omp's `--model` flag.\n * Ignored by claude-sdk (always Anthropic).\n */\n provider?: string;\n /**\n * LLM model identifier (e.g. `\"claude-sonnet-4-5\"`, `\"gpt-4o\"`).\n * For omp: passed as `--model [provider/]model`.\n * For claude-sdk: passed as the `model` query option.\n */\n model?: string;\n /**\n * Provider API keys keyed by provider name (e.g. `{ anthropic: \"sk-...\" }`).\n * Drivers inject the relevant key into the environment or SDK options.\n */\n apiKeys?: Record<string, string>;\n /**\n * Additional environment variables merged into the child process environment.\n * For omp: merged with `process.env` before spawn. For claude-sdk: accessed\n * via `env.ANTHROPIC_API_KEY` as an alternative to `apiKeys.anthropic`.\n */\n env?: Record<string, string>;\n /**\n * AI cloud transport for claude-sdk: `default | bedrock | vertex | azure |\n * gateway`. Absent (or `\"default\"`) preserves today's behavior — the\n * provider's native API. Typed as `string` for forward compatibility; the\n * driver falls back to `default` on unrecognized values (with a warning).\n * Ignored by all other drivers.\n *\n * @since 1.3.0\n */\n cloud?: string;\n /**\n * Non-secret transport settings for the selected `cloud` (camelCase — the\n * skaile.yaml `cloud_config` wire shape is snake_case and converted once at\n * the settings/serve boundary). Ignored when `cloud` is absent/default.\n *\n * @since 1.3.0\n */\n cloudConfig?: { region?: string; projectId?: string; resource?: string; baseUrl?: string };\n /**\n * Resolved credential env bundle for the selected `cloud` — runner-provided\n * (platform `session_init` secrets, or runner-materialized file paths such\n * as the Vertex service-account file). Values are secrets: never logged.\n *\n * @since 1.3.0\n */\n cloudSecrets?: Record<string, string>;\n /** Inline system prompt — written to .omp/system.md and passed via --append-system-prompt (omp driver) */\n systemPrompt?: string;\n /** Path to project .omp/ directory (PI_CODING_AGENT_DIR for omp driver) */\n agentDir?: string;\n /** SSH key path — injected as GIT_SSH_COMMAND */\n sshKeyPath?: string;\n /** Pre-assign a UUID as the session ID for a new session (claude-sdk: passed as sessionId option) */\n sessionId?: string;\n /** Resume this specific past session by UUID instead of starting fresh (claude-sdk: passed as resume option) */\n resumeSessionId?: string;\n /** Maximum agentic turns per query (claude-sdk: passed as maxTurns option) */\n maxTurns?: number;\n /** Agent name — selects a deployed sub-agent definition as the main agent identity.\n * claude-sdk: passed as `agent` option to query() → reads .claude/agents/<name>.md natively.\n * omp: ignored (omp uses agentDir/PI_CODING_AGENT_DIR instead). */\n agentName?: string;\n /** In-process SDK MCP servers for custom tool injection (Claude SDK only, ignored by other drivers) */\n mcpServers?: Record<string, unknown>;\n /** Tool restrictions from agent.yaml — applied to the main agent session */\n tools?: {\n /** Tool names that the agent is allowed to invoke. An empty array means no restriction. */\n allowed?: string[];\n /** Tool names that the agent is explicitly forbidden from invoking. */\n denied?: string[];\n };\n /** Thinking mode for Claude models: adaptive (Claude decides), enabled (always think), disabled (no thinking). */\n thinking?: \"adaptive\" | \"enabled\" | \"disabled\";\n /** Reasoning effort level for Claude models. */\n effort?: \"low\" | \"medium\" | \"high\" | \"max\";\n /** Driver-specific configuration bag. */\n driverOptions?: DriverOptions;\n /**\n * Protocol v2 capability dispatch hooks. When present, the driver uses the\n * registry as the source of LLM tool definitions and routes invocations\n * through `capabilities.invoke()`. Absent → legacy v1 path (existing\n * mcpServers / native dispatch).\n *\n * @since 2.0.0\n */\n capabilities?: BridgeCapabilityHooks;\n /**\n * The platform's `AIProviderConfig.id` for the AI credential currently\n * provisioned into this driver. The runner reads this off `AgentConfig`\n * during 401 mediation and passes it as `configId` in\n * `request_access_token { kind: 'ai-credentials' }`. The driver itself\n * does not consume this field — only the runner uses it.\n *\n * Set by the platform agent-gateway in `ConfigureCommandV2` for mediated\n * sessions. Standalone runners (CLI / forge / Claude plugin) leave it\n * undefined; the runner falls back to surfacing the auth error to the\n * user without mediating.\n *\n * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md`\n * § \"Wire `aiProviderConfigId` end-to-end\".\n *\n * @since 3.3.0\n */\n aiProviderConfigId?: string;\n /**\n * Optional callback invoked by the driver when the underlying agent\n * surfaces an `authentication_error`. In Protocol v3 the runner mediates\n * the refresh via the `host.refresh_credential` capability and returns a\n * typed {@link CredentialMint}. The driver inspects the discriminator:\n *\n * - `mint.ok === true`: a fresh credential is now provisioned. The\n * driver re-attempts the in-flight prompt once.\n * - `mint.ok === false`: refresh failed (`code` carries a stable reason\n * such as `revoked`, `not-configured`, `provider-error`,\n * `backend-error`). The driver surfaces the original `AuthError` to\n * the caller.\n *\n * `args.configId` carries the platform `AIProviderConfig.id` the driver\n * was provisioned with (mirrors {@link AgentConfig.aiProviderConfigId}).\n * The runner uses it to scope the refresh to the correct AI credential.\n *\n * When omitted, the driver throws `AuthError` immediately as before\n * (standalone CLI / forge mode — there is no platform mediator to ask).\n *\n * Centralising auth-retry inside the driver means every consumer of\n * `driver.prompt(...)` (serve handler, compaction orchestrator, flow\n * orchestrator, …) gets self-healing for free — there is no per-call-site\n * wrapping.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"host.refresh_credential capability\".\n *\n * @since 3.4.0\n */\n onAuthError?: (args: { configId: string }) => Promise<CredentialMint>;\n}\n\n/**\n * A single message in an agent conversation.\n *\n * Emitted as part of {@link AgentEvent} variants (`message_start`, `message_update`, `message_end`).\n *\n * @docLink packages/bridge/concepts#agent-message\n */\nexport interface AgentMessage {\n /** Originator of the message. */\n role: \"user\" | \"assistant\" | \"tool\";\n /** Message body — either a plain string or a list of typed content blocks. */\n content: string | ContentBlock[];\n /** Tool invocations made by the assistant in this message. */\n toolCalls?: ToolCall[];\n /** For `role === \"tool\"`: the name of the tool whose result this message carries. */\n toolName?: string;\n /** For `role === \"tool\"`: `true` when the tool returned an error. */\n isError?: boolean;\n /** Optional structured payload for tool results. */\n data?: unknown;\n}\n\n/**\n * A typed block within an {@link AgentMessage}'s content array.\n *\n * Mirrors the Anthropic content block schema but is driver-agnostic.\n *\n * @docLink packages/bridge/concepts#content-block\n */\nexport interface ContentBlock {\n /** Block discriminant. */\n type: \"text\" | \"tool_use\" | \"tool_result\" | \"thinking\";\n /** Present for `text` and `thinking` blocks. */\n text?: string;\n /** Tool use / tool result correlation ID. */\n id?: string;\n /** Tool name (present on `tool_use` blocks). */\n name?: string;\n /** Tool input arguments (present on `tool_use` blocks). */\n input?: any;\n /** Serialised tool result content (present on `tool_result` blocks). */\n content?: string;\n}\n\n/**\n * A single tool invocation made by the agent within a message.\n *\n * @docLink packages/bridge/concepts#tool-call\n */\nexport interface ToolCall {\n /** Correlation ID used to match the call with its result. */\n id: string;\n /** Name of the tool being invoked. */\n name: string;\n /** Arguments passed to the tool. */\n input: any;\n}\n\n/**\n * Error classification for agent failures.\n *\n * Used to determine whether a failure is retryable and to surface actionable\n * hints to the user. See `bridge/CLAUDE.md` for the full category-to-behaviour matrix.\n *\n * @see AgentError\n * @docLink packages/bridge/concepts#error-category\n */\nexport type ErrorCategory =\n | \"auth\"\n | \"account\"\n | \"rate_limit\"\n | \"model\"\n | \"network\"\n | \"config\"\n | \"process\"\n | \"validation\"\n | \"unknown\";\n\n/**\n * Structured error payload emitted inside the `error` {@link AgentEvent}.\n *\n * Consumers should inspect `retryable` before deciding to surface a retry button,\n * and `hint` to provide an actionable message to the user.\n *\n * @docLink packages/bridge/concepts#agent-error\n */\nexport interface AgentError {\n /** Human-readable error description. */\n message: string;\n /** Coarse failure classification used for retry logic and telemetry. */\n category: ErrorCategory;\n /** HTTP status code if the error originated from an API response. */\n statusCode?: number;\n /** `true` when the caller may safely retry the same operation. */\n retryable: boolean;\n /** Short actionable advice suitable for display in the UI. */\n hint?: string;\n}\n\n/**\n * Metadata for a slash command exposed by the active agent runtime.\n *\n * @docLink packages/bridge/concepts#slash-command-info\n */\nexport interface SlashCommandInfo {\n /** Command name without the leading slash (e.g. `\"compact\"`). */\n name: string;\n /** One-line description shown in command pickers. */\n description: string;\n /** Placeholder text describing what argument the command expects. */\n argumentHint?: string;\n}\n\n/**\n * Discriminated union of all events emitted by a driver on the `'agent-event'` channel.\n *\n * @remarks\n * Consumers listen via `driver.on('agent-event', (event: AgentEvent) => ...)`.\n * The `[k: string]: any` index signature on most variants allows drivers to attach\n * driver-specific fields (e.g. `_textDelta`) without breaking the union.\n *\n * @docLink packages/bridge/concepts#agent-event\n */\nexport type AgentEvent =\n | { type: \"message_start\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_update\"; message: AgentMessage; [k: string]: any }\n | { type: \"message_end\"; message: AgentMessage; [k: string]: any }\n | { type: \"turn_end\"; toolResults?: AgentMessage[]; [k: string]: any }\n | { type: \"agent_end\"; [k: string]: any }\n | {\n type: \"result\";\n subtype: string;\n summary?: string;\n costUsd?: number;\n /** Per-turn token usage when the driver tracks it. Added in 3.1.0. */\n tokens?: TokenUsage;\n errors?: string[];\n [k: string]: any;\n }\n | { type: \"error\"; error: string; detail?: AgentError; fatal?: boolean; [k: string]: any }\n | { type: \"tool_call\"; name?: string; tool?: { name: string }; [k: string]: any }\n | { type: \"tool_execution_end\"; toolName?: string; [k: string]: any }\n | { type: \"commands_available\"; commands: SlashCommandInfo[] }\n | { type: \"session_info\"; driverSessionId: string; sessionFile?: string }\n | { type: \"ui_render\"; [k: string]: any }\n | { type: \"ui_render_update\"; [k: string]: any }\n | { type: \"ui_clear\"; [k: string]: any }\n // Phase 3 (resume cascade) — driver-emitted resume outcome.\n | {\n type: \"resume_failed\";\n resumeSessionId: string;\n reason: \"signature_mismatch\" | \"model_mismatch\" | \"jsonl_lost\" | \"jsonl_poisoned\";\n [k: string]: any;\n };\n\n/**\n * Abstract base class for agent driver backends.\n *\n * Wraps an LLM coding agent behind a single `prompt()` interface.\n * All drivers emit `'agent-event'` with {@link AgentEvent} payloads.\n *\n * @remarks\n * Subclasses must implement `start`, `prompt`, `abort`, `kill`, and `isRunning`.\n * Implementations should never emit an event name other than `'agent-event'` as\n * the public streaming channel — internal events (`'ready'`, `'exit'`, etc.) are\n * driver-private.\n *\n * @example\n * ```ts\n * const driver = createDriver('omp', config);\n * driver.on('agent-event', (event) => console.log(event.type));\n * await driver.start();\n * await driver.prompt('Refactor the auth module');\n * driver.kill();\n * ```\n *\n * @docLink packages/bridge/concepts#agent-driver\n */\nexport abstract class AgentDriver extends EventEmitter {\n /** Static metadata describing this driver's capabilities. */\n abstract readonly driverInfo: DriverInfo;\n\n /**\n * Initialises the driver backend — spawns the child process (omp) or loads\n * the SDK module (claude-sdk). Resolves when the backend is ready to accept\n * prompts. Idempotent: calling `start()` on an already-running driver is a no-op.\n *\n * @throws {Error} When the backend binary is missing or the SDK cannot be loaded.\n */\n abstract start(): Promise<void>;\n\n /**\n * Sends a user message to the agent and resolves when the agent's turn completes\n * (i.e. after the `agent_end` event has been emitted).\n *\n * @param message - Plain-text user prompt to send to the agent.\n * @throws {Error} When the driver is not running or the underlying backend reports a fatal error.\n */\n abstract prompt(message: string): Promise<void>;\n\n /**\n * Sends an in-band abort signal to the agent, requesting it to stop the current\n * turn without terminating the process. The driver remains usable after `abort()`.\n *\n * @remarks Only meaningful when `driverInfo.supportsInBandAbort` is `true`.\n * For other drivers, prefer `kill()` followed by creating a new driver instance.\n */\n abstract abort(): Promise<void>;\n\n /**\n * Terminates the agent backend immediately (SIGTERM for subprocess drivers,\n * `close()` for in-process drivers). The driver instance must not be reused\n * after `kill()`.\n */\n abstract kill(): void;\n\n /** `true` when the backend is alive and able to accept new prompts. */\n abstract get isRunning(): boolean;\n\n /** Optional provider-native session/thread identifier for resume support. */\n get runtimeSessionId(): string | undefined {\n return undefined;\n }\n\n /** Slash commands discovered from the agent runtime. Override in drivers that support introspection. */\n getSlashCommands(): SlashCommandInfo[] {\n return [];\n }\n\n // ── Interactive questions (AskUserQuestion) ──────────────────────────────\n\n /**\n * `true` while the driver is paused on an interactive question and waiting\n * for the user's answer rather than a new prompt. The runner routes a\n * `reply` command to {@link answerQuestion} instead of {@link prompt} when\n * this returns `true`. Drivers without interactive-question support return\n * `false` (the default), so a `reply` falls back to `prompt`.\n */\n hasPendingQuestion(): boolean {\n return false;\n }\n\n /**\n * Deliver the user's answer to the question the driver is currently paused\n * on. This resumes the in-flight turn — it does NOT start a new one. When a\n * single tool call asks several questions, replies arrive one per `question`\n * event in order and the driver resolves the underlying call only once every\n * sub-question has an answer.\n *\n * @param answer - The user's reply text (an option label, free text, or for\n * multi-select a comma-joined list).\n * @returns `true` when the answer was consumed by a pending question; `false`\n * when no question was pending (caller should treat the reply as a prompt).\n */\n answerQuestion(answer: string): boolean {\n void answer;\n return false;\n }\n\n // ── Live reconfiguration ────────────────────────────────────────────────\n\n /**\n * Live-update driver configuration. The new values take effect on the next\n * prompt() call. Only model/thinking/effort are reconfigurable mid-session.\n *\n * Subclasses that store config locally should override this to apply the\n * patch to their own config field. The base implementation is a no-op.\n */\n reconfigure(patch: Partial<Pick<AgentConfig, \"model\" | \"thinking\" | \"effort\">>): void {\n // No-op — subclasses override to apply to their own config.\n void patch;\n }\n\n /**\n * Returns the model identifier currently configured on this driver.\n * Subclasses that store config locally should override this.\n */\n getModel(): string | undefined {\n return undefined;\n }\n\n // ── Compaction support ──────────────────────────────────────────────────\n\n /**\n * Returns the token usage from the most recent completed turn.\n * Returns `null` if usage data is unavailable (e.g. driver doesn't track it).\n *\n * Widened in 3.1.0 to return the full `TokenUsage` shape (input / output /\n * cache-read / cache-creation / reasoning). All fields are optional so\n * drivers can populate just what their provider reports. Existing callers\n * that read only `inputTokens` / `outputTokens` keep working — those fields\n * are still present and carry the same semantics as before.\n */\n getTokenUsage(): TokenUsage | null {\n return null;\n }\n\n /**\n * Returns the model's context window size in tokens.\n * Returns `null` if unknown.\n */\n getContextWindow(): number | null {\n return null;\n }\n\n /**\n * Returns the list of models available to this driver without starting a session.\n * Drivers that can enumerate their own models override this method.\n * Returns an empty array by default.\n */\n async listModels(): Promise<ModelEntry[]> {\n return [];\n }\n\n /**\n * Closes the current conversation session and prepares for a fresh start.\n * The next `prompt()` call will begin a new session without prior history.\n * The driver remains usable after `resetSession()`.\n */\n abstract resetSession(): Promise<void>;\n\n // ── Telemetry ─────────────────────────────────────────────────────────────\n\n protected _telemetry?: TelemetryProvider;\n protected _activeTrace?: Trace;\n protected _activeTurnSpan?: Span;\n private _turnStartTime?: number;\n\n /**\n * Attach a telemetry provider and active trace to this driver.\n * Called by the runner after creating the driver but before start().\n */\n setTelemetry(provider: TelemetryProvider, trace: Trace): void {\n this._telemetry = provider;\n this._activeTrace = trace;\n }\n\n /**\n * Begin a turn span. Called by the runner before prompt().\n * Pass a parent span (e.g. the turn span from the orchestrator) for nesting.\n * Returns the span so the runner can annotate it further.\n */\n beginTurnSpan(\n parent?: Trace | Span,\n attrs?: Record<string, string | number | boolean>,\n ): Span | undefined {\n if (!this._telemetry || !this._activeTrace) return undefined;\n this._turnStartTime = Date.now();\n this._activeTurnSpan = this._telemetry.startSpan(parent ?? this._activeTrace, {\n name: \"llm_generation\",\n kind: \"llm_generation\",\n attributes: attrs,\n });\n return this._activeTurnSpan;\n }\n\n /**\n * End the current turn span with generation data.\n * Called by the runner after agent_end. Model and provider are passed\n * explicitly — the driver base class does not access config directly.\n */\n endTurnSpan(result?: {\n model?: string;\n provider?: string;\n inputTokens?: number;\n outputTokens?: number;\n stopReason?: string;\n error?: string;\n }): void {\n if (!this._telemetry || !this._activeTurnSpan) return;\n const durationMs = this._turnStartTime ? Date.now() - this._turnStartTime : 0;\n\n if (result?.inputTokens !== undefined || result?.outputTokens !== undefined) {\n this._telemetry.logGeneration(this._activeTurnSpan, {\n model: result?.model ?? \"unknown\",\n provider: result?.provider ?? \"unknown\",\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs,\n stopReason: result.stopReason,\n });\n }\n\n this._telemetry.endSpan(this._activeTurnSpan, {\n status: result?.error ? \"error\" : \"ok\",\n error: result?.error,\n attributes: { \"skaile.turn.duration_ms\": durationMs },\n });\n\n this._activeTurnSpan = undefined;\n this._turnStartTime = undefined;\n }\n}\n\n/**\n * Factory function signature registered in the driver registry.\n *\n * @param config - Configuration for the new driver instance.\n * @returns A freshly constructed (not yet started) {@link AgentDriver}.\n * @docLink packages/bridge/concepts#driver-factory\n */\nexport type DriverFactory = (config: AgentConfig) => AgentDriver;\n","import type { DriverContext } from \"@skaile/workspaces/plugin-registry\";\nimport { type PluginRegistry, pluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport type { ModelEntry } from \"./models.js\";\nimport type { AgentConfig, AgentDriver, DriverInfo } from \"./types.js\";\n\n// Build-time backend gating. `bun build --compile --define:__INCLUDE_*__=...`\n// folds these to literal booleans so an excluded backend's driver module and\n// its SDK are dead-code-stripped from the binary. Under tsup / vitest / bun\n// run-on-source no `--define` runs and the `typeof` guard defaults to `true` —\n// every backend is available (the SDKs stay optional peers).\ndeclare const __INCLUDE_CLAUDE_SDK__: boolean;\ndeclare const __INCLUDE_CODEX__: boolean;\nconst INCLUDE_CLAUDE_SDK =\n typeof __INCLUDE_CLAUDE_SDK__ === \"undefined\" ? true : __INCLUDE_CLAUDE_SDK__;\nconst INCLUDE_CODEX = typeof __INCLUDE_CODEX__ === \"undefined\" ? true : __INCLUDE_CODEX__;\n\n/**\n * Static capability metadata for every built-in backend.\n *\n * Pure data — this object imports no driver module and references no backend\n * SDK. {@link listDrivers} returns it (merged with any third-party drivers)\n * so selection UIs can enumerate every backend without loading a single\n * driver module. The matching driver module's `DriverTarget` is registered\n * into the plugin registry by {@link registerBuiltinDrivers}.\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport const BUILTIN_DRIVER_CATALOG: Record<string, DriverInfo> = {\n omp: {\n id: \"omp\",\n name: \"omp (oh-my-pi)\",\n modelAgnostic: true,\n supportsInBandAbort: true,\n },\n \"claude-sdk\": {\n id: \"claude-sdk\",\n name: \"Claude Agent SDK\",\n modelAgnostic: false,\n supportsInBandAbort: true,\n },\n codex: {\n id: \"codex\",\n name: \"OpenAI Codex\",\n modelAgnostic: false,\n supportsInBandAbort: true,\n },\n echo: {\n id: \"echo\",\n name: \"Echo (E2E mock)\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n gemini: {\n id: \"gemini\",\n name: \"Gemini CLI\",\n modelAgnostic: false,\n supportsInBandAbort: false,\n },\n qwen: {\n id: \"qwen\",\n name: \"Qwen Code\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n opencode: {\n id: \"opencode\",\n name: \"OpenCode\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n goose: {\n id: \"goose\",\n name: \"Goose\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n continue: {\n id: \"continue\",\n name: \"Continue CLI\",\n modelAgnostic: true,\n supportsInBandAbort: false,\n },\n};\n\n/**\n * Back-compat alias for {@link BUILTIN_DRIVER_CATALOG}. Consumers and tests that\n * predate the plugin-registry refactor still import `DRIVER_CATALOG`.\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport const DRIVER_CATALOG = BUILTIN_DRIVER_CATALOG;\n\nlet _driversRegistered = false;\n\n/**\n * Register every built-in driver's `DriverTarget` into the plugin registry.\n *\n * This is the single id → module map in bridge core. Every specifier is a\n * literal string so `bun --compile` can statically bundle the driver module\n * (and, transitively, its SDK). The in-process-SDK backends (`claude-sdk`,\n * `codex`) are wrapped in build-constant guards so an excluded backend's\n * `await import` folds to dead code in the compiled binary. `omp` and `echo`\n * carry no SDK weight and are always available.\n *\n * **Static import discipline.** Every `import()` specifier below must be a\n * string literal — never aliased through a variable. Indirection defeats\n * `bun --compile`'s static analysis and silently drops the module from the\n * binary.\n *\n * Idempotent: guarded by a module flag so a second call is a no-op (a duplicate\n * `register` would otherwise throw `RegistrationError`).\n *\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function registerBuiltinDrivers(\n registry: PluginRegistry = pluginRegistry,\n): Promise<void> {\n if (registry === pluginRegistry && _driversRegistered) return;\n\n if (!registry.get(\"driver\", \"omp\")) {\n const m = await import(\"@skaile/workspaces/drivers/omp\");\n registry.register(\"driver\", m.ompDriverTarget);\n }\n if (!registry.get(\"driver\", \"echo\")) {\n const m = await import(\"@skaile/workspaces/drivers/echo\");\n registry.register(\"driver\", m.echoDriverTarget);\n }\n if (INCLUDE_CLAUDE_SDK && !registry.get(\"driver\", \"claude-sdk\")) {\n const m = await import(\"@skaile/workspaces/drivers/claude-sdk\");\n registry.register(\"driver\", m.claudeSdkDriverTarget);\n }\n if (INCLUDE_CODEX && !registry.get(\"driver\", \"codex\")) {\n const m = await import(\"@skaile/workspaces/drivers/codex\");\n registry.register(\"driver\", m.codexDriverTarget);\n }\n // Subprocess CLI backends spawn an external binary the user installs\n // separately — no bundled SDK to dead-code-strip, so (like omp/echo) they\n // carry no build constant and are always registered.\n if (!registry.get(\"driver\", \"gemini\")) {\n const m = await import(\"@skaile/workspaces/drivers/gemini\");\n registry.register(\"driver\", m.geminiDriverTarget);\n }\n if (!registry.get(\"driver\", \"qwen\")) {\n const m = await import(\"@skaile/workspaces/drivers/qwen\");\n registry.register(\"driver\", m.qwenDriverTarget);\n }\n if (!registry.get(\"driver\", \"opencode\")) {\n const m = await import(\"@skaile/workspaces/drivers/opencode\");\n registry.register(\"driver\", m.opencodeDriverTarget);\n }\n if (!registry.get(\"driver\", \"goose\")) {\n const m = await import(\"@skaile/workspaces/drivers/goose\");\n registry.register(\"driver\", m.gooseDriverTarget);\n }\n if (!registry.get(\"driver\", \"continue\")) {\n const m = await import(\"@skaile/workspaces/drivers/continue\");\n registry.register(\"driver\", m.continueDriverTarget);\n }\n\n if (registry === pluginRegistry) _driversRegistered = true;\n}\n\n/**\n * Creates a new driver instance for the given backend id.\n *\n * Ensures the built-in driver targets are registered (idempotent), then looks\n * up the target in the plugin registry and calls its `create()`. The returned\n * driver has not been started yet — call `driver.start()` before sending\n * prompts.\n *\n * @param id - Registered driver identifier (e.g. `\"omp\"`, `\"claude-sdk\"`).\n * @param config - Configuration forwarded to the driver constructor.\n * @param ctx - Optional driver context (logger / abort signal). Defaults to `{}`.\n * @returns A freshly constructed (not yet started) {@link AgentDriver}.\n * @throws {Error} When `id` is a known backend excluded from this build, or\n * when `id` does not match any registered driver at all.\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function createDriver(\n id: string,\n config: AgentConfig,\n ctx?: DriverContext,\n): Promise<AgentDriver> {\n const target = await resolveDriverTarget(id);\n return target.create(config, ctx ?? {});\n}\n\n/**\n * Resolve a registered driver target by id, ensuring the built-ins are\n * registered first. Throws the canonical \"not bundled\" / \"unknown driver\"\n * errors so every caller (`createDriver`, `listModelsForDriver`) reports an\n * unrecognized id the same way instead of silently degrading.\n *\n * @throws {Error} When `id` is a known backend excluded from this build, or\n * when `id` does not match any registered driver at all.\n */\nasync function resolveDriverTarget(id: string) {\n await registerBuiltinDrivers();\n const target = pluginRegistry.get(\"driver\", id);\n if (!target) {\n if (id in BUILTIN_DRIVER_CATALOG) {\n throw new Error(`Agent driver \"${id}\" is not bundled in this build.`);\n }\n throw new Error(\n `Unknown agent driver: \"${id}\". Available: ${listDrivers()\n .map((d) => d.id)\n .join(\", \")}`,\n );\n }\n return target;\n}\n\n/**\n * Returns the capability metadata for every available driver.\n *\n * Enumerates every built-in backend from {@link BUILTIN_DRIVER_CATALOG} without\n * loading a single driver module or backend SDK, then merges in any third-party\n * driver targets registered into the plugin registry that are not already in\n * the catalog. Use this to populate driver selection UIs.\n *\n * @returns Array of {@link DriverInfo} objects, one per available driver.\n * @docLink packages/bridge/api-reference#registry\n */\nexport function listDrivers(): DriverInfo[] {\n const merged = new Map<string, DriverInfo>();\n for (const info of Object.values(BUILTIN_DRIVER_CATALOG)) {\n merged.set(info.id, info);\n }\n for (const meta of pluginRegistry.list(\"driver\")) {\n if (!merged.has(meta.id)) {\n // Third-party drivers carry no DriverInfo row; read the capability flags\n // off the registered target (defaulting to conservative false) so a\n // model-agnostic / in-band-abort backend can still advertise them.\n const target = pluginRegistry.get(\"driver\", meta.id);\n merged.set(meta.id, {\n id: meta.id,\n name: meta.displayName,\n modelAgnostic: target?.modelAgnostic ?? false,\n supportsInBandAbort: target?.supportsInBandAbort ?? false,\n });\n }\n }\n return [...merged.values()];\n}\n\n/**\n * List models available to a specific driver without starting a full agent session.\n *\n * Ensures the built-in driver targets are registered (idempotent), then\n * delegates to the target's optional `listModels()`. API keys are forwarded so\n * drivers can authenticate against provider REST APIs if needed. Throws on an\n * unknown / not-bundled id (same contract as {@link createDriver}); returns `[]`\n * only when the target exists but exposes no `listModels`.\n *\n * @param driverId - Registered driver id (e.g. `\"omp\"`, `\"claude-sdk\"`, `\"codex\"`).\n * @param apiKeys - Provider API keys keyed by provider name (e.g. `{ anthropic: \"sk-...\" }`).\n * Pass `settings.apiKeys ?? {}` from forge-assistant's resolved settings.\n * @throws {Error} When `driverId` is unknown or excluded from this build.\n * @docLink packages/bridge/api-reference#registry\n */\nexport async function listModelsForDriver(\n driverId: string,\n apiKeys: Record<string, string>,\n): Promise<ModelEntry[]> {\n const target = await resolveDriverTarget(driverId);\n return (await target.listModels?.(apiKeys)) ?? [];\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MENTION_GROUPS } from './chunk-
|
|
1
|
+
import { MENTION_GROUPS } from './chunk-PK7Q7MC7.js';
|
|
2
2
|
|
|
3
3
|
// session/src/dispatcher.ts
|
|
4
4
|
var TRANSIENT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -516,5 +516,5 @@ function isAgentAddressed(mentions) {
|
|
|
516
516
|
}
|
|
517
517
|
|
|
518
518
|
export { SessionDispatcher, isAddressedByKind, isAgentAddressed, isMentioned, parseMentions };
|
|
519
|
-
//# sourceMappingURL=chunk-
|
|
520
|
-
//# sourceMappingURL=chunk-
|
|
519
|
+
//# sourceMappingURL=chunk-UPZGQZEJ.js.map
|
|
520
|
+
//# sourceMappingURL=chunk-UPZGQZEJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../session/src/dispatcher.ts","../session/src/mentions.ts"],"names":[],"mappings":"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","file":"chunk-Z5Z3MYDL.js","sourcesContent":["import type { AgentClient } from \"@skaile/workspaces/client\";\nimport type {\n AgentCommand,\n AgentEvent,\n ConnectorRequestCommand,\n ConnectorResponseEvent,\n Mention,\n ReplyRef,\n} from \"@skaile/workspaces/types\";\nimport type { Message, MessageStore, SubscriberInfo, SubscriberTransport } from \"./types.js\";\n\n/**\n * Construction options for {@link SessionDispatcher}.\n *\n * All three infrastructure objects (`client`, `store`, `transport`) are\n * injected by the caller so the dispatcher remains testable and framework-agnostic.\n *\n * @docLink packages/session/api-reference#session-dispatcher\n */\nexport interface SessionDispatcherOptions {\n /** Unique identifier for the session this dispatcher manages. */\n sessionId: string;\n /** Connected agent client used to send commands and receive events. */\n client: AgentClient;\n /** Persistence layer for all non-transient messages. */\n store: MessageStore;\n /** Fan-out transport for broadcasting events to connected frontends. */\n transport: SubscriberTransport;\n /**\n * When true, the dispatcher prepends conversation history to every prompt\n * command sent to the agent. Enable for agents without native session\n * continuity (e.g. mock mode). Agents with native multi-turn support\n * (Claude SDK, OMP) should set this to false to avoid duplicate context.\n * Default: false.\n */\n injectConversationHistory?: boolean;\n /** Max conversation history messages to include in enriched prompts (default: 50). */\n historyLimit?: number;\n /**\n * Optional callback to transform prompt text before forwarding to the agent.\n * The original (untransformed) prompt is persisted and broadcast to subscribers.\n * Applied after conversation history enrichment. Use this to inject context\n * (e.g. UI state) that the agent should see but subscribers should not.\n */\n transformPrompt?: (prompt: string, senderId: string | undefined, replyTo?: ReplyRef) => string;\n}\n\n/**\n * Command and event types classified as transient RPC — high-frequency,\n * ephemeral, and not part of the conversation history.\n *\n * Matching payloads are forwarded and broadcast but never persisted\n * or assigned a sequence number, preventing bloat in the message log.\n */\nconst TRANSIENT_TYPES = new Set([\n \"resource_request\",\n \"resource_response\",\n \"resources_available\",\n \"commands_available\",\n \"state_changed\",\n \"typing\",\n \"user_message\",\n // OMP session tracking — forwarded to subscribers but not persisted\n \"session_info\",\n // Structured log forwarding — high-volume; persisted in per-session SQLite, not the message store\n \"log_entry\",\n // Generic connector dispatch — system RPC, not chat messages.\n // Replaces the 6 typed flow commands removed in Protocol 2.2 (Phase 4 of the\n // flow-connector extraction). `connector_mutate` against the `flow` connector\n // is now how hosts drive flow execution between turns.\n \"connector_mutate\",\n \"connector_query\",\n \"connector_query_response\",\n]);\n\n/**\n * Bridges a single agent session between an {@link AgentClient} and one or more\n * frontend subscribers.\n *\n * Responsibilities:\n * - Persists every non-transient command and event as a {@link Message} with a\n * monotonic sequence number via the injected {@link MessageStore}.\n * - Fans agent events out to all connected frontends via the injected\n * {@link SubscriberTransport}.\n * - Optionally enriches prompt commands with conversation history so agents\n * without native multi-turn support maintain context across turns.\n *\n * **Lifecycle:** call {@link init} before any other method, and {@link disconnect}\n * when the session ends.\n *\n * @example\n * ```ts\n * const dispatcher = new SessionDispatcher({\n * sessionId: 'sess-123',\n * client: agentClient,\n * store: prismaMessageStore,\n * transport: trpcTransport,\n * })\n * await dispatcher.init()\n * const history = await dispatcher.subscribe('ws-conn-abc', { limit: 50 })\n * const msg = await dispatcher.sendCommand({ type: 'prompt', prompt: 'Hello' }, 'user-42')\n * await dispatcher.disconnect()\n * ```\n *\n * @docLink packages/session/api-reference#session-dispatcher\n */\nexport class SessionDispatcher {\n /** The session identifier this dispatcher is bound to. */\n readonly sessionId: string;\n private client: AgentClient;\n private store: MessageStore;\n private transport: SubscriberTransport;\n private subscribers: Set<string> = new Set();\n private subscriberMeta: Map<string, SubscriberInfo> = new Map();\n private seq: number = 0;\n private _availableResources: { mounts: unknown[]; connectors: unknown[] } = {\n mounts: [],\n connectors: [],\n };\n\n /** Whether to inject conversation history into prompt commands. */\n private readonly injectConversationHistory: boolean;\n /** Maximum number of history messages to include in enriched prompts. */\n private readonly historyLimit: number;\n /** Optional prompt transformer applied before forwarding (after persistence). */\n private _transformPrompt?: (\n prompt: string,\n senderId: string | undefined,\n replyTo?: ReplyRef,\n ) => string;\n /** Whether the dispatcher is currently in compaction mode. */\n private _compacting = false;\n /** Accumulated text content during compaction. */\n private _compactionText = \"\";\n\n /**\n * One-shot waiters registered via {@link onceNextFinished}. Each is invoked\n * exactly once with the text of the next `finished` assistant event, then\n * the list is cleared. Backs the A2A router's synchronous `ask` answer\n * capture.\n */\n private nextFinishedWaiters: Array<(text: string) => void> = [];\n /**\n * Text streamed by the agent since the last `finished` event. Used as the\n * fallback answer text for {@link onceNextFinished} when a `finished` event\n * carries an empty `summary` (drivers that stream `text` chunks and leave\n * the summary blank).\n */\n private _turnText = \"\";\n\n /**\n * @param opts - Dispatcher configuration — see {@link SessionDispatcherOptions}.\n */\n constructor(opts: SessionDispatcherOptions) {\n this.sessionId = opts.sessionId;\n this.client = opts.client;\n this.store = opts.store;\n this.transport = opts.transport;\n this.injectConversationHistory = opts.injectConversationHistory ?? false;\n this.historyLimit = opts.historyLimit ?? 50;\n this._transformPrompt = opts.transformPrompt;\n }\n\n /**\n * Replace the prompt transformer at runtime.\n *\n * Useful when additional context (e.g. a UI state cache) becomes available\n * after the dispatcher is constructed but before the first prompt is sent.\n */\n set transformPrompt(fn:\n | ((prompt: string, senderId: string | undefined, replyTo?: ReplyRef) => string)\n | undefined,) {\n this._transformPrompt = fn;\n }\n\n /**\n * Initialize the dispatcher for this session.\n *\n * Restores the monotonic sequence counter from the store and wires up the\n * agent event listener. Must be called once before any other method.\n *\n * @throws If {@link MessageStore.getLatestSeq} rejects.\n */\n async init(): Promise<void> {\n this.seq = await this.store.getLatestSeq(this.sessionId);\n this.client.onEvent((event: AgentEvent) => {\n void this.handleAgentEvent(event);\n });\n }\n\n /**\n * Register a frontend subscriber and return recent message history.\n *\n * Subsequent agent events are pushed to this subscriber via the\n * {@link SubscriberTransport} until {@link unsubscribe} is called.\n *\n * @param subscriberId - Opaque connection ID (e.g. WebSocket session ID).\n * @param opts - Optional subscription options.\n * @param opts.limit - Number of recent messages to return (default: 50).\n * @returns Recent messages ordered by seq ascending.\n */\n async subscribe(\n subscriberId: string,\n opts?: { limit?: number; userId?: string; roles?: string[] },\n ): Promise<Message[]> {\n this.subscribers.add(subscriberId);\n if (opts?.userId !== undefined || opts?.roles !== undefined) {\n this.subscriberMeta.set(subscriberId, {\n userId: opts.userId,\n roles: opts.roles,\n });\n }\n return this.store.getMessages(this.sessionId, {\n limit: opts?.limit ?? 50,\n });\n }\n\n /**\n * Remove a frontend subscriber and release transport resources.\n *\n * @param subscriberId - The ID passed to {@link subscribe}.\n */\n unsubscribe(subscriberId: string): void {\n this.subscribers.delete(subscriberId);\n this.subscriberMeta.delete(subscriberId);\n this.transport.remove(subscriberId);\n }\n\n /**\n * Send a command from a frontend to the agent.\n *\n * Persistent commands are wrapped in a Message envelope and stored before\n * forwarding. Transient commands (resource RPC) are forwarded directly\n * without persistence — returns null in that case.\n *\n * For `prompt` commands, the original message is persisted, but an enriched\n * version (with conversation history prepended) is sent to the agent so it\n * maintains context across turns.\n *\n * @param command - The command to dispatch to the agent (a prompt or tool result).\n * @param senderId - ID of the user sending the command, if known.\n * @param mentions - Resolved mention objects extracted from the prompt text.\n * @param visibility - Optional platform visibility envelope (F-23). When\n * `visibilityMode` is `HumansOnly`, or `Private` without the `\"__agent__\"`\n * sentinel in `privateRecipientIds` (B-30), the message is persisted and\n * broadcast to subscribers but NOT forwarded to the agent.\n * @param outgoingPrefix - Optional text prepended to the outgoing command's\n * user-text field ({@link command.prompt} / `command.answer` /\n * `command.content`) AFTER persistence but BEFORE history enrichment and\n * {@link transformPrompt}. The persisted {@link Message} envelope is built\n * from the original `command` and never sees the prefix. Use this to\n * inject one-shot wake-time context (Option B1 pending restoration\n * prompt) that the agent must see but UI subscribers must not.\n * @returns The persisted Message envelope, or null for transient command types.\n */\n async sendCommand(\n command: AgentCommand,\n senderId?: string,\n mentions?: Mention[],\n visibility?: NonNullable<Message[\"visibility\"]>,\n outgoingPrefix?: string,\n ): Promise<Message | null> {\n if (!this.shouldPersist(command)) {\n this.client.send(command);\n return null;\n }\n // Persist the original command (unenriched/untransformed/un-prefixed).\n // `outgoingPrefix` is intentionally NOT applied here — it must only\n // reach the agent client, never the MessageStore.\n const message = this.createMessage(command, senderId);\n // @TODO: Code-Review: v1 back-compat — mentions field no longer used by Protocol v2+ persistence layers; remove once platform PrismaMessageStore is migrated\n if (mentions?.length) message.mentions = mentions;\n // F-23: platform-extension visibility metadata (3.2.0). The platform\n // MessageStore reads this off the envelope and writes the\n // Message.visibilityMode column + recipient rows atomically with\n // the message itself — no post-send patch, no race window where a\n // concurrent send could mis-tag the wrong message id.\n if (visibility) message.visibility = visibility;\n await this.store.append(message);\n\n // Build the outgoing command — enrich and transform prompt commands\n // while leaving the persisted message untouched.\n let outgoing: AgentCommand = command;\n if (command.type === \"prompt\") {\n let prompt = command.prompt;\n // Restoration prefix lands FIRST (innermost) so the user's typed\n // text remains adjacent to its own framing, with history and reply\n // preambles wrapping the combined block from the outside in.\n if (outgoingPrefix) {\n prompt = `${outgoingPrefix}${prompt}`;\n }\n if (this.injectConversationHistory) {\n prompt = await this.enrichPromptWithHistory(prompt);\n }\n if (this._transformPrompt) {\n prompt = this._transformPrompt(prompt, senderId, command.replyTo);\n }\n if (prompt !== command.prompt) {\n outgoing = { ...command, prompt };\n }\n } else if (outgoingPrefix && command.type === \"reply\") {\n // `reply` carries user text in `answer`. Prefix is invisible to\n // subscribers (who see the persisted Message.payload.answer) but\n // visible to the agent driver.\n outgoing = { ...command, answer: `${outgoingPrefix}${command.answer}` };\n } else if (outgoingPrefix && command.type === \"custom_message\") {\n // `custom_message` carries user text in `content`. Same persist/send\n // split as the `reply` branch above.\n outgoing = { ...command, content: `${outgoingPrefix}${command.content}` };\n }\n\n // F-23 P3 review (B1) — HumansOnly visibility means the message is\n // intended for human recipients only; the agent must NOT see the\n // prompt content. Persist + broadcast (humans read it via\n // SubscriberTransport) but skip the agent forward. This closes a\n // critical privacy hole where `@user: secret` was persisted with\n // visibilityMode='HumansOnly' yet still reached the LLM. Threads\n // starting HumansOnly stay agent-invisible even on Public follow-ups\n // (parent-thread visibility threading is intentionally out of scope\n // for v1; only the immediate message is gated). See\n // platform/_concept/plans/f-23-p3-review-blockers.md.\n if (visibility?.visibilityMode === \"HumansOnly\") {\n return message;\n }\n\n // B-30 — Private visibility (F-23 `@mention_` underscore suffix) means the\n // message is addressed to an explicit recipient set. The agent is only a\n // recipient when the sender wrote `@agent_`, which the platform encodes as\n // the `\"__agent__\"` sentinel in `privateRecipientIds`. When the sentinel is\n // absent the message is a private human-to-human exchange: persist +\n // broadcast (the named human recipients read it via SubscriberTransport)\n // but skip the agent forward so the LLM never sees private content.\n if (\n visibility?.visibilityMode === \"Private\" &&\n !visibility.privateRecipientIds?.includes(\"__agent__\")\n ) {\n return message;\n }\n\n this.client.send(outgoing);\n return message;\n }\n\n /**\n * Persist a synthetic event (not received from the agent) and broadcast it.\n *\n * Used by the platform to inject events like a partial-text `finished` record\n * before a cancel command.\n *\n * @param event - The agent event to persist.\n * @param mentions - Resolved mentions from the originating command.\n */\n async persistEvent(event: AgentEvent, mentions?: Mention[]): Promise<Message | null> {\n return this.handleAgentEvent(event, mentions);\n }\n\n /**\n * Load paginated message history for this session.\n *\n * @param opts.before - Return messages with seq strictly less than this value.\n * @param opts.limit - Maximum number of messages to return.\n */\n async getMessages(opts?: { before?: number; limit?: number }): Promise<Message[]> {\n return this.store.getMessages(this.sessionId, opts);\n }\n\n /**\n * Available mounts and connectors captured from the most recent\n * `resources_available` event emitted by the agent on connect.\n *\n * Empty until the agent emits the event or {@link seedResources} is called.\n */\n get availableResources(): { mounts: unknown[]; connectors: unknown[] } {\n return this._availableResources;\n }\n\n /**\n * Seed the available resources from an external cache.\n *\n * This is needed when the dispatcher is created after the agent has already\n * emitted `resources_available` on WebSocket connect. Without seeding, the\n * dispatcher would report an empty resource list until the next reconnect.\n *\n * @param resources - Resource handles to register before the session starts.\n */\n seedResources(resources: { mounts: unknown[]; connectors: unknown[] }): void {\n const empty =\n this._availableResources.mounts.length === 0 &&\n this._availableResources.connectors.length === 0;\n if (empty && (resources.mounts.length > 0 || resources.connectors.length > 0)) {\n this._availableResources = resources;\n }\n }\n\n /**\n * Deliver a prompt to the agent that triggers a turn but is NOT persisted.\n *\n * Unlike {@link sendCommand}, this never touches the {@link MessageStore}:\n * the prompt is forwarded straight to the agent client. The caller owns\n * persistence of whatever conversation rows should represent this turn.\n *\n * Used by the platform's A2A router to deliver a `from-peer` prompt to a\n * target session: the router writes the authoritative A2A `Message` rows\n * itself, so persisting the framed prompt here too would create an extra,\n * un-attributed plain `prompt` row. The dispatcher's job for this prompt is\n * delivery only.\n *\n * The agent still produces a normal turn, so an A2A `ask` round-trip can\n * capture the answer via {@link onceNextFinished}.\n *\n * @param prompt - The fully-formed prompt text to deliver to the agent.\n */\n deliverPrompt(prompt: string): void {\n this.client.send({ type: \"prompt\", prompt });\n }\n\n /**\n * Send a resource request and await the correlated response.\n *\n * This is a convenience method for request/response resource RPC.\n * The request is transient (not persisted). A one-shot event listener\n * waits for the matching `resource_response` by `requestId`.\n *\n * @param command - A ConnectorRequestCommand (caller must set requestId).\n * @param timeoutMs - How long to wait before rejecting (default: 30s).\n */\n async requestResource(\n command: ConnectorRequestCommand,\n timeoutMs = 30_000,\n ): Promise<ConnectorResponseEvent> {\n return new Promise<ConnectorResponseEvent>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.client.offEvent(handler);\n reject(\n new Error(\n `Resource request timed out after ${timeoutMs}ms: ${command.operation} ${command.connectorId}`,\n ),\n );\n }, timeoutMs);\n\n const handler = (event: AgentEvent) => {\n if (event.type === \"resource_response\" && event.requestId === command.requestId) {\n clearTimeout(timer);\n this.client.offEvent(handler);\n resolve(event);\n }\n };\n\n this.client.onEvent(handler);\n this.client.send(command);\n });\n }\n\n /**\n * Broadcast a transient event to all subscribers without persisting it.\n *\n * Used for ephemeral signals like typing indicators or user-command\n * echoes that other subscribers need to see in real time.\n */\n broadcast(event: AgentEvent): void {\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n }\n\n /** Disconnect the agent client and clean up all subscribers. */\n async disconnect(): Promise<void> {\n for (const id of this.subscribers) {\n this.transport.remove(id);\n }\n this.subscribers.clear();\n this.subscriberMeta.clear();\n // Clear any pending A2A waiters so they don't leak if the session\n // disconnects before the target ever emits a `finished` event.\n this.nextFinishedWaiters = [];\n await this.client.dispose();\n }\n\n /**\n * Enter compaction mode. While active, events from the agent are captured\n * but NOT persisted. Text content is accumulated for the snapshot.\n */\n beginCompaction(): void {\n this._compacting = true;\n this._compactionText = \"\";\n }\n\n /**\n * Exit compaction mode and return the accumulated text.\n * The caller is responsible for creating and emitting the snapshot event.\n */\n endCompaction(): string {\n this._compacting = false;\n const text = this._compactionText;\n this._compactionText = \"\";\n return text;\n }\n\n /** Whether the dispatcher is currently in compaction mode. */\n get isCompacting(): boolean {\n return this._compacting;\n }\n\n /**\n * Register a one-shot callback fired with the text of this session's next\n * finished assistant turn. Returns an unsubscribe function.\n *\n * Semantics:\n * - The callback fires **at most once** — on the first `finished` event\n * observed after registration. Later finished turns do not re-fire it.\n * - A waiter registered after a `finished` event waits for the *following*\n * one. The hook captures the *next* finished turn, not a past one.\n * - The answer text is the `finished` event's `summary`; when that is\n * empty (drivers that stream `text` and leave the summary blank), the\n * text accumulated since the previous finished turn is used instead.\n * - Calling the returned function removes the waiter; if it has already\n * fired this is a no-op.\n *\n * Used by the platform's A2A router (`A2ARouterService.ask`) to capture a\n * peer session's answer to a `from-peer` question. v1 captures the first\n * finished turn after delivery — see the busy-target notes in the A2A\n * design spec for the known turn-matching limitation.\n *\n * @param cb - Invoked once with the next finished turn's text.\n * @returns Unsubscribe function that removes this waiter.\n */\n onceNextFinished(cb: (text: string) => void): () => void {\n this.nextFinishedWaiters.push(cb);\n return () => {\n const idx = this.nextFinishedWaiters.indexOf(cb);\n if (idx !== -1) {\n this.nextFinishedWaiters.splice(idx, 1);\n }\n };\n }\n\n // -- Private ----------------------------------------------------------------\n\n /**\n * Build an enriched prompt by prepending conversation history.\n *\n * Loads recent persisted messages, formats user/assistant turns, and\n * wraps them in a `<conversation_history>` block. The current user\n * message (just persisted) is excluded since it is already the prompt.\n *\n * Each turn is rendered as a self-closing `<turn role=\"...\">` element\n * rather than a bare `[User]:` / `[Assistant]:` transcript line. A plain\n * role-prefixed transcript invites the model to *continue* it — completing\n * the next `[User]:` turn as plaintext inside its own answer (a fabricated\n * follow-up that swallows the real next message). XML-tagged, explicitly\n * read-only turns remove that completion affordance while preserving the\n * same information.\n *\n * Returns the original prompt unchanged if there is no prior history.\n */\n private async enrichPromptWithHistory(currentPrompt: string): Promise<string> {\n const messages = await this.store.getMessages(this.sessionId, {\n limit: this.historyLimit,\n });\n\n // Filter to conversation-relevant types and exclude the message we just appended\n const history = messages\n .filter((m) => {\n const type = m.payload.type;\n return type === \"prompt\" || type === \"finished\" || type === \"question\";\n })\n .slice(0, -1); // Exclude the current message (last by seq)\n\n if (history.length === 0) {\n return currentPrompt;\n }\n\n // Escape so message content (which may itself contain `</turn>` or other\n // angle brackets) cannot break out of the XML structure or inject a forged\n // turn. The model reads entity-encoded text fine; this is read-only context.\n const esc = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n\n const turns = history.map((m) => {\n const p = m.payload as Record<string, unknown>;\n if (p.type === \"prompt\") {\n return `<turn role=\"user\">${esc(String(p.prompt ?? \"\"))}</turn>`;\n }\n // finished (assistant response) or question\n const content = String(p.summary ?? p.question ?? \"\");\n return `<turn role=\"assistant\">${esc(content)}</turn>`;\n });\n\n return (\n \"<conversation_history>\\n\" +\n \"The following is prior conversation context, provided read-only so you \" +\n \"have continuity. Do NOT continue, echo, or complete it — it is history, \" +\n \"not a turn for you to extend. Respond only to the user message that \" +\n \"follows the closing tag.\\n\" +\n `${turns.join(\"\\n\")}\\n` +\n \"</conversation_history>\\n\\n\" +\n currentPrompt\n );\n }\n\n private async handleAgentEvent(event: AgentEvent, mentions?: Mention[]): Promise<Message | null> {\n // ── Compaction mode: capture text but don't persist ─────────────────\n if (this._compacting) {\n if (event.type === \"text\") {\n this._compactionText += (event as { content: string }).content;\n }\n // Still broadcast to subscribers (UI shows compaction progress)\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n return null;\n }\n\n // Capture resource metadata when the server announces available resources\n if (event.type === \"resources_available\") {\n const payload = event as { mounts?: unknown[]; connectors?: unknown[] };\n this._availableResources = {\n mounts: payload.mounts ?? [],\n connectors: payload.connectors ?? [],\n };\n }\n\n // ── onceNextFinished — one-shot \"next finished turn\" answer capture ──\n // Accumulate streamed text across the turn so the answer is available\n // even when a driver emits an empty `finished.summary`. On `finished`,\n // drain every queued waiter exactly once, then reset the accumulator.\n if (event.type === \"text\") {\n this._turnText += (event as { content: string }).content;\n } else if (event.type === \"finished\") {\n const summary = (event as { summary?: string }).summary ?? \"\";\n const answer = summary.length > 0 ? summary : this._turnText;\n this._turnText = \"\";\n if (this.nextFinishedWaiters.length > 0) {\n const waiters = this.nextFinishedWaiters;\n this.nextFinishedWaiters = [];\n for (const cb of waiters) {\n cb(answer);\n }\n }\n }\n\n let message: Message | null = null;\n if (this.shouldPersist(event)) {\n message = this.createMessage(event);\n if (mentions?.length) message.mentions = mentions;\n await this.store.append(message);\n }\n\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n return message;\n }\n\n private shouldPersist(payload: AgentCommand | AgentEvent): boolean {\n return !TRANSIENT_TYPES.has(payload.type);\n }\n\n private createMessage(payload: AgentCommand | AgentEvent, senderId?: string): Message {\n this.seq++;\n return {\n id: crypto.randomUUID(),\n seq: this.seq,\n timestamp: new Date().toISOString(),\n sessionId: this.sessionId,\n ...(senderId ? { senderId } : {}),\n payload,\n };\n }\n}\n","import type { Mention, MentionGroup } from \"@skaile/workspaces/types\";\nimport { MENTION_GROUPS } from \"@skaile/workspaces/types\";\n\n/**\n * Re-exported from `@skaile/workspaces/types` so consumers can import mention\n * types directly from `@skaile/workspaces/session` without a separate dependency.\n */\nexport type { Mention, MentionGroup };\n\n// ---------------------------------------------------------------------------\n// MentionResolver\n// ---------------------------------------------------------------------------\n\n/**\n * Consumer-provided resolver that maps @mention tokens to users and groups.\n *\n * The platform provides an implementation backed by Prisma and the live\n * presence service. Tests use lightweight mocks.\n *\n * @example\n * ```ts\n * const resolver: MentionResolver = {\n * resolveUser: (name) => db.users.findByName(name),\n * resolveGroup: (group) => presence.getMemberIds(group),\n * getOnlineUserIds: () => presence.getOnlineIds(),\n * }\n * ```\n *\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport interface MentionResolver {\n /**\n * Resolve a `@name` token to a user record.\n *\n * @param name - The token after the `@` symbol (case-insensitive lookup).\n * @returns User record, or null if the name is not found.\n */\n resolveUser(name: string): { userId: string; name: string } | null;\n /**\n * Resolve a group mention token to the list of member user IDs.\n *\n * @param group - The well-known group name (e.g. `\"agent\"`, `\"all\"`).\n * @returns Array of member user IDs belonging to the group.\n */\n resolveGroup(group: MentionGroup): string[];\n /** Return the IDs of users currently online (used by the `here` and `humans_here` groups). */\n getOnlineUserIds(): string[];\n}\n\n// ---------------------------------------------------------------------------\n// parseMentions\n// ---------------------------------------------------------------------------\n\nconst MENTION_RE = /@(\\w+)/g;\nconst MENTION_GROUP_SET: ReadonlySet<string> = new Set(MENTION_GROUPS);\n\n/**\n * Scan `text` for @token patterns and return resolved {@link Mention} objects.\n *\n * - Well-known group tokens (`agent`, `humans`, `humans_here`, `here`, `all`)\n * are resolved via {@link MentionResolver.resolveGroup}.\n * - All other tokens are resolved via {@link MentionResolver.resolveUser}.\n * - Tokens that cannot be resolved are silently ignored.\n * - Results are sorted by character offset ascending.\n *\n * @param text - Raw message text to scan for `@token` patterns.\n * @param resolver - Resolver that maps tokens to users and groups.\n * @returns Resolved mentions ordered by position in text.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function parseMentions(text: string, resolver: MentionResolver): Mention[] {\n const mentions: Mention[] = [];\n\n for (const match of text.matchAll(MENTION_RE)) {\n const token = match[1]!;\n const offset = match.index!;\n const length = match[0].length; // includes the @\n\n if (MENTION_GROUP_SET.has(token)) {\n const group = token as MentionGroup;\n mentions.push({\n type: \"group\",\n group,\n resolved: resolver.resolveGroup(group),\n offset,\n length,\n });\n } else {\n const user = resolver.resolveUser(token);\n if (user) {\n mentions.push({\n type: \"user\",\n userId: user.userId,\n name: user.name,\n offset,\n length,\n });\n }\n }\n }\n\n // matchAll iterates left-to-right, so mentions are already offset-ascending.\n return mentions;\n}\n\n// ---------------------------------------------------------------------------\n// isMentioned\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if `userId` is directly mentioned or is a member of any\n * group mention's resolved list.\n *\n * @param userId - The user ID to check (e.g. from the auth context).\n * @param mentions - Parsed mentions from {@link parseMentions}.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function isMentioned(userId: string, mentions: Mention[]): boolean {\n for (const m of mentions) {\n if (m.type === \"user\" && m.userId === userId) return true;\n if (m.type === \"group\" && m.resolved.includes(userId)) return true;\n if (m.type === \"custom\" && m.resolved.includes(userId)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// isAddressedByKind\n// ---------------------------------------------------------------------------\n\n/**\n * True if any mention is a custom mention of the given kind.\n *\n * Consumers use this to check for platform-specific mention kinds (e.g.\n * `\"assistant\"`) that the SDK parser does not produce — only consumers\n * construct custom mentions.\n *\n * @param mentions - Mention list from the message or consumer-constructed.\n * @param kind - The consumer-defined kind string to match.\n */\nexport function isAddressedByKind(mentions: Mention[], kind: string): boolean {\n return mentions.some((m) => m.type === \"custom\" && m.kind === kind);\n}\n\n// ---------------------------------------------------------------------------\n// isAgentAddressed\n// ---------------------------------------------------------------------------\n\n/** Groups that include the agent in their resolved membership. */\nconst AGENT_GROUPS: ReadonlySet<MentionGroup> = new Set([\"agent\", \"here\", \"all\"]);\n\n/**\n * Returns true if the agent is addressed by any mention in the list.\n *\n * The groups `@agent`, `@here`, and `@all` address the agent. `@humans`\n * and `@humans_here` do not. Individual user mentions are also not matched\n * here — agent addressing is group-based.\n *\n * @param mentions - Parsed mentions from {@link parseMentions}.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function isAgentAddressed(mentions: Mention[]): boolean {\n return mentions.some((m) => m.type === \"group\" && AGENT_GROUPS.has(m.group));\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../session/src/dispatcher.ts","../session/src/mentions.ts"],"names":[],"mappings":"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","file":"chunk-UPZGQZEJ.js","sourcesContent":["import type { AgentClient } from \"@skaile/workspaces/client\";\nimport type {\n AgentCommand,\n AgentEvent,\n ConnectorRequestCommand,\n ConnectorResponseEvent,\n Mention,\n ReplyRef,\n} from \"@skaile/workspaces/types\";\nimport type { Message, MessageStore, SubscriberInfo, SubscriberTransport } from \"./types.js\";\n\n/**\n * Construction options for {@link SessionDispatcher}.\n *\n * All three infrastructure objects (`client`, `store`, `transport`) are\n * injected by the caller so the dispatcher remains testable and framework-agnostic.\n *\n * @docLink packages/session/api-reference#session-dispatcher\n */\nexport interface SessionDispatcherOptions {\n /** Unique identifier for the session this dispatcher manages. */\n sessionId: string;\n /** Connected agent client used to send commands and receive events. */\n client: AgentClient;\n /** Persistence layer for all non-transient messages. */\n store: MessageStore;\n /** Fan-out transport for broadcasting events to connected frontends. */\n transport: SubscriberTransport;\n /**\n * When true, the dispatcher prepends conversation history to every prompt\n * command sent to the agent. Enable for agents without native session\n * continuity (e.g. mock mode). Agents with native multi-turn support\n * (Claude SDK, OMP) should set this to false to avoid duplicate context.\n * Default: false.\n */\n injectConversationHistory?: boolean;\n /** Max conversation history messages to include in enriched prompts (default: 50). */\n historyLimit?: number;\n /**\n * Optional callback to transform prompt text before forwarding to the agent.\n * The original (untransformed) prompt is persisted and broadcast to subscribers.\n * Applied after conversation history enrichment. Use this to inject context\n * (e.g. UI state) that the agent should see but subscribers should not.\n */\n transformPrompt?: (prompt: string, senderId: string | undefined, replyTo?: ReplyRef) => string;\n}\n\n/**\n * Command and event types classified as transient RPC — high-frequency,\n * ephemeral, and not part of the conversation history.\n *\n * Matching payloads are forwarded and broadcast but never persisted\n * or assigned a sequence number, preventing bloat in the message log.\n */\nconst TRANSIENT_TYPES = new Set([\n \"resource_request\",\n \"resource_response\",\n \"resources_available\",\n \"commands_available\",\n \"state_changed\",\n \"typing\",\n \"user_message\",\n // OMP session tracking — forwarded to subscribers but not persisted\n \"session_info\",\n // Structured log forwarding — high-volume; persisted in per-session SQLite, not the message store\n \"log_entry\",\n // Generic connector dispatch — system RPC, not chat messages.\n // Replaces the 6 typed flow commands removed in Protocol 2.2 (Phase 4 of the\n // flow-connector extraction). `connector_mutate` against the `flow` connector\n // is now how hosts drive flow execution between turns.\n \"connector_mutate\",\n \"connector_query\",\n \"connector_query_response\",\n]);\n\n/**\n * Bridges a single agent session between an {@link AgentClient} and one or more\n * frontend subscribers.\n *\n * Responsibilities:\n * - Persists every non-transient command and event as a {@link Message} with a\n * monotonic sequence number via the injected {@link MessageStore}.\n * - Fans agent events out to all connected frontends via the injected\n * {@link SubscriberTransport}.\n * - Optionally enriches prompt commands with conversation history so agents\n * without native multi-turn support maintain context across turns.\n *\n * **Lifecycle:** call {@link init} before any other method, and {@link disconnect}\n * when the session ends.\n *\n * @example\n * ```ts\n * const dispatcher = new SessionDispatcher({\n * sessionId: 'sess-123',\n * client: agentClient,\n * store: prismaMessageStore,\n * transport: trpcTransport,\n * })\n * await dispatcher.init()\n * const history = await dispatcher.subscribe('ws-conn-abc', { limit: 50 })\n * const msg = await dispatcher.sendCommand({ type: 'prompt', prompt: 'Hello' }, 'user-42')\n * await dispatcher.disconnect()\n * ```\n *\n * @docLink packages/session/api-reference#session-dispatcher\n */\nexport class SessionDispatcher {\n /** The session identifier this dispatcher is bound to. */\n readonly sessionId: string;\n private client: AgentClient;\n private store: MessageStore;\n private transport: SubscriberTransport;\n private subscribers: Set<string> = new Set();\n private subscriberMeta: Map<string, SubscriberInfo> = new Map();\n private seq: number = 0;\n private _availableResources: { mounts: unknown[]; connectors: unknown[] } = {\n mounts: [],\n connectors: [],\n };\n\n /** Whether to inject conversation history into prompt commands. */\n private readonly injectConversationHistory: boolean;\n /** Maximum number of history messages to include in enriched prompts. */\n private readonly historyLimit: number;\n /** Optional prompt transformer applied before forwarding (after persistence). */\n private _transformPrompt?: (\n prompt: string,\n senderId: string | undefined,\n replyTo?: ReplyRef,\n ) => string;\n /** Whether the dispatcher is currently in compaction mode. */\n private _compacting = false;\n /** Accumulated text content during compaction. */\n private _compactionText = \"\";\n\n /**\n * One-shot waiters registered via {@link onceNextFinished}. Each is invoked\n * exactly once with the text of the next `finished` assistant event, then\n * the list is cleared. Backs the A2A router's synchronous `ask` answer\n * capture.\n */\n private nextFinishedWaiters: Array<(text: string) => void> = [];\n /**\n * Text streamed by the agent since the last `finished` event. Used as the\n * fallback answer text for {@link onceNextFinished} when a `finished` event\n * carries an empty `summary` (drivers that stream `text` chunks and leave\n * the summary blank).\n */\n private _turnText = \"\";\n\n /**\n * @param opts - Dispatcher configuration — see {@link SessionDispatcherOptions}.\n */\n constructor(opts: SessionDispatcherOptions) {\n this.sessionId = opts.sessionId;\n this.client = opts.client;\n this.store = opts.store;\n this.transport = opts.transport;\n this.injectConversationHistory = opts.injectConversationHistory ?? false;\n this.historyLimit = opts.historyLimit ?? 50;\n this._transformPrompt = opts.transformPrompt;\n }\n\n /**\n * Replace the prompt transformer at runtime.\n *\n * Useful when additional context (e.g. a UI state cache) becomes available\n * after the dispatcher is constructed but before the first prompt is sent.\n */\n set transformPrompt(fn:\n | ((prompt: string, senderId: string | undefined, replyTo?: ReplyRef) => string)\n | undefined,) {\n this._transformPrompt = fn;\n }\n\n /**\n * Initialize the dispatcher for this session.\n *\n * Restores the monotonic sequence counter from the store and wires up the\n * agent event listener. Must be called once before any other method.\n *\n * @throws If {@link MessageStore.getLatestSeq} rejects.\n */\n async init(): Promise<void> {\n this.seq = await this.store.getLatestSeq(this.sessionId);\n this.client.onEvent((event: AgentEvent) => {\n void this.handleAgentEvent(event);\n });\n }\n\n /**\n * Register a frontend subscriber and return recent message history.\n *\n * Subsequent agent events are pushed to this subscriber via the\n * {@link SubscriberTransport} until {@link unsubscribe} is called.\n *\n * @param subscriberId - Opaque connection ID (e.g. WebSocket session ID).\n * @param opts - Optional subscription options.\n * @param opts.limit - Number of recent messages to return (default: 50).\n * @returns Recent messages ordered by seq ascending.\n */\n async subscribe(\n subscriberId: string,\n opts?: { limit?: number; userId?: string; roles?: string[] },\n ): Promise<Message[]> {\n this.subscribers.add(subscriberId);\n if (opts?.userId !== undefined || opts?.roles !== undefined) {\n this.subscriberMeta.set(subscriberId, {\n userId: opts.userId,\n roles: opts.roles,\n });\n }\n return this.store.getMessages(this.sessionId, {\n limit: opts?.limit ?? 50,\n });\n }\n\n /**\n * Remove a frontend subscriber and release transport resources.\n *\n * @param subscriberId - The ID passed to {@link subscribe}.\n */\n unsubscribe(subscriberId: string): void {\n this.subscribers.delete(subscriberId);\n this.subscriberMeta.delete(subscriberId);\n this.transport.remove(subscriberId);\n }\n\n /**\n * Send a command from a frontend to the agent.\n *\n * Persistent commands are wrapped in a Message envelope and stored before\n * forwarding. Transient commands (resource RPC) are forwarded directly\n * without persistence — returns null in that case.\n *\n * For `prompt` commands, the original message is persisted, but an enriched\n * version (with conversation history prepended) is sent to the agent so it\n * maintains context across turns.\n *\n * @param command - The command to dispatch to the agent (a prompt or tool result).\n * @param senderId - ID of the user sending the command, if known.\n * @param mentions - Resolved mention objects extracted from the prompt text.\n * @param visibility - Optional platform visibility envelope (F-23). When\n * `visibilityMode` is `HumansOnly`, or `Private` without the `\"__agent__\"`\n * sentinel in `privateRecipientIds` (B-30), the message is persisted and\n * broadcast to subscribers but NOT forwarded to the agent.\n * @param outgoingPrefix - Optional text prepended to the outgoing command's\n * user-text field ({@link command.prompt} / `command.answer` /\n * `command.content`) AFTER persistence but BEFORE history enrichment and\n * {@link transformPrompt}. The persisted {@link Message} envelope is built\n * from the original `command` and never sees the prefix. Use this to\n * inject one-shot wake-time context (Option B1 pending restoration\n * prompt) that the agent must see but UI subscribers must not.\n * @returns The persisted Message envelope, or null for transient command types.\n */\n async sendCommand(\n command: AgentCommand,\n senderId?: string,\n mentions?: Mention[],\n visibility?: NonNullable<Message[\"visibility\"]>,\n outgoingPrefix?: string,\n ): Promise<Message | null> {\n if (!this.shouldPersist(command)) {\n this.client.send(command);\n return null;\n }\n // Persist the original command (unenriched/untransformed/un-prefixed).\n // `outgoingPrefix` is intentionally NOT applied here — it must only\n // reach the agent client, never the MessageStore.\n const message = this.createMessage(command, senderId);\n // @TODO: Code-Review: v1 back-compat — mentions field no longer used by Protocol v2+ persistence layers; remove once platform PrismaMessageStore is migrated\n if (mentions?.length) message.mentions = mentions;\n // F-23: platform-extension visibility metadata (3.2.0). The platform\n // MessageStore reads this off the envelope and writes the\n // Message.visibilityMode column + recipient rows atomically with\n // the message itself — no post-send patch, no race window where a\n // concurrent send could mis-tag the wrong message id.\n if (visibility) message.visibility = visibility;\n await this.store.append(message);\n\n // Build the outgoing command — enrich and transform prompt commands\n // while leaving the persisted message untouched.\n let outgoing: AgentCommand = command;\n if (command.type === \"prompt\") {\n let prompt = command.prompt;\n // Restoration prefix lands FIRST (innermost) so the user's typed\n // text remains adjacent to its own framing, with history and reply\n // preambles wrapping the combined block from the outside in.\n if (outgoingPrefix) {\n prompt = `${outgoingPrefix}${prompt}`;\n }\n if (this.injectConversationHistory) {\n prompt = await this.enrichPromptWithHistory(prompt);\n }\n if (this._transformPrompt) {\n prompt = this._transformPrompt(prompt, senderId, command.replyTo);\n }\n if (prompt !== command.prompt) {\n outgoing = { ...command, prompt };\n }\n } else if (outgoingPrefix && command.type === \"reply\") {\n // `reply` carries user text in `answer`. Prefix is invisible to\n // subscribers (who see the persisted Message.payload.answer) but\n // visible to the agent driver.\n outgoing = { ...command, answer: `${outgoingPrefix}${command.answer}` };\n } else if (outgoingPrefix && command.type === \"custom_message\") {\n // `custom_message` carries user text in `content`. Same persist/send\n // split as the `reply` branch above.\n outgoing = { ...command, content: `${outgoingPrefix}${command.content}` };\n }\n\n // F-23 P3 review (B1) — HumansOnly visibility means the message is\n // intended for human recipients only; the agent must NOT see the\n // prompt content. Persist + broadcast (humans read it via\n // SubscriberTransport) but skip the agent forward. This closes a\n // critical privacy hole where `@user: secret` was persisted with\n // visibilityMode='HumansOnly' yet still reached the LLM. Threads\n // starting HumansOnly stay agent-invisible even on Public follow-ups\n // (parent-thread visibility threading is intentionally out of scope\n // for v1; only the immediate message is gated). See\n // platform/_concept/plans/f-23-p3-review-blockers.md.\n if (visibility?.visibilityMode === \"HumansOnly\") {\n return message;\n }\n\n // B-30 — Private visibility (F-23 `@mention_` underscore suffix) means the\n // message is addressed to an explicit recipient set. The agent is only a\n // recipient when the sender wrote `@agent_`, which the platform encodes as\n // the `\"__agent__\"` sentinel in `privateRecipientIds`. When the sentinel is\n // absent the message is a private human-to-human exchange: persist +\n // broadcast (the named human recipients read it via SubscriberTransport)\n // but skip the agent forward so the LLM never sees private content.\n if (\n visibility?.visibilityMode === \"Private\" &&\n !visibility.privateRecipientIds?.includes(\"__agent__\")\n ) {\n return message;\n }\n\n this.client.send(outgoing);\n return message;\n }\n\n /**\n * Persist a synthetic event (not received from the agent) and broadcast it.\n *\n * Used by the platform to inject events like a partial-text `finished` record\n * before a cancel command.\n *\n * @param event - The agent event to persist.\n * @param mentions - Resolved mentions from the originating command.\n */\n async persistEvent(event: AgentEvent, mentions?: Mention[]): Promise<Message | null> {\n return this.handleAgentEvent(event, mentions);\n }\n\n /**\n * Load paginated message history for this session.\n *\n * @param opts.before - Return messages with seq strictly less than this value.\n * @param opts.limit - Maximum number of messages to return.\n */\n async getMessages(opts?: { before?: number; limit?: number }): Promise<Message[]> {\n return this.store.getMessages(this.sessionId, opts);\n }\n\n /**\n * Available mounts and connectors captured from the most recent\n * `resources_available` event emitted by the agent on connect.\n *\n * Empty until the agent emits the event or {@link seedResources} is called.\n */\n get availableResources(): { mounts: unknown[]; connectors: unknown[] } {\n return this._availableResources;\n }\n\n /**\n * Seed the available resources from an external cache.\n *\n * This is needed when the dispatcher is created after the agent has already\n * emitted `resources_available` on WebSocket connect. Without seeding, the\n * dispatcher would report an empty resource list until the next reconnect.\n *\n * @param resources - Resource handles to register before the session starts.\n */\n seedResources(resources: { mounts: unknown[]; connectors: unknown[] }): void {\n const empty =\n this._availableResources.mounts.length === 0 &&\n this._availableResources.connectors.length === 0;\n if (empty && (resources.mounts.length > 0 || resources.connectors.length > 0)) {\n this._availableResources = resources;\n }\n }\n\n /**\n * Deliver a prompt to the agent that triggers a turn but is NOT persisted.\n *\n * Unlike {@link sendCommand}, this never touches the {@link MessageStore}:\n * the prompt is forwarded straight to the agent client. The caller owns\n * persistence of whatever conversation rows should represent this turn.\n *\n * Used by the platform's A2A router to deliver a `from-peer` prompt to a\n * target session: the router writes the authoritative A2A `Message` rows\n * itself, so persisting the framed prompt here too would create an extra,\n * un-attributed plain `prompt` row. The dispatcher's job for this prompt is\n * delivery only.\n *\n * The agent still produces a normal turn, so an A2A `ask` round-trip can\n * capture the answer via {@link onceNextFinished}.\n *\n * @param prompt - The fully-formed prompt text to deliver to the agent.\n */\n deliverPrompt(prompt: string): void {\n this.client.send({ type: \"prompt\", prompt });\n }\n\n /**\n * Send a resource request and await the correlated response.\n *\n * This is a convenience method for request/response resource RPC.\n * The request is transient (not persisted). A one-shot event listener\n * waits for the matching `resource_response` by `requestId`.\n *\n * @param command - A ConnectorRequestCommand (caller must set requestId).\n * @param timeoutMs - How long to wait before rejecting (default: 30s).\n */\n async requestResource(\n command: ConnectorRequestCommand,\n timeoutMs = 30_000,\n ): Promise<ConnectorResponseEvent> {\n return new Promise<ConnectorResponseEvent>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.client.offEvent(handler);\n reject(\n new Error(\n `Resource request timed out after ${timeoutMs}ms: ${command.operation} ${command.connectorId}`,\n ),\n );\n }, timeoutMs);\n\n const handler = (event: AgentEvent) => {\n if (event.type === \"resource_response\" && event.requestId === command.requestId) {\n clearTimeout(timer);\n this.client.offEvent(handler);\n resolve(event);\n }\n };\n\n this.client.onEvent(handler);\n this.client.send(command);\n });\n }\n\n /**\n * Broadcast a transient event to all subscribers without persisting it.\n *\n * Used for ephemeral signals like typing indicators or user-command\n * echoes that other subscribers need to see in real time.\n */\n broadcast(event: AgentEvent): void {\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n }\n\n /** Disconnect the agent client and clean up all subscribers. */\n async disconnect(): Promise<void> {\n for (const id of this.subscribers) {\n this.transport.remove(id);\n }\n this.subscribers.clear();\n this.subscriberMeta.clear();\n // Clear any pending A2A waiters so they don't leak if the session\n // disconnects before the target ever emits a `finished` event.\n this.nextFinishedWaiters = [];\n await this.client.dispose();\n }\n\n /**\n * Enter compaction mode. While active, events from the agent are captured\n * but NOT persisted. Text content is accumulated for the snapshot.\n */\n beginCompaction(): void {\n this._compacting = true;\n this._compactionText = \"\";\n }\n\n /**\n * Exit compaction mode and return the accumulated text.\n * The caller is responsible for creating and emitting the snapshot event.\n */\n endCompaction(): string {\n this._compacting = false;\n const text = this._compactionText;\n this._compactionText = \"\";\n return text;\n }\n\n /** Whether the dispatcher is currently in compaction mode. */\n get isCompacting(): boolean {\n return this._compacting;\n }\n\n /**\n * Register a one-shot callback fired with the text of this session's next\n * finished assistant turn. Returns an unsubscribe function.\n *\n * Semantics:\n * - The callback fires **at most once** — on the first `finished` event\n * observed after registration. Later finished turns do not re-fire it.\n * - A waiter registered after a `finished` event waits for the *following*\n * one. The hook captures the *next* finished turn, not a past one.\n * - The answer text is the `finished` event's `summary`; when that is\n * empty (drivers that stream `text` and leave the summary blank), the\n * text accumulated since the previous finished turn is used instead.\n * - Calling the returned function removes the waiter; if it has already\n * fired this is a no-op.\n *\n * Used by the platform's A2A router (`A2ARouterService.ask`) to capture a\n * peer session's answer to a `from-peer` question. v1 captures the first\n * finished turn after delivery — see the busy-target notes in the A2A\n * design spec for the known turn-matching limitation.\n *\n * @param cb - Invoked once with the next finished turn's text.\n * @returns Unsubscribe function that removes this waiter.\n */\n onceNextFinished(cb: (text: string) => void): () => void {\n this.nextFinishedWaiters.push(cb);\n return () => {\n const idx = this.nextFinishedWaiters.indexOf(cb);\n if (idx !== -1) {\n this.nextFinishedWaiters.splice(idx, 1);\n }\n };\n }\n\n // -- Private ----------------------------------------------------------------\n\n /**\n * Build an enriched prompt by prepending conversation history.\n *\n * Loads recent persisted messages, formats user/assistant turns, and\n * wraps them in a `<conversation_history>` block. The current user\n * message (just persisted) is excluded since it is already the prompt.\n *\n * Each turn is rendered as a self-closing `<turn role=\"...\">` element\n * rather than a bare `[User]:` / `[Assistant]:` transcript line. A plain\n * role-prefixed transcript invites the model to *continue* it — completing\n * the next `[User]:` turn as plaintext inside its own answer (a fabricated\n * follow-up that swallows the real next message). XML-tagged, explicitly\n * read-only turns remove that completion affordance while preserving the\n * same information.\n *\n * Returns the original prompt unchanged if there is no prior history.\n */\n private async enrichPromptWithHistory(currentPrompt: string): Promise<string> {\n const messages = await this.store.getMessages(this.sessionId, {\n limit: this.historyLimit,\n });\n\n // Filter to conversation-relevant types and exclude the message we just appended\n const history = messages\n .filter((m) => {\n const type = m.payload.type;\n return type === \"prompt\" || type === \"finished\" || type === \"question\";\n })\n .slice(0, -1); // Exclude the current message (last by seq)\n\n if (history.length === 0) {\n return currentPrompt;\n }\n\n // Escape so message content (which may itself contain `</turn>` or other\n // angle brackets) cannot break out of the XML structure or inject a forged\n // turn. The model reads entity-encoded text fine; this is read-only context.\n const esc = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n\n const turns = history.map((m) => {\n const p = m.payload as Record<string, unknown>;\n if (p.type === \"prompt\") {\n return `<turn role=\"user\">${esc(String(p.prompt ?? \"\"))}</turn>`;\n }\n // finished (assistant response) or question\n const content = String(p.summary ?? p.question ?? \"\");\n return `<turn role=\"assistant\">${esc(content)}</turn>`;\n });\n\n return (\n \"<conversation_history>\\n\" +\n \"The following is prior conversation context, provided read-only so you \" +\n \"have continuity. Do NOT continue, echo, or complete it — it is history, \" +\n \"not a turn for you to extend. Respond only to the user message that \" +\n \"follows the closing tag.\\n\" +\n `${turns.join(\"\\n\")}\\n` +\n \"</conversation_history>\\n\\n\" +\n currentPrompt\n );\n }\n\n private async handleAgentEvent(event: AgentEvent, mentions?: Mention[]): Promise<Message | null> {\n // ── Compaction mode: capture text but don't persist ─────────────────\n if (this._compacting) {\n if (event.type === \"text\") {\n this._compactionText += (event as { content: string }).content;\n }\n // Still broadcast to subscribers (UI shows compaction progress)\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n return null;\n }\n\n // Capture resource metadata when the server announces available resources\n if (event.type === \"resources_available\") {\n const payload = event as { mounts?: unknown[]; connectors?: unknown[] };\n this._availableResources = {\n mounts: payload.mounts ?? [],\n connectors: payload.connectors ?? [],\n };\n }\n\n // ── onceNextFinished — one-shot \"next finished turn\" answer capture ──\n // Accumulate streamed text across the turn so the answer is available\n // even when a driver emits an empty `finished.summary`. On `finished`,\n // drain every queued waiter exactly once, then reset the accumulator.\n if (event.type === \"text\") {\n this._turnText += (event as { content: string }).content;\n } else if (event.type === \"finished\") {\n const summary = (event as { summary?: string }).summary ?? \"\";\n const answer = summary.length > 0 ? summary : this._turnText;\n this._turnText = \"\";\n if (this.nextFinishedWaiters.length > 0) {\n const waiters = this.nextFinishedWaiters;\n this.nextFinishedWaiters = [];\n for (const cb of waiters) {\n cb(answer);\n }\n }\n }\n\n let message: Message | null = null;\n if (this.shouldPersist(event)) {\n message = this.createMessage(event);\n if (mentions?.length) message.mentions = mentions;\n await this.store.append(message);\n }\n\n for (const subscriberId of this.subscribers) {\n this.transport.send(subscriberId, event);\n }\n return message;\n }\n\n private shouldPersist(payload: AgentCommand | AgentEvent): boolean {\n return !TRANSIENT_TYPES.has(payload.type);\n }\n\n private createMessage(payload: AgentCommand | AgentEvent, senderId?: string): Message {\n this.seq++;\n return {\n id: crypto.randomUUID(),\n seq: this.seq,\n timestamp: new Date().toISOString(),\n sessionId: this.sessionId,\n ...(senderId ? { senderId } : {}),\n payload,\n };\n }\n}\n","import type { Mention, MentionGroup } from \"@skaile/workspaces/types\";\nimport { MENTION_GROUPS } from \"@skaile/workspaces/types\";\n\n/**\n * Re-exported from `@skaile/workspaces/types` so consumers can import mention\n * types directly from `@skaile/workspaces/session` without a separate dependency.\n */\nexport type { Mention, MentionGroup };\n\n// ---------------------------------------------------------------------------\n// MentionResolver\n// ---------------------------------------------------------------------------\n\n/**\n * Consumer-provided resolver that maps @mention tokens to users and groups.\n *\n * The platform provides an implementation backed by Prisma and the live\n * presence service. Tests use lightweight mocks.\n *\n * @example\n * ```ts\n * const resolver: MentionResolver = {\n * resolveUser: (name) => db.users.findByName(name),\n * resolveGroup: (group) => presence.getMemberIds(group),\n * getOnlineUserIds: () => presence.getOnlineIds(),\n * }\n * ```\n *\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport interface MentionResolver {\n /**\n * Resolve a `@name` token to a user record.\n *\n * @param name - The token after the `@` symbol (case-insensitive lookup).\n * @returns User record, or null if the name is not found.\n */\n resolveUser(name: string): { userId: string; name: string } | null;\n /**\n * Resolve a group mention token to the list of member user IDs.\n *\n * @param group - The well-known group name (e.g. `\"agent\"`, `\"all\"`).\n * @returns Array of member user IDs belonging to the group.\n */\n resolveGroup(group: MentionGroup): string[];\n /** Return the IDs of users currently online (used by the `here` and `humans_here` groups). */\n getOnlineUserIds(): string[];\n}\n\n// ---------------------------------------------------------------------------\n// parseMentions\n// ---------------------------------------------------------------------------\n\nconst MENTION_RE = /@(\\w+)/g;\nconst MENTION_GROUP_SET: ReadonlySet<string> = new Set(MENTION_GROUPS);\n\n/**\n * Scan `text` for @token patterns and return resolved {@link Mention} objects.\n *\n * - Well-known group tokens (`agent`, `humans`, `humans_here`, `here`, `all`)\n * are resolved via {@link MentionResolver.resolveGroup}.\n * - All other tokens are resolved via {@link MentionResolver.resolveUser}.\n * - Tokens that cannot be resolved are silently ignored.\n * - Results are sorted by character offset ascending.\n *\n * @param text - Raw message text to scan for `@token` patterns.\n * @param resolver - Resolver that maps tokens to users and groups.\n * @returns Resolved mentions ordered by position in text.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function parseMentions(text: string, resolver: MentionResolver): Mention[] {\n const mentions: Mention[] = [];\n\n for (const match of text.matchAll(MENTION_RE)) {\n const token = match[1]!;\n const offset = match.index!;\n const length = match[0].length; // includes the @\n\n if (MENTION_GROUP_SET.has(token)) {\n const group = token as MentionGroup;\n mentions.push({\n type: \"group\",\n group,\n resolved: resolver.resolveGroup(group),\n offset,\n length,\n });\n } else {\n const user = resolver.resolveUser(token);\n if (user) {\n mentions.push({\n type: \"user\",\n userId: user.userId,\n name: user.name,\n offset,\n length,\n });\n }\n }\n }\n\n // matchAll iterates left-to-right, so mentions are already offset-ascending.\n return mentions;\n}\n\n// ---------------------------------------------------------------------------\n// isMentioned\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if `userId` is directly mentioned or is a member of any\n * group mention's resolved list.\n *\n * @param userId - The user ID to check (e.g. from the auth context).\n * @param mentions - Parsed mentions from {@link parseMentions}.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function isMentioned(userId: string, mentions: Mention[]): boolean {\n for (const m of mentions) {\n if (m.type === \"user\" && m.userId === userId) return true;\n if (m.type === \"group\" && m.resolved.includes(userId)) return true;\n if (m.type === \"custom\" && m.resolved.includes(userId)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// isAddressedByKind\n// ---------------------------------------------------------------------------\n\n/**\n * True if any mention is a custom mention of the given kind.\n *\n * Consumers use this to check for platform-specific mention kinds (e.g.\n * `\"assistant\"`) that the SDK parser does not produce — only consumers\n * construct custom mentions.\n *\n * @param mentions - Mention list from the message or consumer-constructed.\n * @param kind - The consumer-defined kind string to match.\n */\nexport function isAddressedByKind(mentions: Mention[], kind: string): boolean {\n return mentions.some((m) => m.type === \"custom\" && m.kind === kind);\n}\n\n// ---------------------------------------------------------------------------\n// isAgentAddressed\n// ---------------------------------------------------------------------------\n\n/** Groups that include the agent in their resolved membership. */\nconst AGENT_GROUPS: ReadonlySet<MentionGroup> = new Set([\"agent\", \"here\", \"all\"]);\n\n/**\n * Returns true if the agent is addressed by any mention in the list.\n *\n * The groups `@agent`, `@here`, and `@all` address the agent. `@humans`\n * and `@humans_here` do not. Individual user mentions are also not matched\n * here — agent addressing is group-based.\n *\n * @param mentions - Parsed mentions from {@link parseMentions}.\n * @docLink packages/session/api-reference#mention-resolver\n */\nexport function isAgentAddressed(mentions: Mention[]): boolean {\n return mentions.some((m) => m.type === \"group\" && AGENT_GROUPS.has(m.group));\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { classifyGenericErrorMessage } from './chunk-GAH4YRCI.js';
|
|
2
|
-
import { AgentDriver } from './chunk-
|
|
2
|
+
import { AgentDriver } from './chunk-TNSCHS4I.js';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
4
|
import { createInterface } from 'readline';
|
|
5
5
|
|
|
@@ -441,5 +441,5 @@ function mapApiKeysToEnv(apiKeys) {
|
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
export { SubprocessCliDriver, extractCost, extractText, extractTokens, mapApiKeysToEnv };
|
|
444
|
-
//# sourceMappingURL=chunk-
|
|
445
|
-
//# sourceMappingURL=chunk-
|
|
444
|
+
//# sourceMappingURL=chunk-UQ5FK2AB.js.map
|
|
445
|
+
//# sourceMappingURL=chunk-UQ5FK2AB.js.map
|