open-multi-agent-kit 1.0.0 → 1.2.2
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 +52 -1
- package/README.md +2 -2
- package/dist/approval-api.d.ts +5 -0
- package/dist/approval-api.d.ts.map +1 -0
- package/dist/approval-api.js +5 -0
- package/dist/approval-api.js.map +1 -0
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +1 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/session-picker.d.ts +2 -2
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/commands/verified-run-cli.d.ts.map +1 -1
- package/dist/commands/verified-run-cli.js +69 -22
- package/dist/commands/verified-run-cli.js.map +1 -1
- package/dist/commands/verified-run-signal.d.ts +3 -0
- package/dist/commands/verified-run-signal.d.ts.map +1 -0
- package/dist/commands/verified-run-signal.js +15 -0
- package/dist/commands/verified-run-signal.js.map +1 -0
- package/dist/coordination/awareness.d.ts +32 -0
- package/dist/coordination/awareness.d.ts.map +1 -0
- package/dist/coordination/awareness.js +55 -0
- package/dist/coordination/awareness.js.map +1 -0
- package/dist/coordination/broker.d.ts +79 -0
- package/dist/coordination/broker.d.ts.map +1 -0
- package/dist/coordination/broker.js +202 -0
- package/dist/coordination/broker.js.map +1 -0
- package/dist/coordination/index.d.ts +16 -0
- package/dist/coordination/index.d.ts.map +1 -0
- package/dist/coordination/index.js +16 -0
- package/dist/coordination/index.js.map +1 -0
- package/dist/coordination/integration.d.ts +59 -0
- package/dist/coordination/integration.d.ts.map +1 -0
- package/dist/coordination/integration.js +126 -0
- package/dist/coordination/integration.js.map +1 -0
- package/dist/coordination/operation.d.ts +107 -0
- package/dist/coordination/operation.d.ts.map +1 -0
- package/dist/coordination/operation.js +231 -0
- package/dist/coordination/operation.js.map +1 -0
- package/dist/coordination/resource.d.ts +33 -0
- package/dist/coordination/resource.d.ts.map +1 -0
- package/dist/coordination/resource.js +95 -0
- package/dist/coordination/resource.js.map +1 -0
- package/dist/coordination/session.d.ts +74 -0
- package/dist/coordination/session.d.ts.map +1 -0
- package/dist/coordination/session.js +143 -0
- package/dist/coordination/session.js.map +1 -0
- package/dist/coordination/types.d.ts +68 -0
- package/dist/coordination/types.d.ts.map +1 -0
- package/dist/coordination/types.js +26 -0
- package/dist/coordination/types.js.map +1 -0
- package/dist/core/adaptorch-bridge.d.ts +1 -1
- package/dist/core/adaptorch-bridge.d.ts.map +1 -1
- package/dist/core/adaptorch-bridge.js +1 -1
- package/dist/core/adaptorch-bridge.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +16 -16
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +35 -15
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/canonical-json.d.ts +3 -0
- package/dist/core/canonical-json.d.ts.map +1 -0
- package/dist/core/canonical-json.js +20 -0
- package/dist/core/canonical-json.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +7 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +14 -29
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/control-state.d.ts +41 -0
- package/dist/core/compaction/control-state.d.ts.map +1 -0
- package/dist/core/compaction/control-state.js +85 -0
- package/dist/core/compaction/control-state.js.map +1 -0
- package/dist/core/compaction/fallback.d.ts +71 -0
- package/dist/core/compaction/fallback.d.ts.map +1 -0
- package/dist/core/compaction/fallback.js +118 -0
- package/dist/core/compaction/fallback.js.map +1 -0
- package/dist/core/compaction/index.d.ts +2 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +2 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/model-failover.d.ts +21 -0
- package/dist/core/compaction/model-failover.d.ts.map +1 -0
- package/dist/core/compaction/model-failover.js +46 -0
- package/dist/core/compaction/model-failover.js.map +1 -0
- package/dist/core/compaction/session-summary.d.ts +21 -0
- package/dist/core/compaction/session-summary.d.ts.map +1 -0
- package/dist/core/compaction/session-summary.js +27 -0
- package/dist/core/compaction/session-summary.js.map +1 -0
- package/dist/core/context-budget-headroom-candidates.d.ts +16 -1
- package/dist/core/context-budget-headroom-candidates.d.ts.map +1 -1
- package/dist/core/context-budget-headroom-candidates.js +31 -13
- package/dist/core/context-budget-headroom-candidates.js.map +1 -1
- package/dist/core/context-budget-headroom-types.d.ts +7 -1
- package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
- package/dist/core/context-budget-headroom-types.js +0 -1
- package/dist/core/context-budget-headroom-types.js.map +1 -1
- package/dist/core/context-budget-headroom.d.ts +10 -0
- package/dist/core/context-budget-headroom.d.ts.map +1 -1
- package/dist/core/context-budget-headroom.js +11 -2
- package/dist/core/context-budget-headroom.js.map +1 -1
- package/dist/core/context-budget-v2-global-pass.d.ts +21 -0
- package/dist/core/context-budget-v2-global-pass.d.ts.map +1 -0
- package/dist/core/context-budget-v2-global-pass.js +203 -0
- package/dist/core/context-budget-v2-global-pass.js.map +1 -0
- package/dist/core/context-budget-v2-planned-items.d.ts +12 -0
- package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planned-items.js +21 -2
- package/dist/core/context-budget-v2-planned-items.js.map +1 -1
- package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planner.js +18 -19
- package/dist/core/context-budget-v2-planner.js.map +1 -1
- package/dist/core/context-budget-v2-scoring.d.ts +7 -1
- package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
- package/dist/core/context-budget-v2-scoring.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts +3 -0
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +6 -2
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +6 -1
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js +6 -1
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/export-html/template.js +14 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/session-lifecycle-types.d.ts +14 -0
- package/dist/core/extensions/session-lifecycle-types.d.ts.map +1 -0
- package/dist/core/extensions/session-lifecycle-types.js +2 -0
- package/dist/core/extensions/session-lifecycle-types.js.map +1 -0
- package/dist/core/extensions/types.d.ts +4 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +10 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/legacy-resource-policy.d.ts +7 -0
- package/dist/core/legacy-resource-policy.d.ts.map +1 -0
- package/dist/core/legacy-resource-policy.js +9 -0
- package/dist/core/legacy-resource-policy.js.map +1 -0
- package/dist/core/mcp/config.d.ts.map +1 -1
- package/dist/core/mcp/config.js +17 -3
- package/dist/core/mcp/config.js.map +1 -1
- package/dist/core/mcp/connection-queue.d.ts +12 -0
- package/dist/core/mcp/connection-queue.d.ts.map +1 -0
- package/dist/core/mcp/connection-queue.js +46 -0
- package/dist/core/mcp/connection-queue.js.map +1 -0
- package/dist/core/mcp/manager-runtime.d.ts +55 -0
- package/dist/core/mcp/manager-runtime.d.ts.map +1 -0
- package/dist/core/mcp/manager-runtime.js +66 -0
- package/dist/core/mcp/manager-runtime.js.map +1 -0
- package/dist/core/mcp/manager.d.ts +12 -68
- package/dist/core/mcp/manager.d.ts.map +1 -1
- package/dist/core/mcp/manager.js +33 -113
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/mcp/protocol.d.ts.map +1 -1
- package/dist/core/mcp/protocol.js +70 -40
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +59 -129
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/package-resource-patterns.d.ts +7 -0
- package/dist/core/package-resource-patterns.d.ts.map +1 -0
- package/dist/core/package-resource-patterns.js +99 -0
- package/dist/core/package-resource-patterns.js.map +1 -0
- package/dist/core/provider-default-models.d.ts +1 -0
- package/dist/core/provider-default-models.d.ts.map +1 -1
- package/dist/core/provider-default-models.js +1 -0
- package/dist/core/provider-default-models.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +1 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/provider-resilience.d.ts +9 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +15 -0
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/reasoning-router-resolver.d.ts +7 -0
- package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
- package/dist/core/reasoning-router-resolver.js +21 -3
- package/dist/core/reasoning-router-resolver.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts +13 -7
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +8 -5
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +4 -8
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/run-execution-api.d.ts +8 -1
- package/dist/core/run-execution-api.d.ts.map +1 -1
- package/dist/core/run-execution-api.js +4 -0
- package/dist/core/run-execution-api.js.map +1 -1
- package/dist/core/run-journal.d.ts +1 -2
- package/dist/core/run-journal.d.ts.map +1 -1
- package/dist/core/run-journal.js +2 -19
- package/dist/core/run-journal.js.map +1 -1
- package/dist/core/session-compaction-service.d.ts +15 -0
- package/dist/core/session-compaction-service.d.ts.map +1 -1
- package/dist/core/session-compaction-service.js +17 -4
- package/dist/core/session-compaction-service.js.map +1 -1
- package/dist/core/session-listing.d.ts +51 -0
- package/dist/core/session-listing.d.ts.map +1 -0
- package/dist/core/session-listing.js +111 -0
- package/dist/core/session-listing.js.map +1 -0
- package/dist/core/session-manager.d.ts +12 -37
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +12 -118
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-message-text.d.ts +5 -0
- package/dist/core/session-message-text.d.ts.map +1 -0
- package/dist/core/session-message-text.js +9 -0
- package/dist/core/session-message-text.js.map +1 -0
- package/dist/core/skills-catalog-cache.d.ts +2 -0
- package/dist/core/skills-catalog-cache.d.ts.map +1 -1
- package/dist/core/skills-catalog-cache.js +71 -17
- package/dist/core/skills-catalog-cache.js.map +1 -1
- package/dist/core/todo-runtime-state.d.ts +12 -0
- package/dist/core/todo-runtime-state.d.ts.map +1 -1
- package/dist/core/todo-runtime-state.js +23 -0
- package/dist/core/todo-runtime-state.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/verified-run/authority-clock.d.ts +3 -0
- package/dist/core/verified-run/authority-clock.d.ts.map +1 -0
- package/dist/core/verified-run/authority-clock.js +25 -0
- package/dist/core/verified-run/authority-clock.js.map +1 -0
- package/dist/core/verified-run/authority-errors.d.ts +10 -0
- package/dist/core/verified-run/authority-errors.d.ts.map +1 -0
- package/dist/core/verified-run/authority-errors.js +17 -0
- package/dist/core/verified-run/authority-errors.js.map +1 -0
- package/dist/core/verified-run/authority-events.d.ts +126 -0
- package/dist/core/verified-run/authority-events.d.ts.map +1 -0
- package/dist/core/verified-run/authority-events.js +482 -0
- package/dist/core/verified-run/authority-events.js.map +1 -0
- package/dist/core/verified-run/authority-journal.d.ts +27 -0
- package/dist/core/verified-run/authority-journal.d.ts.map +1 -0
- package/dist/core/verified-run/authority-journal.js +83 -0
- package/dist/core/verified-run/authority-journal.js.map +1 -0
- package/dist/core/verified-run/authority-meaning.d.ts +13 -0
- package/dist/core/verified-run/authority-meaning.d.ts.map +1 -0
- package/dist/core/verified-run/authority-meaning.js +40 -0
- package/dist/core/verified-run/authority-meaning.js.map +1 -0
- package/dist/core/verified-run/authority-runtime.d.ts +82 -0
- package/dist/core/verified-run/authority-runtime.d.ts.map +1 -0
- package/dist/core/verified-run/authority-runtime.js +169 -0
- package/dist/core/verified-run/authority-runtime.js.map +1 -0
- package/dist/core/verified-run/authority-store.d.ts +204 -0
- package/dist/core/verified-run/authority-store.d.ts.map +1 -0
- package/dist/core/verified-run/authority-store.js +852 -0
- package/dist/core/verified-run/authority-store.js.map +1 -0
- package/dist/core/verified-run/broker.d.ts +8 -1
- package/dist/core/verified-run/broker.d.ts.map +1 -1
- package/dist/core/verified-run/broker.js +94 -90
- package/dist/core/verified-run/broker.js.map +1 -1
- package/dist/core/verified-run/candidate-policy.d.ts +10 -0
- package/dist/core/verified-run/candidate-policy.d.ts.map +1 -0
- package/dist/core/verified-run/candidate-policy.js +33 -0
- package/dist/core/verified-run/candidate-policy.js.map +1 -0
- package/dist/core/verified-run/candidate.d.ts.map +1 -1
- package/dist/core/verified-run/candidate.js +5 -10
- package/dist/core/verified-run/candidate.js.map +1 -1
- package/dist/core/verified-run/coordinator.d.ts +35 -17
- package/dist/core/verified-run/coordinator.d.ts.map +1 -1
- package/dist/core/verified-run/coordinator.js +111 -57
- package/dist/core/verified-run/coordinator.js.map +1 -1
- package/dist/core/verified-run/dag-phase.d.ts.map +1 -1
- package/dist/core/verified-run/dag-phase.js +1 -1
- package/dist/core/verified-run/dag-phase.js.map +1 -1
- package/dist/core/verified-run/dag-recovery.d.ts +5 -1
- package/dist/core/verified-run/dag-recovery.d.ts.map +1 -1
- package/dist/core/verified-run/dag-recovery.js +10 -4
- package/dist/core/verified-run/dag-recovery.js.map +1 -1
- package/dist/core/verified-run/event-parser.d.ts.map +1 -1
- package/dist/core/verified-run/event-parser.js +33 -0
- package/dist/core/verified-run/event-parser.js.map +1 -1
- package/dist/core/verified-run/evidence-read-projection.d.ts +4 -0
- package/dist/core/verified-run/evidence-read-projection.d.ts.map +1 -0
- package/dist/core/verified-run/evidence-read-projection.js +27 -0
- package/dist/core/verified-run/evidence-read-projection.js.map +1 -0
- package/dist/core/verified-run/evidence.d.ts +12 -1
- package/dist/core/verified-run/evidence.d.ts.map +1 -1
- package/dist/core/verified-run/evidence.js +51 -2
- package/dist/core/verified-run/evidence.js.map +1 -1
- package/dist/core/verified-run/git-candidate-preflight.d.ts +4 -0
- package/dist/core/verified-run/git-candidate-preflight.d.ts.map +1 -0
- package/dist/core/verified-run/git-candidate-preflight.js +34 -0
- package/dist/core/verified-run/git-candidate-preflight.js.map +1 -0
- package/dist/core/verified-run/git-effect-supervisor.d.ts +24 -0
- package/dist/core/verified-run/git-effect-supervisor.d.ts.map +1 -0
- package/dist/core/verified-run/git-effect-supervisor.js +174 -0
- package/dist/core/verified-run/git-effect-supervisor.js.map +1 -0
- package/dist/core/verified-run/git-execution.d.ts +27 -0
- package/dist/core/verified-run/git-execution.d.ts.map +1 -0
- package/dist/core/verified-run/git-execution.js +118 -0
- package/dist/core/verified-run/git-execution.js.map +1 -0
- package/dist/core/verified-run/git-plumbing.d.ts +37 -0
- package/dist/core/verified-run/git-plumbing.d.ts.map +1 -0
- package/dist/core/verified-run/git-plumbing.js +171 -0
- package/dist/core/verified-run/git-plumbing.js.map +1 -0
- package/dist/core/verified-run/git-publication-worker.d.ts +2 -0
- package/dist/core/verified-run/git-publication-worker.d.ts.map +1 -0
- package/dist/core/verified-run/git-publication-worker.js +54 -0
- package/dist/core/verified-run/git-publication-worker.js.map +1 -0
- package/dist/core/verified-run/git-sandbox-layout.d.ts +4 -0
- package/dist/core/verified-run/git-sandbox-layout.d.ts.map +1 -0
- package/dist/core/verified-run/git-sandbox-layout.js +29 -0
- package/dist/core/verified-run/git-sandbox-layout.js.map +1 -0
- package/dist/core/verified-run/git-worker-protocol.d.ts +19 -0
- package/dist/core/verified-run/git-worker-protocol.d.ts.map +1 -0
- package/dist/core/verified-run/git-worker-protocol.js +109 -0
- package/dist/core/verified-run/git-worker-protocol.js.map +1 -0
- package/dist/core/verified-run/git-worker-runtime.d.ts +19 -0
- package/dist/core/verified-run/git-worker-runtime.d.ts.map +1 -0
- package/dist/core/verified-run/git-worker-runtime.js +74 -0
- package/dist/core/verified-run/git-worker-runtime.js.map +1 -0
- package/dist/core/verified-run/journal.d.ts +2 -3
- package/dist/core/verified-run/journal.d.ts.map +1 -1
- package/dist/core/verified-run/journal.js.map +1 -1
- package/dist/core/verified-run/namespace-identity.d.ts.map +1 -1
- package/dist/core/verified-run/namespace-identity.js +1 -1
- package/dist/core/verified-run/namespace-identity.js.map +1 -1
- package/dist/core/verified-run/owned-execution.d.ts +10 -1
- package/dist/core/verified-run/owned-execution.d.ts.map +1 -1
- package/dist/core/verified-run/owned-execution.js +124 -31
- package/dist/core/verified-run/owned-execution.js.map +1 -1
- package/dist/core/verified-run/phase-context.d.ts +2 -0
- package/dist/core/verified-run/phase-context.d.ts.map +1 -1
- package/dist/core/verified-run/phase-context.js.map +1 -1
- package/dist/core/verified-run/projection.d.ts.map +1 -1
- package/dist/core/verified-run/projection.js +51 -1
- package/dist/core/verified-run/projection.js.map +1 -1
- package/dist/core/verified-run/publish-preflight.d.ts +12 -0
- package/dist/core/verified-run/publish-preflight.d.ts.map +1 -0
- package/dist/core/verified-run/publish-preflight.js +38 -0
- package/dist/core/verified-run/publish-preflight.js.map +1 -0
- package/dist/core/verified-run/publish-start.d.ts +5 -0
- package/dist/core/verified-run/publish-start.d.ts.map +1 -0
- package/dist/core/verified-run/publish-start.js +7 -0
- package/dist/core/verified-run/publish-start.js.map +1 -0
- package/dist/core/verified-run/recovery-projection.d.ts.map +1 -1
- package/dist/core/verified-run/recovery-projection.js +5 -0
- package/dist/core/verified-run/recovery-projection.js.map +1 -1
- package/dist/core/verified-run/recovery.d.ts +5 -1
- package/dist/core/verified-run/recovery.d.ts.map +1 -1
- package/dist/core/verified-run/recovery.js +10 -4
- package/dist/core/verified-run/recovery.js.map +1 -1
- package/dist/core/verified-run/run-plan.d.ts +17 -0
- package/dist/core/verified-run/run-plan.d.ts.map +1 -0
- package/dist/core/verified-run/run-plan.js +18 -0
- package/dist/core/verified-run/run-plan.js.map +1 -0
- package/dist/core/verified-run/run-publish-owned.d.ts +6 -0
- package/dist/core/verified-run/run-publish-owned.d.ts.map +1 -0
- package/dist/core/verified-run/run-publish-owned.js +199 -0
- package/dist/core/verified-run/run-publish-owned.js.map +1 -0
- package/dist/core/verified-run/run-publish.d.ts +19 -0
- package/dist/core/verified-run/run-publish.d.ts.map +1 -0
- package/dist/core/verified-run/run-publish.js +210 -0
- package/dist/core/verified-run/run-publish.js.map +1 -0
- package/dist/core/verified-run/run-status-owned.d.ts +4 -0
- package/dist/core/verified-run/run-status-owned.d.ts.map +1 -0
- package/dist/core/verified-run/run-status-owned.js +14 -0
- package/dist/core/verified-run/run-status-owned.js.map +1 -0
- package/dist/core/verified-run/run-status.d.ts +133 -0
- package/dist/core/verified-run/run-status.d.ts.map +1 -0
- package/dist/core/verified-run/run-status.js +193 -0
- package/dist/core/verified-run/run-status.js.map +1 -0
- package/dist/core/verified-run/run-types.d.ts +31 -0
- package/dist/core/verified-run/run-types.d.ts.map +1 -1
- package/dist/core/verified-run/run-types.js.map +1 -1
- package/dist/core/verified-run/storage.d.ts.map +1 -1
- package/dist/core/verified-run/storage.js +1 -1
- package/dist/core/verified-run/storage.js.map +1 -1
- package/dist/core/verified-run/supervisor-adapter.d.ts +77 -0
- package/dist/core/verified-run/supervisor-adapter.d.ts.map +1 -0
- package/dist/core/verified-run/supervisor-adapter.js +164 -0
- package/dist/core/verified-run/supervisor-adapter.js.map +1 -0
- package/dist/core/verified-run/verification-phase.d.ts.map +1 -1
- package/dist/core/verified-run/verification-phase.js +1 -1
- package/dist/core/verified-run/verification-phase.js.map +1 -1
- package/dist/core/verified-run/writer-phase.d.ts.map +1 -1
- package/dist/core/verified-run/writer-phase.js +1 -0
- package/dist/core/verified-run/writer-phase.js.map +1 -1
- package/dist/core/verified-run/writer-recovery.d.ts +2 -0
- package/dist/core/verified-run/writer-recovery.d.ts.map +1 -1
- package/dist/core/verified-run/writer-recovery.js +1 -0
- package/dist/core/verified-run/writer-recovery.js.map +1 -1
- package/dist/core/workload-shard-execution-types.d.ts +4 -1
- package/dist/core/workload-shard-execution-types.d.ts.map +1 -1
- package/dist/core/workload-shard-execution-types.js.map +1 -1
- package/dist/core/workload-shard-executor.d.ts +2 -2
- package/dist/core/workload-shard-executor.d.ts.map +1 -1
- package/dist/core/workload-shard-executor.js +16 -37
- package/dist/core/workload-shard-executor.js.map +1 -1
- package/dist/core/workload-shard-frontier.d.ts +5 -0
- package/dist/core/workload-shard-frontier.d.ts.map +1 -0
- package/dist/core/workload-shard-frontier.js +47 -0
- package/dist/core/workload-shard-frontier.js.map +1 -0
- package/dist/core/workload-shard-runner.d.ts.map +1 -1
- package/dist/core/workload-shard-runner.js +28 -17
- package/dist/core/workload-shard-runner.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/metacognition/bridge-result.d.ts +148 -0
- package/dist/metacognition/bridge-result.d.ts.map +1 -0
- package/dist/metacognition/bridge-result.js +46 -0
- package/dist/metacognition/bridge-result.js.map +1 -0
- package/dist/metacognition/calibration-selective.d.ts +78 -0
- package/dist/metacognition/calibration-selective.d.ts.map +1 -0
- package/dist/metacognition/calibration-selective.js +167 -0
- package/dist/metacognition/calibration-selective.js.map +1 -0
- package/dist/metacognition/calibration.d.ts +62 -0
- package/dist/metacognition/calibration.d.ts.map +1 -0
- package/dist/metacognition/calibration.js +122 -0
- package/dist/metacognition/calibration.js.map +1 -0
- package/dist/metacognition/checkpoint.d.ts +60 -0
- package/dist/metacognition/checkpoint.d.ts.map +1 -0
- package/dist/metacognition/checkpoint.js +57 -0
- package/dist/metacognition/checkpoint.js.map +1 -0
- package/dist/metacognition/context7.d.ts +48 -0
- package/dist/metacognition/context7.d.ts.map +1 -0
- package/dist/metacognition/context7.js +154 -0
- package/dist/metacognition/context7.js.map +1 -0
- package/dist/metacognition/decision.d.ts +44 -0
- package/dist/metacognition/decision.d.ts.map +1 -0
- package/dist/metacognition/decision.js +111 -0
- package/dist/metacognition/decision.js.map +1 -0
- package/dist/metacognition/evaluation.d.ts +73 -0
- package/dist/metacognition/evaluation.d.ts.map +1 -0
- package/dist/metacognition/evaluation.js +97 -0
- package/dist/metacognition/evaluation.js.map +1 -0
- package/dist/metacognition/index.d.ts +32 -0
- package/dist/metacognition/index.d.ts.map +1 -0
- package/dist/metacognition/index.js +32 -0
- package/dist/metacognition/index.js.map +1 -0
- package/dist/metacognition/knowledge-action.d.ts +43 -0
- package/dist/metacognition/knowledge-action.d.ts.map +1 -0
- package/dist/metacognition/knowledge-action.js +60 -0
- package/dist/metacognition/knowledge-action.js.map +1 -0
- package/dist/metacognition/knowledge.d.ts +84 -0
- package/dist/metacognition/knowledge.d.ts.map +1 -0
- package/dist/metacognition/knowledge.js +165 -0
- package/dist/metacognition/knowledge.js.map +1 -0
- package/dist/metacognition/obligations.d.ts +82 -0
- package/dist/metacognition/obligations.d.ts.map +1 -0
- package/dist/metacognition/obligations.js +139 -0
- package/dist/metacognition/obligations.js.map +1 -0
- package/dist/metacognition/observation-validity.d.ts +83 -0
- package/dist/metacognition/observation-validity.d.ts.map +1 -0
- package/dist/metacognition/observation-validity.js +118 -0
- package/dist/metacognition/observation-validity.js.map +1 -0
- package/dist/metacognition/observe.d.ts +45 -0
- package/dist/metacognition/observe.d.ts.map +1 -0
- package/dist/metacognition/observe.js +59 -0
- package/dist/metacognition/observe.js.map +1 -0
- package/dist/metacognition/policy.d.ts +53 -0
- package/dist/metacognition/policy.d.ts.map +1 -0
- package/dist/metacognition/policy.js +155 -0
- package/dist/metacognition/policy.js.map +1 -0
- package/dist/metacognition/predictions.d.ts +78 -0
- package/dist/metacognition/predictions.d.ts.map +1 -0
- package/dist/metacognition/predictions.js +181 -0
- package/dist/metacognition/predictions.js.map +1 -0
- package/dist/metacognition/retrieval.d.ts +53 -0
- package/dist/metacognition/retrieval.d.ts.map +1 -0
- package/dist/metacognition/retrieval.js +170 -0
- package/dist/metacognition/retrieval.js.map +1 -0
- package/dist/metacognition/risk.d.ts +48 -0
- package/dist/metacognition/risk.d.ts.map +1 -0
- package/dist/metacognition/risk.js +179 -0
- package/dist/metacognition/risk.js.map +1 -0
- package/dist/metacognition/route-economics.d.ts +95 -0
- package/dist/metacognition/route-economics.d.ts.map +1 -0
- package/dist/metacognition/route-economics.js +110 -0
- package/dist/metacognition/route-economics.js.map +1 -0
- package/dist/metacognition/runtime-bridge.d.ts +81 -0
- package/dist/metacognition/runtime-bridge.d.ts.map +1 -0
- package/dist/metacognition/runtime-bridge.js +458 -0
- package/dist/metacognition/runtime-bridge.js.map +1 -0
- package/dist/metacognition/skills.d.ts +42 -0
- package/dist/metacognition/skills.d.ts.map +1 -0
- package/dist/metacognition/skills.js +220 -0
- package/dist/metacognition/skills.js.map +1 -0
- package/dist/metacognition/state.d.ts +110 -0
- package/dist/metacognition/state.d.ts.map +1 -0
- package/dist/metacognition/state.js +52 -0
- package/dist/metacognition/state.js.map +1 -0
- package/dist/metacognition/validation.d.ts +19 -0
- package/dist/metacognition/validation.d.ts.map +1 -0
- package/dist/metacognition/validation.js +58 -0
- package/dist/metacognition/validation.js.map +1 -0
- package/dist/metacognition/verification.d.ts +93 -0
- package/dist/metacognition/verification.d.ts.map +1 -0
- package/dist/metacognition/verification.js +132 -0
- package/dist/metacognition/verification.js.map +1 -0
- package/dist/metacognition/verifier.d.ts +49 -0
- package/dist/metacognition/verifier.d.ts.map +1 -0
- package/dist/metacognition/verifier.js +88 -0
- package/dist/metacognition/verifier.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +42 -26
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-container.d.ts +7 -0
- package/dist/modes/interactive/components/chat-container.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-container.js +14 -0
- package/dist/modes/interactive/components/chat-container.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +4 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +0 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-async-search.d.ts +33 -0
- package/dist/modes/interactive/components/session-selector-async-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-async-search.js +109 -0
- package/dist/modes/interactive/components/session-selector-async-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-loaders.d.ts +6 -0
- package/dist/modes/interactive/components/session-selector-loaders.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-loaders.js +9 -0
- package/dist/modes/interactive/components/session-selector-loaders.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-tree.d.ts +16 -0
- package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-tree.js +46 -0
- package/dist/modes/interactive/components/session-selector-tree.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +6 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +36 -76
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution-images.d.ts +32 -0
- package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-images.js +144 -0
- package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -4
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +22 -71
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -9
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tui-diagnostics.d.ts +8 -4
- package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -1
- package/dist/modes/interactive/tui-diagnostics.js +35 -1
- package/dist/modes/interactive/tui-diagnostics.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -17
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-response.d.ts +5 -0
- package/dist/modes/rpc/rpc-response.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-response.js +17 -0
- package/dist/modes/rpc/rpc-response.js.map +1 -0
- package/dist/observation/identity.d.ts +14 -0
- package/dist/observation/identity.d.ts.map +1 -0
- package/dist/observation/identity.js +29 -0
- package/dist/observation/identity.js.map +1 -0
- package/dist/observation/index.d.ts +13 -0
- package/dist/observation/index.d.ts.map +1 -0
- package/dist/observation/index.js +13 -0
- package/dist/observation/index.js.map +1 -0
- package/dist/observation/observe-mode.d.ts +46 -0
- package/dist/observation/observe-mode.d.ts.map +1 -0
- package/dist/observation/observe-mode.js +83 -0
- package/dist/observation/observe-mode.js.map +1 -0
- package/dist/observation/store.d.ts +53 -0
- package/dist/observation/store.d.ts.map +1 -0
- package/dist/observation/store.js +126 -0
- package/dist/observation/store.js.map +1 -0
- package/dist/observation/types.d.ts +72 -0
- package/dist/observation/types.d.ts.map +1 -0
- package/dist/observation/types.js +9 -0
- package/dist/observation/types.js.map +1 -0
- package/dist/observation/view.d.ts +39 -0
- package/dist/observation/view.d.ts.map +1 -0
- package/dist/observation/view.js +173 -0
- package/dist/observation/view.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -5
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/adaptorch-preview-spec.md +7 -7
- package/docs/atomic-commit-planning.md +69 -0
- package/docs/compaction.md +26 -1
- package/docs/extensions.md +14 -0
- package/docs/mcp.md +22 -0
- package/docs/metacognition.md +130 -0
- package/docs/model-catalog-refresh.md +218 -1
- package/docs/models.md +1 -1
- package/docs/providers.md +53 -0
- package/docs/runtime-algorithms.md +156 -7
- package/docs/session-format.md +8 -0
- package/docs/sessions.md +19 -0
- package/docs/skills.md +19 -0
- package/docs/terminal-setup.md +15 -0
- package/docs/tui.md +41 -0
- package/docs/verified-run-safety.md +75 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptorch-bridge.d.ts","sourceRoot":"","sources":["../../src/core/adaptorch-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router AdaptOrch advisory bridge (Goal 009 Lane B).\n *\n * A default-off, advisory-only primitive wired into AgentSession's v4 resolver.\n * AgentSession reads only the bridge's bounded cache and uses a caller-injected\n * advisory function for refresh; the current production transport is a no-op,\n * so it returns no hint. This module owns payload/result validation, TTL cache,\n * budget counter, circuit breaker, and timeout handling. It never calls an MCP\n * transport directly or receives execution authority.\n *\n * Grounded in the read-first plan at\n * .omk/goals/008-reasoning-router-advanced-accuracy-plan/laneC-privacy-adaptorch.md\n * (Part B, sections 4.1-4.11) and the real AdaptOrch MCP tool surface\n * documented in packages/adaptorch-wpl/src/adaptorch-client.ts. Key\n * constraints carried over from that plan:\n *\n * - The router's turn-start path is synchronous and I/O-free today (plan\n * 4.4). This module never calls an MCP transport directly and exposes a\n * synchronous, cache-read-only accessor (`getFreshHint`) plus a\n * fire-and-forget refresh (`requestRefresh`) so the AgentSession integration\n * never blocks or awaits advisory I/O on the turn-start path.\n * - The real, documented AdaptOrch tool surface has no confidence or\n * reasoning-level field (plan 4.3); this module's inbound result type\n * (`AdaptorchAdvisoryResult`) therefore only carries a locally-relevant\n * `taskClass` and a `confidenceBand` - both closed enums - never a raw\n * numeric confidence or a `ThinkingLevel` value. AgentSession owns the\n * bounded fusion from this advisory shape into the v4 resolver.\n * - No product source is imported here. `AdaptorchTaskClass` /\n * `AdaptorchLaneType` intentionally mirror the v4 task/lane unions by\n * value, while AgentSession adapts them at the integration boundary.\n * - Every outbound/inbound field is a closed enum, a boolean, or a bounded\n * integer, derived from local features only. There is structurally no\n * field shaped to carry prompt text, a prompt hash, a file/working-\n * directory path, a model/provider identifier, a session/user identifier,\n * a tool name, or hook output - the sanitize/validate helpers below also\n * reject any candidate object exposing such a field by name, and reject\n * any key outside the exact closed schema, so a hand-built or forged\n * payload can never smuggle one through either type.\n * - The router-bridge tool allowlist (`ADAPTORCH_READONLY_TOOL_ALLOWLIST`)\n * only ever contains the two read/local AdaptOrch tools the plan\n * identifies as in scope (the session-start capabilities probe and the\n * per-consult topology-classification call, plan 4.2/4.6); the\n * run-submission and run-cancellation tools (and any other mutating\n * tool) can never appear in it, checked both by an exact-value test and\n * by `looksMutatingToolName`'s verb-segment guard.\n *\n * Pure aside from `Date.now`/`setTimeout` (both injectable/wrapped so tests\n * never depend on real wall-clock time or a real MCP call).\n */\n\n/** Closed set of task classes. Mirrors the v4 task-class union by value (see file header). */\nexport type AdaptorchTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Closed set of subagent lane types. Mirrors the v4 lane union by value. */\nexport type AdaptorchLaneType = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Local top1-vs-runner-up score-margin bucket (plan 4.5 ConsultPayloadV1.marginBucket). */\nexport type AdaptorchMarginBucket = \"tie\" | \"low\" | \"medium\" | \"high\";\n\n/** Locally-derived confidence band for a returned hint. Never a raw network-supplied float (plan 4.3). */\nexport type AdaptorchConfidenceBand = \"low\" | \"medium\" | \"high\";\n\n/** floor(log2(trimmedPromptLen+1)) clamped to [0,7]. */\nexport type AdaptorchLenBucket = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/** Context-pressure band 0..3 - same bucketing as agent-session.ts's _computePressureBucket. */\nexport type AdaptorchPressureBucket = 0 | 1 | 2 | 3;\n\n/**\n * Outbound advisory request (plan 4.5, field names adapted for this lane's\n * assignment: taskClass/runnerUp/marginBucket/lenBucket/hadFence/hadDiff/\n * pressureBucket/laneType). Every field is a closed enum, a boolean, or a\n * bounded integer built from local classifier features only. Structurally\n * excludes prompt text/hash, file paths, model/provider/session\n * identifiers, tool names, and hook output - there is no field shaped to\n * carry any of those.\n */\nexport interface AdaptorchConsultPayload {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly runnerUp: AdaptorchTaskClass;\n\treadonly marginBucket: AdaptorchMarginBucket;\n\treadonly lenBucket: AdaptorchLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n\treadonly pressureBucket: AdaptorchPressureBucket;\n\treadonly laneType?: AdaptorchLaneType;\n}\n\n/**\n * Inbound advisory result after local sanitation (plan 4.3, corrected\n * schema). `confidenceBand` is always synthesized/validated locally - the\n * real AdaptOrch tool contract carries no confidence or effort-level field,\n * so this type never trusts a raw numeric confidence from the advisory\n * function.\n */\nexport interface AdaptorchAdvisoryResult {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly confidenceBand: AdaptorchConfidenceBand;\n}\n\nconst TASK_CLASSES: readonly AdaptorchTaskClass[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\nconst TASK_CLASS_SET: ReadonlySet<string> = new Set(TASK_CLASSES);\n\nconst LANE_TYPES: readonly AdaptorchLaneType[] = [\"planner\", \"security\", \"explorer\", \"coder\", \"reviewer\", \"tester\"];\nconst LANE_TYPE_SET: ReadonlySet<string> = new Set(LANE_TYPES);\n\nconst MARGIN_BUCKETS: readonly AdaptorchMarginBucket[] = [\"tie\", \"low\", \"medium\", \"high\"];\nconst MARGIN_BUCKET_SET: ReadonlySet<string> = new Set(MARGIN_BUCKETS);\n\nconst CONFIDENCE_BANDS: readonly AdaptorchConfidenceBand[] = [\"low\", \"medium\", \"high\"];\nconst CONFIDENCE_BAND_SET: ReadonlySet<string> = new Set(CONFIDENCE_BANDS);\n\nconst CONSULT_PAYLOAD_KEYS: ReadonlySet<string> = new Set([\n\t\"schemaVersion\",\n\t\"taskClass\",\n\t\"runnerUp\",\n\t\"marginBucket\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"pressureBucket\",\n\t\"laneType\",\n]);\n\nconst ADVISORY_RESULT_KEYS: ReadonlySet<string> = new Set([\"schemaVersion\", \"taskClass\", \"confidenceBand\"]);\n\n/**\n * Field-name markers that must never appear on a payload or result object,\n * checked case-insensitively as a substring of every own key. This is\n * defense-in-depth alongside the exact-key allowlist below: even a candidate\n * that otherwise has every required field still fails if it also carries a\n * field shaped like a stored prompt, hash, path, model/provider identifier,\n * session identifier, tool name, or hook output.\n */\nconst FORBIDDEN_KEY_SUBSTRINGS: readonly string[] = [\n\t\"prompt\",\n\t\"hash\",\n\t\"path\",\n\t\"model\",\n\t\"session\",\n\t\"tool\",\n\t\"hook\",\n\t\"provider\",\n];\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * True if any own key of `record` contains a forbidden field-name marker\n * (case-insensitive). Typed `object` rather than `Record<string, unknown>` so\n * callers (including tests) can pass any concrete object shape - named\n * interface, ad-hoc literal, or an already-`Record`-typed value - without a\n * missing-index-signature type error; only `Object.keys` is used internally,\n * which accepts any `object`.\n */\nexport function containsForbiddenField(record: object): boolean {\n\tfor (const key of Object.keys(record)) {\n\t\tconst lowerKey = key.toLowerCase();\n\t\tif (FORBIDDEN_KEY_SUBSTRINGS.some((needle) => lowerKey.includes(needle))) return true;\n\t}\n\treturn false;\n}\n\nfunction hasOnlyAllowedKeys(record: Record<string, unknown>, allowed: ReadonlySet<string>): boolean {\n\treturn Object.keys(record).every((key) => allowed.has(key));\n}\n\nfunction clampInt(value: number, min: number, max: number): number {\n\tconst truncated = Number.isFinite(value) ? Math.trunc(value) : min;\n\tif (truncated < min) return min;\n\tif (truncated > max) return max;\n\treturn truncated;\n}\n\nfunction clampLenBucket(value: number): AdaptorchLenBucket {\n\treturn clampInt(value, 0, 7) as AdaptorchLenBucket;\n}\n\nfunction clampPressureBucket(value: number): AdaptorchPressureBucket {\n\treturn clampInt(value, 0, 3) as AdaptorchPressureBucket;\n}\n\n/** `value ?? fallback`, coerced to an integer and floored at `min` (used for config, not untrusted payloads). */\nfunction boundedIntOrDefault(value: number | undefined, fallback: number, min: number): number {\n\tconst raw = value ?? fallback;\n\tif (!Number.isFinite(raw)) return fallback;\n\treturn Math.max(min, Math.trunc(raw));\n}\n\n/**\n * Sanitize/validate an outbound advisory request. Returns `null` (never\n * throws) when `candidate` is not a plain object, carries a forbidden-named\n * field, carries any key outside the exact closed schema, or has a\n * wrong-typed/unknown-enum field. `lenBucket`/`pressureBucket` are clamped\n * into their bounded ranges rather than rejected, so a caller-side rounding\n * or edge-case computation can never smuggle an out-of-range integer through.\n */\nexport function sanitizeConsultPayload(candidate: unknown): AdaptorchConsultPayload | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, CONSULT_PAYLOAD_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst runnerUp = candidate.runnerUp;\n\tif (typeof runnerUp !== \"string\" || !TASK_CLASS_SET.has(runnerUp)) return null;\n\n\tconst marginBucket = candidate.marginBucket;\n\tif (typeof marginBucket !== \"string\" || !MARGIN_BUCKET_SET.has(marginBucket)) return null;\n\n\tconst lenBucketRaw = candidate.lenBucket;\n\tif (typeof lenBucketRaw !== \"number\") return null;\n\n\tconst pressureBucketRaw = candidate.pressureBucket;\n\tif (typeof pressureBucketRaw !== \"number\") return null;\n\n\tconst hadFence = candidate.hadFence;\n\tif (typeof hadFence !== \"boolean\") return null;\n\n\tconst hadDiff = candidate.hadDiff;\n\tif (typeof hadDiff !== \"boolean\") return null;\n\n\tconst laneTypeRaw = candidate.laneType;\n\tlet laneType: AdaptorchLaneType | undefined;\n\tif (laneTypeRaw !== undefined) {\n\t\tif (typeof laneTypeRaw !== \"string\" || !LANE_TYPE_SET.has(laneTypeRaw)) return null;\n\t\tlaneType = laneTypeRaw as AdaptorchLaneType;\n\t}\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\trunnerUp: runnerUp as AdaptorchTaskClass,\n\t\tmarginBucket: marginBucket as AdaptorchMarginBucket,\n\t\tlenBucket: clampLenBucket(lenBucketRaw),\n\t\thadFence,\n\t\thadDiff,\n\t\tpressureBucket: clampPressureBucket(pressureBucketRaw),\n\t\t...(laneType !== undefined ? { laneType } : {}),\n\t};\n}\n\n/**\n * Sanitize/validate an inbound advisory result (i.e. whatever the injected\n * advisory function returned). Returns `null` (never throws) on any shape,\n * forbidden-field, extra-key, or unknown-enum violation - the caller\n * (`AdaptorchBridge.consult`) treats `null` as \"no hint\" and falls back\n * silently, exactly like every other failure mode.\n */\nexport function sanitizeAdvisoryResult(candidate: unknown): AdaptorchAdvisoryResult | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, ADVISORY_RESULT_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst confidenceBand = candidate.confidenceBand;\n\tif (typeof confidenceBand !== \"string\" || !CONFIDENCE_BAND_SET.has(confidenceBand)) return null;\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\tconfidenceBand: confidenceBand as AdaptorchConfidenceBand,\n\t};\n}\n\n/**\n * Read-only/local AdaptOrch MCP tool names this bridge may ever reference,\n * grounded in the real 10-tool surface documented in\n * packages/adaptorch-wpl/src/adaptorch-client.ts (cross-checked there\n * against the AdaptOrch server's own docs) and narrowed to the two tools the\n * read-first plan puts in scope for a reasoning-effort hint (plan 4.2): the\n * session-start capabilities probe and the per-consult topology\n * classification call. This module does not call any MCP tool directly\n * today - the advisory function is fully caller-injected - so this\n * allowlist is not wired to a transport here; it exists so a future\n * transport, and this module's own tests, can assert by construction that\n * `adaptorch_run` (submit) and `adaptorch_cancel_run` (cancel), or any other\n * mutating tool, can never be reachable from router/bridge code.\n */\nexport const ADAPTORCH_READONLY_TOOL_ALLOWLIST = [\"adaptorch_capabilities\", \"adaptorch_route_topology\"] as const;\n\nexport type AdaptorchReadonlyToolName = (typeof ADAPTORCH_READONLY_TOOL_ALLOWLIST)[number];\n\n/** Underscore-segment verbs that mark an AdaptOrch-shaped tool name as mutating (write/execute/lifecycle). */\nconst MUTATING_TOOL_VERB_SEGMENTS: ReadonlySet<string> = new Set([\n\t\"run\",\n\t\"submit\",\n\t\"cancel\",\n\t\"dispatch\",\n\t\"delete\",\n\t\"execute\",\n\t\"mutate\",\n\t\"write\",\n\t\"create\",\n\t\"update\",\n\t\"remove\",\n]);\n\n/**\n * True if any underscore-delimited segment of an AdaptOrch-shaped tool name\n * is a known mutating verb. Segment-based rather than a raw substring/regex\n * match so \"adaptorch_route_topology\" is never falsely flagged (no segment\n * is a mutating verb), while \"adaptorch_run\" and \"adaptorch_cancel_run\" are\n * always caught regardless of future prefix/suffix changes.\n */\nexport function looksMutatingToolName(name: string): boolean {\n\treturn name\n\t\t.toLowerCase()\n\t\t.split(\"_\")\n\t\t.some((segment) => MUTATING_TOOL_VERB_SEGMENTS.has(segment));\n}\n\n/**\n * Type guard: true only for the two allowlisted read/local AdaptOrch tool\n * names. Also independently rejects any name `looksMutatingToolName` flags,\n * so a future accidental addition of a mutating name to the allowlist array\n * still cannot make this function report it as allowed.\n */\nexport function isAdaptorchToolAllowed(name: string): name is AdaptorchReadonlyToolName {\n\tif (looksMutatingToolName(name)) return false;\n\treturn (ADAPTORCH_READONLY_TOOL_ALLOWLIST as readonly string[]).includes(name);\n}\n\n/**\n * Timeout wrapper interface around any injected async advisory call. Races\n * the call against a timer; a slow or hung call rejects at `timeoutMs`\n * instead of leaving the caller waiting indefinitely. Always clears the\n * timer once the call settles (or once the timeout itself fires), and\n * always resolves/rejects exactly once - a synchronous throw from `run` is\n * treated the same as an asynchronous rejection.\n */\nexport type AdvisoryTimeoutWrapper = <T>(run: () => Promise<T>, timeoutMs: number) => Promise<T>;\n\nexport const withAdvisoryTimeout: AdvisoryTimeoutWrapper = <T>(\n\trun: () => Promise<T>,\n\ttimeoutMs: number,\n): Promise<T> => {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\treject(new Error(\"adaptorch-bridge: advisory call timed out\"));\n\t\t}, timeoutMs);\n\n\t\tlet pending: Promise<T>;\n\t\ttry {\n\t\t\tpending = run();\n\t\t} catch (err) {\n\t\t\tclearTimeout(timer);\n\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\treturn;\n\t\t}\n\n\t\tpending.then(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(err: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t},\n\t\t);\n\t});\n};\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\nconst MIN_TTL_MS = 30 * 1000;\nconst MAX_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_MAX_TURNS_PER_ENTRY = 10;\nconst DEFAULT_CACHE_SIZE = 16;\nconst DEFAULT_MAX_CONSULTS_PER_SESSION = 5;\nconst DEFAULT_MIN_INTERVAL_MS = 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 1500;\nconst MIN_TIMEOUT_MS = 1;\nconst MAX_TIMEOUT_MS = 30 * 1000;\nconst DEFAULT_FAILURE_THRESHOLD = 3;\nconst DEFAULT_COOLDOWN_MS = 10 * 60 * 1000;\n\n/**\n * Exact TTL/cache/budget/circuit-breaker defaults from the read-first plan,\n * section 4.8. Exported so a security/review lane can confirm the shipped\n * numbers without reading this module's internals.\n */\nexport const ADAPTORCH_BRIDGE_DEFAULTS = {\n\tttlMs: DEFAULT_TTL_MS,\n\tminTtlMs: MIN_TTL_MS,\n\tmaxTtlMs: MAX_TTL_MS,\n\tmaxTurnsPerEntry: DEFAULT_MAX_TURNS_PER_ENTRY,\n\tcacheSize: DEFAULT_CACHE_SIZE,\n\tmaxConsultsPerSession: DEFAULT_MAX_CONSULTS_PER_SESSION,\n\tminIntervalMs: DEFAULT_MIN_INTERVAL_MS,\n\ttimeoutMs: DEFAULT_TIMEOUT_MS,\n\tminTimeoutMs: MIN_TIMEOUT_MS,\n\tmaxTimeoutMs: MAX_TIMEOUT_MS,\n\tfailureThreshold: DEFAULT_FAILURE_THRESHOLD,\n\tcooldownMs: DEFAULT_COOLDOWN_MS,\n} as const;\n\n/**\n * Bridge configuration. `advisoryFn` is the ONLY seam to any external\n * system - this module never imports or calls an MCP client/transport\n * itself, so it is impossible for a unit test (or a misconfigured caller)\n * to reach a real MCP server through this module by accident.\n */\nexport interface AdaptorchBridgeConfig {\n\t/**\n\t * Caller-injected advisory call. Its return value is untrusted: `consult`\n\t * always runs it through `sanitizeAdvisoryResult` before using or caching\n\t * it, and a rejected/thrown/timed-out/schema-invalid response is treated\n\t * as an ordinary failure (counts toward the circuit breaker), never as a\n\t * value to fuse.\n\t */\n\treadonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\t/** Injectable clock; defaults to `Date.now`. Tests supply a fake to avoid real wall-clock waits. */\n\treadonly now?: () => number;\n\t/** Cache TTL in ms; clamped to [30s, 5min] (plan 4.8). Default 5 minutes. */\n\treadonly ttlMs?: number;\n\t/** A cache entry also expires after this many `getFreshHint` calls (\"turns\"). Default 10. */\n\treadonly maxTurnsPerEntry?: number;\n\t/** Max distinct cached payload keys (LRU eviction beyond this). Default 16. */\n\treadonly cacheSize?: number;\n\t/** Max advisory attempts for this bridge instance's lifetime (\"session\"). Default 5. */\n\treadonly maxConsultsPerSession?: number;\n\t/** Minimum ms between advisory attempts. Default 60000 (60s). */\n\treadonly minIntervalMs?: number;\n\t/** Per-attempt timeout in ms; clamped to [1ms, 30s]. Default 1500 (1.5s). */\n\treadonly timeoutMs?: number;\n\t/** Consecutive failures before the circuit opens. Default 3. */\n\treadonly failureThreshold?: number;\n\t/** Circuit-open duration in ms once opened. Default 600000 (10min). */\n\treadonly cooldownMs?: number;\n}\n\n/** Read-only, non-sensitive introspection snapshot - never exposes payload/result content. */\nexport interface AdaptorchBridgeStats {\n\treadonly consultCount: number;\n\treadonly failureStreak: number;\n\treadonly circuitOpen: boolean;\n\treadonly circuitOpenUntilMs: number;\n\treadonly cacheEntryCount: number;\n}\n\nexport interface AdaptorchBridge {\n\t/**\n\t * Synchronous, cache-read-only. Never blocks, never throws, and never\n\t * calls the advisory function - a cache miss or an expired/invalid entry\n\t * silently returns `null` (\"no hint\"), matching the turn-start path's\n\t * synchronous, I/O-free contract (plan 4.4).\n\t */\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null;\n\t/**\n\t * Fire-and-forget refresh for a LATER call to `getFreshHint`, never the\n\t * current one. Never throws synchronously and never rejects into the\n\t * caller - any failure is swallowed internally by `consult`.\n\t */\n\trequestRefresh(payload: AdaptorchConsultPayload): void;\n\t/**\n\t * The awaitable core operation `requestRefresh` fires without awaiting.\n\t * Gated by the circuit breaker, budget counter, and minimum interval (in\n\t * that order) before ever invoking `advisoryFn`; always resolves (never\n\t * rejects) to either a sanitized hint or `null`.\n\t */\n\tconsult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null>;\n\t/** Read-only introspection for tests/diagnostics; contains no payload or result content. */\n\tgetStats(): AdaptorchBridgeStats;\n}\n\ninterface CacheEntry {\n\treadonly result: AdaptorchAdvisoryResult;\n\treadonly insertedAtMs: number;\n\treadonly insertedAtTurn: number;\n}\n\n/** Deterministic cache key built field-by-field from a sanitized payload - never from raw prompt text. */\nfunction cacheKeyFor(payload: AdaptorchConsultPayload): string {\n\treturn JSON.stringify({\n\t\ttaskClass: payload.taskClass,\n\t\trunnerUp: payload.runnerUp,\n\t\tmarginBucket: payload.marginBucket,\n\t\tlenBucket: payload.lenBucket,\n\t\thadFence: payload.hadFence,\n\t\thadDiff: payload.hadDiff,\n\t\tpressureBucket: payload.pressureBucket,\n\t\tlaneType: payload.laneType ?? null,\n\t});\n}\n\nclass AdaptorchBridgeImpl implements AdaptorchBridge {\n\tprivate readonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\tprivate readonly now: () => number;\n\tprivate readonly ttlMs: number;\n\tprivate readonly maxTurnsPerEntry: number;\n\tprivate readonly cacheSize: number;\n\tprivate readonly maxConsultsPerSession: number;\n\tprivate readonly minIntervalMs: number;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly failureThreshold: number;\n\tprivate readonly cooldownMs: number;\n\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate turnCounter = 0;\n\tprivate consultCount = 0;\n\tprivate failureStreak = 0;\n\tprivate lastAttemptAtMs: number | null = null;\n\tprivate circuitOpenUntilMs = 0;\n\n\tconstructor(config: AdaptorchBridgeConfig) {\n\t\tthis.advisoryFn = config.advisoryFn;\n\t\tthis.now = config.now ?? Date.now;\n\t\tthis.ttlMs = clampInt(config.ttlMs ?? DEFAULT_TTL_MS, MIN_TTL_MS, MAX_TTL_MS);\n\t\tthis.maxTurnsPerEntry = boundedIntOrDefault(config.maxTurnsPerEntry, DEFAULT_MAX_TURNS_PER_ENTRY, 1);\n\t\tthis.cacheSize = boundedIntOrDefault(config.cacheSize, DEFAULT_CACHE_SIZE, 1);\n\t\tthis.maxConsultsPerSession = boundedIntOrDefault(\n\t\t\tconfig.maxConsultsPerSession,\n\t\t\tDEFAULT_MAX_CONSULTS_PER_SESSION,\n\t\t\t0,\n\t\t);\n\t\tthis.minIntervalMs = boundedIntOrDefault(config.minIntervalMs, DEFAULT_MIN_INTERVAL_MS, 0);\n\t\tthis.timeoutMs = clampInt(config.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);\n\t\tthis.failureThreshold = boundedIntOrDefault(config.failureThreshold, DEFAULT_FAILURE_THRESHOLD, 1);\n\t\tthis.cooldownMs = boundedIntOrDefault(config.cooldownMs, DEFAULT_COOLDOWN_MS, 0);\n\t}\n\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null {\n\t\tthis.turnCounter += 1;\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst key = cacheKeyFor(sanitizedPayload);\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry === undefined) return null;\n\t\tif (!this.isEntryFresh(entry)) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\treturn entry.result;\n\t}\n\n\trequestRefresh(payload: AdaptorchConsultPayload): void {\n\t\tvoid this.consult(payload).catch(() => {\n\t\t\t// consult() already swallows every failure and always resolves; this\n\t\t\t// catch only guards a future refactor from letting one escape, so a\n\t\t\t// fire-and-forget refresh can never surface an unhandled rejection.\n\t\t});\n\t}\n\n\tasync consult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null> {\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst nowMs = this.now();\n\t\tif (nowMs < this.circuitOpenUntilMs) return null;\n\t\tif (this.consultCount >= this.maxConsultsPerSession) return null;\n\t\tif (this.lastAttemptAtMs !== null && nowMs - this.lastAttemptAtMs < this.minIntervalMs) return null;\n\n\t\tthis.consultCount += 1;\n\t\tthis.lastAttemptAtMs = nowMs;\n\n\t\tlet raw: unknown;\n\t\ttry {\n\t\t\traw = await withAdvisoryTimeout(() => this.advisoryFn(sanitizedPayload), this.timeoutMs);\n\t\t} catch {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = sanitizeAdvisoryResult(raw);\n\t\tif (result === null) {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.failureStreak = 0;\n\t\tthis.cacheResult(sanitizedPayload, result);\n\t\treturn result;\n\t}\n\n\tgetStats(): AdaptorchBridgeStats {\n\t\tconst nowMs = this.now();\n\t\treturn {\n\t\t\tconsultCount: this.consultCount,\n\t\t\tfailureStreak: this.failureStreak,\n\t\t\tcircuitOpen: nowMs < this.circuitOpenUntilMs,\n\t\t\tcircuitOpenUntilMs: this.circuitOpenUntilMs,\n\t\t\tcacheEntryCount: this.cache.size,\n\t\t};\n\t}\n\n\tprivate recordFailure(): void {\n\t\tthis.failureStreak += 1;\n\t\tif (this.failureStreak >= this.failureThreshold) {\n\t\t\tthis.circuitOpenUntilMs = this.now() + this.cooldownMs;\n\t\t}\n\t}\n\n\tprivate isEntryFresh(entry: CacheEntry): boolean {\n\t\tconst ageMs = this.now() - entry.insertedAtMs;\n\t\tconst ageTurns = this.turnCounter - entry.insertedAtTurn;\n\t\treturn ageMs < this.ttlMs && ageTurns < this.maxTurnsPerEntry;\n\t}\n\n\t/** Eviction favors entries last refreshed by a successful consult (write-time LRU, not read-time). */\n\tprivate cacheResult(payload: AdaptorchConsultPayload, result: AdaptorchAdvisoryResult): void {\n\t\tconst key = cacheKeyFor(payload);\n\t\tif (this.cache.has(key)) this.cache.delete(key);\n\t\tthis.cache.set(key, { result, insertedAtMs: this.now(), insertedAtTurn: this.turnCounter });\n\t\twhile (this.cache.size > this.cacheSize) {\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey === undefined) break;\n\t\t\tthis.cache.delete(oldestKey);\n\t\t}\n\t}\n}\n\n/** Construct a default-off AdaptOrch advisory bridge. See `AdaptorchBridgeConfig` for tunables. */\nexport function createAdaptorchBridge(config: AdaptorchBridgeConfig): AdaptorchBridge {\n\treturn new AdaptorchBridgeImpl(config);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"adaptorch-bridge.d.ts","sourceRoot":"","sources":["../../src/core/adaptorch-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router AdaptOrch advisory bridge (Goal 009 Lane B).\n *\n * A default-off, advisory-only primitive wired into AgentSession's v4 resolver.\n * AgentSession reads only the bridge's bounded cache and uses a caller-injected\n * advisory function for refresh; the current production transport is a no-op,\n * so it returns no hint. This module owns payload/result validation, TTL cache,\n * budget counter, circuit breaker, and timeout handling. It never calls an MCP\n * transport directly or receives execution authority.\n *\n * Grounded in the read-first plan at\n * .omk/goals/008-reasoning-router-advanced-accuracy-plan/laneC-privacy-adaptorch.md\n * (Part B, sections 4.1-4.11) and the real AdaptOrch MCP tool surface\n * documented in packages/adaptorch-wpl/src/adaptorch-client.ts. Key\n * constraints carried over from that plan:\n *\n * - The router's turn-start path is synchronous and I/O-free today (plan\n * 4.4). This module never calls an MCP transport directly and exposes a\n * synchronous, cache-read-only accessor (`getFreshHint`) plus a\n * fire-and-forget refresh (`requestRefresh`) so the AgentSession integration\n * never blocks or awaits advisory I/O on the turn-start path.\n * - The real, documented AdaptOrch tool surface has no confidence or\n * reasoning-level field (plan 4.3); this module's inbound result type\n * (`AdaptorchAdvisoryResult`) therefore only carries a locally-relevant\n * `taskClass` and a `confidenceBand` - both closed enums - never a raw\n * numeric confidence or a `ThinkingLevel` value. AgentSession owns the\n * bounded fusion from this advisory shape into the v4 resolver.\n * - No product source is imported here. `AdaptorchTaskClass` /\n * `AdaptorchLaneType` intentionally mirror the v4 task/lane unions by\n * value, while AgentSession adapts them at the integration boundary.\n * - Every outbound/inbound field is a closed enum, a boolean, or a bounded\n * integer, derived from local features only. There is structurally no\n * field shaped to carry prompt text, a prompt hash, a file/working-\n * directory path, a model/provider identifier, a session/user identifier,\n * a tool name, or hook output - the sanitize/validate helpers below also\n * reject any candidate object exposing such a field by name, and reject\n * any key outside the exact closed schema, so a hand-built or forged\n * payload can never smuggle one through either type.\n * - The router-bridge tool allowlist (`ADAPTORCH_READONLY_TOOL_ALLOWLIST`)\n * only ever contains the two read/local AdaptOrch tools the plan\n * identifies as in scope (the session-start capabilities probe and the\n * per-consult topology-classification call, plan 4.2/4.6); the\n * run-submission and run-cancellation tools (and any other mutating\n * tool) can never appear in it, checked both by an exact-value test and\n * by `looksMutatingToolName`'s verb-segment guard.\n *\n * Pure aside from `Date.now`/`setTimeout` (both injectable/wrapped so tests\n * never depend on real wall-clock time or a real MCP call).\n */\n\n/** Closed set of task classes. Mirrors the v4 task-class union by value (see file header). */\nexport type AdaptorchTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Closed set of subagent lane types. Mirrors the v4 lane union by value. */\nexport type AdaptorchLaneType = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Local top1-vs-runner-up score-margin bucket (plan 4.5 ConsultPayloadV1.marginBucket). */\nexport type AdaptorchMarginBucket = \"tie\" | \"low\" | \"medium\" | \"high\";\n\n/** Locally-derived confidence band for a returned hint. Never a raw network-supplied float (plan 4.3). */\nexport type AdaptorchConfidenceBand = \"low\" | \"medium\" | \"high\";\n\n/** floor(log2(trimmedPromptLen+1)) clamped to [0,7]. */\nexport type AdaptorchLenBucket = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/** Context-pressure band 0..3 - same bucketing as agent-session.ts's _computePressureBucket. */\nexport type AdaptorchPressureBucket = 0 | 1 | 2 | 3;\n\n/**\n * Outbound advisory request (plan 4.5, field names adapted for this lane's\n * assignment: taskClass/runnerUp/marginBucket/lenBucket/hadFence/hadDiff/\n * pressureBucket/laneType). Every field is a closed enum, a boolean, or a\n * bounded integer built from local classifier features only. Structurally\n * excludes prompt text/hash, file paths, model/provider/session\n * identifiers, tool names, and hook output - there is no field shaped to\n * carry any of those.\n */\nexport interface AdaptorchConsultPayload {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly runnerUp: AdaptorchTaskClass;\n\treadonly marginBucket: AdaptorchMarginBucket;\n\treadonly lenBucket: AdaptorchLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n\treadonly pressureBucket: AdaptorchPressureBucket;\n\treadonly laneType?: AdaptorchLaneType;\n}\n\n/**\n * Inbound advisory result after local sanitation (plan 4.3, corrected\n * schema). `confidenceBand` is always synthesized/validated locally - the\n * real AdaptOrch tool contract carries no confidence or effort-level field,\n * so this type never trusts a raw numeric confidence from the advisory\n * function.\n */\nexport interface AdaptorchAdvisoryResult {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly confidenceBand: AdaptorchConfidenceBand;\n}\n\nconst TASK_CLASSES: readonly AdaptorchTaskClass[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\nconst TASK_CLASS_SET: ReadonlySet<string> = new Set(TASK_CLASSES);\n\nconst LANE_TYPES: readonly AdaptorchLaneType[] = [\"planner\", \"security\", \"explorer\", \"coder\", \"reviewer\", \"tester\"];\nconst LANE_TYPE_SET: ReadonlySet<string> = new Set(LANE_TYPES);\n\nconst MARGIN_BUCKETS: readonly AdaptorchMarginBucket[] = [\"tie\", \"low\", \"medium\", \"high\"];\nconst MARGIN_BUCKET_SET: ReadonlySet<string> = new Set(MARGIN_BUCKETS);\n\nconst CONFIDENCE_BANDS: readonly AdaptorchConfidenceBand[] = [\"low\", \"medium\", \"high\"];\nconst CONFIDENCE_BAND_SET: ReadonlySet<string> = new Set(CONFIDENCE_BANDS);\n\nconst CONSULT_PAYLOAD_KEYS: ReadonlySet<string> = new Set([\n\t\"schemaVersion\",\n\t\"taskClass\",\n\t\"runnerUp\",\n\t\"marginBucket\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"pressureBucket\",\n\t\"laneType\",\n]);\n\nconst ADVISORY_RESULT_KEYS: ReadonlySet<string> = new Set([\"schemaVersion\", \"taskClass\", \"confidenceBand\"]);\n\n/**\n * Field-name markers that must never appear on a payload or result object,\n * checked case-insensitively as a substring of every own key. This is\n * defense-in-depth alongside the exact-key allowlist below: even a candidate\n * that otherwise has every required field still fails if it also carries a\n * field shaped like a stored prompt, hash, path, model/provider identifier,\n * session identifier, tool name, or hook output.\n */\nconst FORBIDDEN_KEY_SUBSTRINGS: readonly string[] = [\n\t\"prompt\",\n\t\"hash\",\n\t\"path\",\n\t\"model\",\n\t\"session\",\n\t\"tool\",\n\t\"hook\",\n\t\"provider\",\n];\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * True if any own key of `record` contains a forbidden field-name marker\n * (case-insensitive). Typed `object` rather than `Record<string, unknown>` so\n * callers (including tests) can pass any concrete object shape - named\n * interface, ad-hoc literal, or an already-`Record`-typed value - without a\n * missing-index-signature type error; only `Object.keys` is used internally,\n * which accepts any `object`.\n */\nexport function containsForbiddenField(record: object): boolean {\n\tfor (const key of Object.keys(record)) {\n\t\tconst lowerKey = key.toLowerCase();\n\t\tif (FORBIDDEN_KEY_SUBSTRINGS.some((needle) => lowerKey.includes(needle))) return true;\n\t}\n\treturn false;\n}\n\nfunction hasOnlyAllowedKeys(record: Record<string, unknown>, allowed: ReadonlySet<string>): boolean {\n\treturn Object.keys(record).every((key) => allowed.has(key));\n}\n\nfunction clampInt(value: number, min: number, max: number): number {\n\tconst truncated = Number.isFinite(value) ? Math.trunc(value) : min;\n\tif (truncated < min) return min;\n\tif (truncated > max) return max;\n\treturn truncated;\n}\n\nfunction clampLenBucket(value: number): AdaptorchLenBucket {\n\treturn clampInt(value, 0, 7) as AdaptorchLenBucket;\n}\n\nfunction clampPressureBucket(value: number): AdaptorchPressureBucket {\n\treturn clampInt(value, 0, 3) as AdaptorchPressureBucket;\n}\n\n/** `value ?? fallback`, coerced to an integer and floored at `min` (used for config, not untrusted payloads). */\nfunction boundedIntOrDefault(value: number | undefined, fallback: number, min: number): number {\n\tconst raw = value ?? fallback;\n\tif (!Number.isFinite(raw)) return fallback;\n\treturn Math.max(min, Math.trunc(raw));\n}\n\n/**\n * Sanitize/validate an outbound advisory request. Returns `null` (never\n * throws) when `candidate` is not a plain object, carries a forbidden-named\n * field, carries any key outside the exact closed schema, or has a\n * wrong-typed/unknown-enum field. `lenBucket`/`pressureBucket` are clamped\n * into their bounded ranges rather than rejected, so a caller-side rounding\n * or edge-case computation can never smuggle an out-of-range integer through.\n */\nexport function sanitizeConsultPayload(candidate: unknown): AdaptorchConsultPayload | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, CONSULT_PAYLOAD_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst runnerUp = candidate.runnerUp;\n\tif (typeof runnerUp !== \"string\" || !TASK_CLASS_SET.has(runnerUp)) return null;\n\n\tconst marginBucket = candidate.marginBucket;\n\tif (typeof marginBucket !== \"string\" || !MARGIN_BUCKET_SET.has(marginBucket)) return null;\n\n\tconst lenBucketRaw = candidate.lenBucket;\n\tif (typeof lenBucketRaw !== \"number\") return null;\n\n\tconst pressureBucketRaw = candidate.pressureBucket;\n\tif (typeof pressureBucketRaw !== \"number\") return null;\n\n\tconst hadFence = candidate.hadFence;\n\tif (typeof hadFence !== \"boolean\") return null;\n\n\tconst hadDiff = candidate.hadDiff;\n\tif (typeof hadDiff !== \"boolean\") return null;\n\n\tconst laneTypeRaw = candidate.laneType;\n\tlet laneType: AdaptorchLaneType | undefined;\n\tif (laneTypeRaw !== undefined) {\n\t\tif (typeof laneTypeRaw !== \"string\" || !LANE_TYPE_SET.has(laneTypeRaw)) return null;\n\t\tlaneType = laneTypeRaw as AdaptorchLaneType;\n\t}\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\trunnerUp: runnerUp as AdaptorchTaskClass,\n\t\tmarginBucket: marginBucket as AdaptorchMarginBucket,\n\t\tlenBucket: clampLenBucket(lenBucketRaw),\n\t\thadFence,\n\t\thadDiff,\n\t\tpressureBucket: clampPressureBucket(pressureBucketRaw),\n\t\t...(laneType !== undefined ? { laneType } : {}),\n\t};\n}\n\n/**\n * Sanitize/validate an inbound advisory result (i.e. whatever the injected\n * advisory function returned). Returns `null` (never throws) on any shape,\n * forbidden-field, extra-key, or unknown-enum violation - the caller\n * (`AdaptorchBridge.consult`) treats `null` as \"no hint\" and falls back\n * silently, exactly like every other failure mode.\n */\nexport function sanitizeAdvisoryResult(candidate: unknown): AdaptorchAdvisoryResult | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, ADVISORY_RESULT_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst confidenceBand = candidate.confidenceBand;\n\tif (typeof confidenceBand !== \"string\" || !CONFIDENCE_BAND_SET.has(confidenceBand)) return null;\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\tconfidenceBand: confidenceBand as AdaptorchConfidenceBand,\n\t};\n}\n\n/**\n * Read-only/local AdaptOrch MCP tool names this bridge may ever reference,\n * grounded in the eleven-tool surface documented in\n * packages/adaptorch-wpl/src/adaptorch-client.ts (cross-checked there\n * against the AdaptOrch server's own docs) and narrowed to the two tools the\n * read-first plan puts in scope for a reasoning-effort hint (plan 4.2): the\n * session-start capabilities probe and the per-consult topology\n * classification call. This module does not call any MCP tool directly\n * today - the advisory function is fully caller-injected - so this\n * allowlist is not wired to a transport here; it exists so a future\n * transport, and this module's own tests, can assert by construction that\n * `adaptorch_run` (submit) and `adaptorch_cancel_run` (cancel), or any other\n * mutating tool, can never be reachable from router/bridge code.\n */\nexport const ADAPTORCH_READONLY_TOOL_ALLOWLIST = [\"adaptorch_capabilities\", \"adaptorch_route_topology\"] as const;\n\nexport type AdaptorchReadonlyToolName = (typeof ADAPTORCH_READONLY_TOOL_ALLOWLIST)[number];\n\n/** Underscore-segment verbs that mark an AdaptOrch-shaped tool name as mutating (write/execute/lifecycle). */\nconst MUTATING_TOOL_VERB_SEGMENTS: ReadonlySet<string> = new Set([\n\t\"run\",\n\t\"submit\",\n\t\"cancel\",\n\t\"dispatch\",\n\t\"delete\",\n\t\"execute\",\n\t\"mutate\",\n\t\"write\",\n\t\"create\",\n\t\"update\",\n\t\"remove\",\n]);\n\n/**\n * True if any underscore-delimited segment of an AdaptOrch-shaped tool name\n * is a known mutating verb. Segment-based rather than a raw substring/regex\n * match so \"adaptorch_route_topology\" is never falsely flagged (no segment\n * is a mutating verb), while \"adaptorch_run\" and \"adaptorch_cancel_run\" are\n * always caught regardless of future prefix/suffix changes.\n */\nexport function looksMutatingToolName(name: string): boolean {\n\treturn name\n\t\t.toLowerCase()\n\t\t.split(\"_\")\n\t\t.some((segment) => MUTATING_TOOL_VERB_SEGMENTS.has(segment));\n}\n\n/**\n * Type guard: true only for the two allowlisted read/local AdaptOrch tool\n * names. Also independently rejects any name `looksMutatingToolName` flags,\n * so a future accidental addition of a mutating name to the allowlist array\n * still cannot make this function report it as allowed.\n */\nexport function isAdaptorchToolAllowed(name: string): name is AdaptorchReadonlyToolName {\n\tif (looksMutatingToolName(name)) return false;\n\treturn (ADAPTORCH_READONLY_TOOL_ALLOWLIST as readonly string[]).includes(name);\n}\n\n/**\n * Timeout wrapper interface around any injected async advisory call. Races\n * the call against a timer; a slow or hung call rejects at `timeoutMs`\n * instead of leaving the caller waiting indefinitely. Always clears the\n * timer once the call settles (or once the timeout itself fires), and\n * always resolves/rejects exactly once - a synchronous throw from `run` is\n * treated the same as an asynchronous rejection.\n */\nexport type AdvisoryTimeoutWrapper = <T>(run: () => Promise<T>, timeoutMs: number) => Promise<T>;\n\nexport const withAdvisoryTimeout: AdvisoryTimeoutWrapper = <T>(\n\trun: () => Promise<T>,\n\ttimeoutMs: number,\n): Promise<T> => {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\treject(new Error(\"adaptorch-bridge: advisory call timed out\"));\n\t\t}, timeoutMs);\n\n\t\tlet pending: Promise<T>;\n\t\ttry {\n\t\t\tpending = run();\n\t\t} catch (err) {\n\t\t\tclearTimeout(timer);\n\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\treturn;\n\t\t}\n\n\t\tpending.then(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(err: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t},\n\t\t);\n\t});\n};\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\nconst MIN_TTL_MS = 30 * 1000;\nconst MAX_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_MAX_TURNS_PER_ENTRY = 10;\nconst DEFAULT_CACHE_SIZE = 16;\nconst DEFAULT_MAX_CONSULTS_PER_SESSION = 5;\nconst DEFAULT_MIN_INTERVAL_MS = 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 1500;\nconst MIN_TIMEOUT_MS = 1;\nconst MAX_TIMEOUT_MS = 30 * 1000;\nconst DEFAULT_FAILURE_THRESHOLD = 3;\nconst DEFAULT_COOLDOWN_MS = 10 * 60 * 1000;\n\n/**\n * Exact TTL/cache/budget/circuit-breaker defaults from the read-first plan,\n * section 4.8. Exported so a security/review lane can confirm the shipped\n * numbers without reading this module's internals.\n */\nexport const ADAPTORCH_BRIDGE_DEFAULTS = {\n\tttlMs: DEFAULT_TTL_MS,\n\tminTtlMs: MIN_TTL_MS,\n\tmaxTtlMs: MAX_TTL_MS,\n\tmaxTurnsPerEntry: DEFAULT_MAX_TURNS_PER_ENTRY,\n\tcacheSize: DEFAULT_CACHE_SIZE,\n\tmaxConsultsPerSession: DEFAULT_MAX_CONSULTS_PER_SESSION,\n\tminIntervalMs: DEFAULT_MIN_INTERVAL_MS,\n\ttimeoutMs: DEFAULT_TIMEOUT_MS,\n\tminTimeoutMs: MIN_TIMEOUT_MS,\n\tmaxTimeoutMs: MAX_TIMEOUT_MS,\n\tfailureThreshold: DEFAULT_FAILURE_THRESHOLD,\n\tcooldownMs: DEFAULT_COOLDOWN_MS,\n} as const;\n\n/**\n * Bridge configuration. `advisoryFn` is the ONLY seam to any external\n * system - this module never imports or calls an MCP client/transport\n * itself, so it is impossible for a unit test (or a misconfigured caller)\n * to reach a real MCP server through this module by accident.\n */\nexport interface AdaptorchBridgeConfig {\n\t/**\n\t * Caller-injected advisory call. Its return value is untrusted: `consult`\n\t * always runs it through `sanitizeAdvisoryResult` before using or caching\n\t * it, and a rejected/thrown/timed-out/schema-invalid response is treated\n\t * as an ordinary failure (counts toward the circuit breaker), never as a\n\t * value to fuse.\n\t */\n\treadonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\t/** Injectable clock; defaults to `Date.now`. Tests supply a fake to avoid real wall-clock waits. */\n\treadonly now?: () => number;\n\t/** Cache TTL in ms; clamped to [30s, 5min] (plan 4.8). Default 5 minutes. */\n\treadonly ttlMs?: number;\n\t/** A cache entry also expires after this many `getFreshHint` calls (\"turns\"). Default 10. */\n\treadonly maxTurnsPerEntry?: number;\n\t/** Max distinct cached payload keys (LRU eviction beyond this). Default 16. */\n\treadonly cacheSize?: number;\n\t/** Max advisory attempts for this bridge instance's lifetime (\"session\"). Default 5. */\n\treadonly maxConsultsPerSession?: number;\n\t/** Minimum ms between advisory attempts. Default 60000 (60s). */\n\treadonly minIntervalMs?: number;\n\t/** Per-attempt timeout in ms; clamped to [1ms, 30s]. Default 1500 (1.5s). */\n\treadonly timeoutMs?: number;\n\t/** Consecutive failures before the circuit opens. Default 3. */\n\treadonly failureThreshold?: number;\n\t/** Circuit-open duration in ms once opened. Default 600000 (10min). */\n\treadonly cooldownMs?: number;\n}\n\n/** Read-only, non-sensitive introspection snapshot - never exposes payload/result content. */\nexport interface AdaptorchBridgeStats {\n\treadonly consultCount: number;\n\treadonly failureStreak: number;\n\treadonly circuitOpen: boolean;\n\treadonly circuitOpenUntilMs: number;\n\treadonly cacheEntryCount: number;\n}\n\nexport interface AdaptorchBridge {\n\t/**\n\t * Synchronous, cache-read-only. Never blocks, never throws, and never\n\t * calls the advisory function - a cache miss or an expired/invalid entry\n\t * silently returns `null` (\"no hint\"), matching the turn-start path's\n\t * synchronous, I/O-free contract (plan 4.4).\n\t */\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null;\n\t/**\n\t * Fire-and-forget refresh for a LATER call to `getFreshHint`, never the\n\t * current one. Never throws synchronously and never rejects into the\n\t * caller - any failure is swallowed internally by `consult`.\n\t */\n\trequestRefresh(payload: AdaptorchConsultPayload): void;\n\t/**\n\t * The awaitable core operation `requestRefresh` fires without awaiting.\n\t * Gated by the circuit breaker, budget counter, and minimum interval (in\n\t * that order) before ever invoking `advisoryFn`; always resolves (never\n\t * rejects) to either a sanitized hint or `null`.\n\t */\n\tconsult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null>;\n\t/** Read-only introspection for tests/diagnostics; contains no payload or result content. */\n\tgetStats(): AdaptorchBridgeStats;\n}\n\ninterface CacheEntry {\n\treadonly result: AdaptorchAdvisoryResult;\n\treadonly insertedAtMs: number;\n\treadonly insertedAtTurn: number;\n}\n\n/** Deterministic cache key built field-by-field from a sanitized payload - never from raw prompt text. */\nfunction cacheKeyFor(payload: AdaptorchConsultPayload): string {\n\treturn JSON.stringify({\n\t\ttaskClass: payload.taskClass,\n\t\trunnerUp: payload.runnerUp,\n\t\tmarginBucket: payload.marginBucket,\n\t\tlenBucket: payload.lenBucket,\n\t\thadFence: payload.hadFence,\n\t\thadDiff: payload.hadDiff,\n\t\tpressureBucket: payload.pressureBucket,\n\t\tlaneType: payload.laneType ?? null,\n\t});\n}\n\nclass AdaptorchBridgeImpl implements AdaptorchBridge {\n\tprivate readonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\tprivate readonly now: () => number;\n\tprivate readonly ttlMs: number;\n\tprivate readonly maxTurnsPerEntry: number;\n\tprivate readonly cacheSize: number;\n\tprivate readonly maxConsultsPerSession: number;\n\tprivate readonly minIntervalMs: number;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly failureThreshold: number;\n\tprivate readonly cooldownMs: number;\n\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate turnCounter = 0;\n\tprivate consultCount = 0;\n\tprivate failureStreak = 0;\n\tprivate lastAttemptAtMs: number | null = null;\n\tprivate circuitOpenUntilMs = 0;\n\n\tconstructor(config: AdaptorchBridgeConfig) {\n\t\tthis.advisoryFn = config.advisoryFn;\n\t\tthis.now = config.now ?? Date.now;\n\t\tthis.ttlMs = clampInt(config.ttlMs ?? DEFAULT_TTL_MS, MIN_TTL_MS, MAX_TTL_MS);\n\t\tthis.maxTurnsPerEntry = boundedIntOrDefault(config.maxTurnsPerEntry, DEFAULT_MAX_TURNS_PER_ENTRY, 1);\n\t\tthis.cacheSize = boundedIntOrDefault(config.cacheSize, DEFAULT_CACHE_SIZE, 1);\n\t\tthis.maxConsultsPerSession = boundedIntOrDefault(\n\t\t\tconfig.maxConsultsPerSession,\n\t\t\tDEFAULT_MAX_CONSULTS_PER_SESSION,\n\t\t\t0,\n\t\t);\n\t\tthis.minIntervalMs = boundedIntOrDefault(config.minIntervalMs, DEFAULT_MIN_INTERVAL_MS, 0);\n\t\tthis.timeoutMs = clampInt(config.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);\n\t\tthis.failureThreshold = boundedIntOrDefault(config.failureThreshold, DEFAULT_FAILURE_THRESHOLD, 1);\n\t\tthis.cooldownMs = boundedIntOrDefault(config.cooldownMs, DEFAULT_COOLDOWN_MS, 0);\n\t}\n\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null {\n\t\tthis.turnCounter += 1;\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst key = cacheKeyFor(sanitizedPayload);\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry === undefined) return null;\n\t\tif (!this.isEntryFresh(entry)) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\treturn entry.result;\n\t}\n\n\trequestRefresh(payload: AdaptorchConsultPayload): void {\n\t\tvoid this.consult(payload).catch(() => {\n\t\t\t// consult() already swallows every failure and always resolves; this\n\t\t\t// catch only guards a future refactor from letting one escape, so a\n\t\t\t// fire-and-forget refresh can never surface an unhandled rejection.\n\t\t});\n\t}\n\n\tasync consult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null> {\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst nowMs = this.now();\n\t\tif (nowMs < this.circuitOpenUntilMs) return null;\n\t\tif (this.consultCount >= this.maxConsultsPerSession) return null;\n\t\tif (this.lastAttemptAtMs !== null && nowMs - this.lastAttemptAtMs < this.minIntervalMs) return null;\n\n\t\tthis.consultCount += 1;\n\t\tthis.lastAttemptAtMs = nowMs;\n\n\t\tlet raw: unknown;\n\t\ttry {\n\t\t\traw = await withAdvisoryTimeout(() => this.advisoryFn(sanitizedPayload), this.timeoutMs);\n\t\t} catch {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = sanitizeAdvisoryResult(raw);\n\t\tif (result === null) {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.failureStreak = 0;\n\t\tthis.cacheResult(sanitizedPayload, result);\n\t\treturn result;\n\t}\n\n\tgetStats(): AdaptorchBridgeStats {\n\t\tconst nowMs = this.now();\n\t\treturn {\n\t\t\tconsultCount: this.consultCount,\n\t\t\tfailureStreak: this.failureStreak,\n\t\t\tcircuitOpen: nowMs < this.circuitOpenUntilMs,\n\t\t\tcircuitOpenUntilMs: this.circuitOpenUntilMs,\n\t\t\tcacheEntryCount: this.cache.size,\n\t\t};\n\t}\n\n\tprivate recordFailure(): void {\n\t\tthis.failureStreak += 1;\n\t\tif (this.failureStreak >= this.failureThreshold) {\n\t\t\tthis.circuitOpenUntilMs = this.now() + this.cooldownMs;\n\t\t}\n\t}\n\n\tprivate isEntryFresh(entry: CacheEntry): boolean {\n\t\tconst ageMs = this.now() - entry.insertedAtMs;\n\t\tconst ageTurns = this.turnCounter - entry.insertedAtTurn;\n\t\treturn ageMs < this.ttlMs && ageTurns < this.maxTurnsPerEntry;\n\t}\n\n\t/** Eviction favors entries last refreshed by a successful consult (write-time LRU, not read-time). */\n\tprivate cacheResult(payload: AdaptorchConsultPayload, result: AdaptorchAdvisoryResult): void {\n\t\tconst key = cacheKeyFor(payload);\n\t\tif (this.cache.has(key)) this.cache.delete(key);\n\t\tthis.cache.set(key, { result, insertedAtMs: this.now(), insertedAtTurn: this.turnCounter });\n\t\twhile (this.cache.size > this.cacheSize) {\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey === undefined) break;\n\t\t\tthis.cache.delete(oldestKey);\n\t\t}\n\t}\n}\n\n/** Construct a default-off AdaptOrch advisory bridge. See `AdaptorchBridgeConfig` for tunables. */\nexport function createAdaptorchBridge(config: AdaptorchBridgeConfig): AdaptorchBridge {\n\treturn new AdaptorchBridgeImpl(config);\n}\n"]}
|
|
@@ -223,7 +223,7 @@ export function sanitizeAdvisoryResult(candidate) {
|
|
|
223
223
|
}
|
|
224
224
|
/**
|
|
225
225
|
* Read-only/local AdaptOrch MCP tool names this bridge may ever reference,
|
|
226
|
-
* grounded in the
|
|
226
|
+
* grounded in the eleven-tool surface documented in
|
|
227
227
|
* packages/adaptorch-wpl/src/adaptorch-client.ts (cross-checked there
|
|
228
228
|
* against the AdaptOrch server's own docs) and narrowed to the two tools the
|
|
229
229
|
* read-first plan puts in scope for a reasoning-effort hint (plan 4.2): the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptorch-bridge.js","sourceRoot":"","sources":["../../src/core/adaptorch-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router AdaptOrch advisory bridge (Goal 009 Lane B).\n *\n * A default-off, advisory-only primitive wired into AgentSession's v4 resolver.\n * AgentSession reads only the bridge's bounded cache and uses a caller-injected\n * advisory function for refresh; the current production transport is a no-op,\n * so it returns no hint. This module owns payload/result validation, TTL cache,\n * budget counter, circuit breaker, and timeout handling. It never calls an MCP\n * transport directly or receives execution authority.\n *\n * Grounded in the read-first plan at\n * .omk/goals/008-reasoning-router-advanced-accuracy-plan/laneC-privacy-adaptorch.md\n * (Part B, sections 4.1-4.11) and the real AdaptOrch MCP tool surface\n * documented in packages/adaptorch-wpl/src/adaptorch-client.ts. Key\n * constraints carried over from that plan:\n *\n * - The router's turn-start path is synchronous and I/O-free today (plan\n * 4.4). This module never calls an MCP transport directly and exposes a\n * synchronous, cache-read-only accessor (`getFreshHint`) plus a\n * fire-and-forget refresh (`requestRefresh`) so the AgentSession integration\n * never blocks or awaits advisory I/O on the turn-start path.\n * - The real, documented AdaptOrch tool surface has no confidence or\n * reasoning-level field (plan 4.3); this module's inbound result type\n * (`AdaptorchAdvisoryResult`) therefore only carries a locally-relevant\n * `taskClass` and a `confidenceBand` - both closed enums - never a raw\n * numeric confidence or a `ThinkingLevel` value. AgentSession owns the\n * bounded fusion from this advisory shape into the v4 resolver.\n * - No product source is imported here. `AdaptorchTaskClass` /\n * `AdaptorchLaneType` intentionally mirror the v4 task/lane unions by\n * value, while AgentSession adapts them at the integration boundary.\n * - Every outbound/inbound field is a closed enum, a boolean, or a bounded\n * integer, derived from local features only. There is structurally no\n * field shaped to carry prompt text, a prompt hash, a file/working-\n * directory path, a model/provider identifier, a session/user identifier,\n * a tool name, or hook output - the sanitize/validate helpers below also\n * reject any candidate object exposing such a field by name, and reject\n * any key outside the exact closed schema, so a hand-built or forged\n * payload can never smuggle one through either type.\n * - The router-bridge tool allowlist (`ADAPTORCH_READONLY_TOOL_ALLOWLIST`)\n * only ever contains the two read/local AdaptOrch tools the plan\n * identifies as in scope (the session-start capabilities probe and the\n * per-consult topology-classification call, plan 4.2/4.6); the\n * run-submission and run-cancellation tools (and any other mutating\n * tool) can never appear in it, checked both by an exact-value test and\n * by `looksMutatingToolName`'s verb-segment guard.\n *\n * Pure aside from `Date.now`/`setTimeout` (both injectable/wrapped so tests\n * never depend on real wall-clock time or a real MCP call).\n */\n\n/** Closed set of task classes. Mirrors the v4 task-class union by value (see file header). */\nexport type AdaptorchTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Closed set of subagent lane types. Mirrors the v4 lane union by value. */\nexport type AdaptorchLaneType = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Local top1-vs-runner-up score-margin bucket (plan 4.5 ConsultPayloadV1.marginBucket). */\nexport type AdaptorchMarginBucket = \"tie\" | \"low\" | \"medium\" | \"high\";\n\n/** Locally-derived confidence band for a returned hint. Never a raw network-supplied float (plan 4.3). */\nexport type AdaptorchConfidenceBand = \"low\" | \"medium\" | \"high\";\n\n/** floor(log2(trimmedPromptLen+1)) clamped to [0,7]. */\nexport type AdaptorchLenBucket = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/** Context-pressure band 0..3 - same bucketing as agent-session.ts's _computePressureBucket. */\nexport type AdaptorchPressureBucket = 0 | 1 | 2 | 3;\n\n/**\n * Outbound advisory request (plan 4.5, field names adapted for this lane's\n * assignment: taskClass/runnerUp/marginBucket/lenBucket/hadFence/hadDiff/\n * pressureBucket/laneType). Every field is a closed enum, a boolean, or a\n * bounded integer built from local classifier features only. Structurally\n * excludes prompt text/hash, file paths, model/provider/session\n * identifiers, tool names, and hook output - there is no field shaped to\n * carry any of those.\n */\nexport interface AdaptorchConsultPayload {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly runnerUp: AdaptorchTaskClass;\n\treadonly marginBucket: AdaptorchMarginBucket;\n\treadonly lenBucket: AdaptorchLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n\treadonly pressureBucket: AdaptorchPressureBucket;\n\treadonly laneType?: AdaptorchLaneType;\n}\n\n/**\n * Inbound advisory result after local sanitation (plan 4.3, corrected\n * schema). `confidenceBand` is always synthesized/validated locally - the\n * real AdaptOrch tool contract carries no confidence or effort-level field,\n * so this type never trusts a raw numeric confidence from the advisory\n * function.\n */\nexport interface AdaptorchAdvisoryResult {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly confidenceBand: AdaptorchConfidenceBand;\n}\n\nconst TASK_CLASSES: readonly AdaptorchTaskClass[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\nconst TASK_CLASS_SET: ReadonlySet<string> = new Set(TASK_CLASSES);\n\nconst LANE_TYPES: readonly AdaptorchLaneType[] = [\"planner\", \"security\", \"explorer\", \"coder\", \"reviewer\", \"tester\"];\nconst LANE_TYPE_SET: ReadonlySet<string> = new Set(LANE_TYPES);\n\nconst MARGIN_BUCKETS: readonly AdaptorchMarginBucket[] = [\"tie\", \"low\", \"medium\", \"high\"];\nconst MARGIN_BUCKET_SET: ReadonlySet<string> = new Set(MARGIN_BUCKETS);\n\nconst CONFIDENCE_BANDS: readonly AdaptorchConfidenceBand[] = [\"low\", \"medium\", \"high\"];\nconst CONFIDENCE_BAND_SET: ReadonlySet<string> = new Set(CONFIDENCE_BANDS);\n\nconst CONSULT_PAYLOAD_KEYS: ReadonlySet<string> = new Set([\n\t\"schemaVersion\",\n\t\"taskClass\",\n\t\"runnerUp\",\n\t\"marginBucket\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"pressureBucket\",\n\t\"laneType\",\n]);\n\nconst ADVISORY_RESULT_KEYS: ReadonlySet<string> = new Set([\"schemaVersion\", \"taskClass\", \"confidenceBand\"]);\n\n/**\n * Field-name markers that must never appear on a payload or result object,\n * checked case-insensitively as a substring of every own key. This is\n * defense-in-depth alongside the exact-key allowlist below: even a candidate\n * that otherwise has every required field still fails if it also carries a\n * field shaped like a stored prompt, hash, path, model/provider identifier,\n * session identifier, tool name, or hook output.\n */\nconst FORBIDDEN_KEY_SUBSTRINGS: readonly string[] = [\n\t\"prompt\",\n\t\"hash\",\n\t\"path\",\n\t\"model\",\n\t\"session\",\n\t\"tool\",\n\t\"hook\",\n\t\"provider\",\n];\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * True if any own key of `record` contains a forbidden field-name marker\n * (case-insensitive). Typed `object` rather than `Record<string, unknown>` so\n * callers (including tests) can pass any concrete object shape - named\n * interface, ad-hoc literal, or an already-`Record`-typed value - without a\n * missing-index-signature type error; only `Object.keys` is used internally,\n * which accepts any `object`.\n */\nexport function containsForbiddenField(record: object): boolean {\n\tfor (const key of Object.keys(record)) {\n\t\tconst lowerKey = key.toLowerCase();\n\t\tif (FORBIDDEN_KEY_SUBSTRINGS.some((needle) => lowerKey.includes(needle))) return true;\n\t}\n\treturn false;\n}\n\nfunction hasOnlyAllowedKeys(record: Record<string, unknown>, allowed: ReadonlySet<string>): boolean {\n\treturn Object.keys(record).every((key) => allowed.has(key));\n}\n\nfunction clampInt(value: number, min: number, max: number): number {\n\tconst truncated = Number.isFinite(value) ? Math.trunc(value) : min;\n\tif (truncated < min) return min;\n\tif (truncated > max) return max;\n\treturn truncated;\n}\n\nfunction clampLenBucket(value: number): AdaptorchLenBucket {\n\treturn clampInt(value, 0, 7) as AdaptorchLenBucket;\n}\n\nfunction clampPressureBucket(value: number): AdaptorchPressureBucket {\n\treturn clampInt(value, 0, 3) as AdaptorchPressureBucket;\n}\n\n/** `value ?? fallback`, coerced to an integer and floored at `min` (used for config, not untrusted payloads). */\nfunction boundedIntOrDefault(value: number | undefined, fallback: number, min: number): number {\n\tconst raw = value ?? fallback;\n\tif (!Number.isFinite(raw)) return fallback;\n\treturn Math.max(min, Math.trunc(raw));\n}\n\n/**\n * Sanitize/validate an outbound advisory request. Returns `null` (never\n * throws) when `candidate` is not a plain object, carries a forbidden-named\n * field, carries any key outside the exact closed schema, or has a\n * wrong-typed/unknown-enum field. `lenBucket`/`pressureBucket` are clamped\n * into their bounded ranges rather than rejected, so a caller-side rounding\n * or edge-case computation can never smuggle an out-of-range integer through.\n */\nexport function sanitizeConsultPayload(candidate: unknown): AdaptorchConsultPayload | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, CONSULT_PAYLOAD_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst runnerUp = candidate.runnerUp;\n\tif (typeof runnerUp !== \"string\" || !TASK_CLASS_SET.has(runnerUp)) return null;\n\n\tconst marginBucket = candidate.marginBucket;\n\tif (typeof marginBucket !== \"string\" || !MARGIN_BUCKET_SET.has(marginBucket)) return null;\n\n\tconst lenBucketRaw = candidate.lenBucket;\n\tif (typeof lenBucketRaw !== \"number\") return null;\n\n\tconst pressureBucketRaw = candidate.pressureBucket;\n\tif (typeof pressureBucketRaw !== \"number\") return null;\n\n\tconst hadFence = candidate.hadFence;\n\tif (typeof hadFence !== \"boolean\") return null;\n\n\tconst hadDiff = candidate.hadDiff;\n\tif (typeof hadDiff !== \"boolean\") return null;\n\n\tconst laneTypeRaw = candidate.laneType;\n\tlet laneType: AdaptorchLaneType | undefined;\n\tif (laneTypeRaw !== undefined) {\n\t\tif (typeof laneTypeRaw !== \"string\" || !LANE_TYPE_SET.has(laneTypeRaw)) return null;\n\t\tlaneType = laneTypeRaw as AdaptorchLaneType;\n\t}\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\trunnerUp: runnerUp as AdaptorchTaskClass,\n\t\tmarginBucket: marginBucket as AdaptorchMarginBucket,\n\t\tlenBucket: clampLenBucket(lenBucketRaw),\n\t\thadFence,\n\t\thadDiff,\n\t\tpressureBucket: clampPressureBucket(pressureBucketRaw),\n\t\t...(laneType !== undefined ? { laneType } : {}),\n\t};\n}\n\n/**\n * Sanitize/validate an inbound advisory result (i.e. whatever the injected\n * advisory function returned). Returns `null` (never throws) on any shape,\n * forbidden-field, extra-key, or unknown-enum violation - the caller\n * (`AdaptorchBridge.consult`) treats `null` as \"no hint\" and falls back\n * silently, exactly like every other failure mode.\n */\nexport function sanitizeAdvisoryResult(candidate: unknown): AdaptorchAdvisoryResult | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, ADVISORY_RESULT_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst confidenceBand = candidate.confidenceBand;\n\tif (typeof confidenceBand !== \"string\" || !CONFIDENCE_BAND_SET.has(confidenceBand)) return null;\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\tconfidenceBand: confidenceBand as AdaptorchConfidenceBand,\n\t};\n}\n\n/**\n * Read-only/local AdaptOrch MCP tool names this bridge may ever reference,\n * grounded in the real 10-tool surface documented in\n * packages/adaptorch-wpl/src/adaptorch-client.ts (cross-checked there\n * against the AdaptOrch server's own docs) and narrowed to the two tools the\n * read-first plan puts in scope for a reasoning-effort hint (plan 4.2): the\n * session-start capabilities probe and the per-consult topology\n * classification call. This module does not call any MCP tool directly\n * today - the advisory function is fully caller-injected - so this\n * allowlist is not wired to a transport here; it exists so a future\n * transport, and this module's own tests, can assert by construction that\n * `adaptorch_run` (submit) and `adaptorch_cancel_run` (cancel), or any other\n * mutating tool, can never be reachable from router/bridge code.\n */\nexport const ADAPTORCH_READONLY_TOOL_ALLOWLIST = [\"adaptorch_capabilities\", \"adaptorch_route_topology\"] as const;\n\nexport type AdaptorchReadonlyToolName = (typeof ADAPTORCH_READONLY_TOOL_ALLOWLIST)[number];\n\n/** Underscore-segment verbs that mark an AdaptOrch-shaped tool name as mutating (write/execute/lifecycle). */\nconst MUTATING_TOOL_VERB_SEGMENTS: ReadonlySet<string> = new Set([\n\t\"run\",\n\t\"submit\",\n\t\"cancel\",\n\t\"dispatch\",\n\t\"delete\",\n\t\"execute\",\n\t\"mutate\",\n\t\"write\",\n\t\"create\",\n\t\"update\",\n\t\"remove\",\n]);\n\n/**\n * True if any underscore-delimited segment of an AdaptOrch-shaped tool name\n * is a known mutating verb. Segment-based rather than a raw substring/regex\n * match so \"adaptorch_route_topology\" is never falsely flagged (no segment\n * is a mutating verb), while \"adaptorch_run\" and \"adaptorch_cancel_run\" are\n * always caught regardless of future prefix/suffix changes.\n */\nexport function looksMutatingToolName(name: string): boolean {\n\treturn name\n\t\t.toLowerCase()\n\t\t.split(\"_\")\n\t\t.some((segment) => MUTATING_TOOL_VERB_SEGMENTS.has(segment));\n}\n\n/**\n * Type guard: true only for the two allowlisted read/local AdaptOrch tool\n * names. Also independently rejects any name `looksMutatingToolName` flags,\n * so a future accidental addition of a mutating name to the allowlist array\n * still cannot make this function report it as allowed.\n */\nexport function isAdaptorchToolAllowed(name: string): name is AdaptorchReadonlyToolName {\n\tif (looksMutatingToolName(name)) return false;\n\treturn (ADAPTORCH_READONLY_TOOL_ALLOWLIST as readonly string[]).includes(name);\n}\n\n/**\n * Timeout wrapper interface around any injected async advisory call. Races\n * the call against a timer; a slow or hung call rejects at `timeoutMs`\n * instead of leaving the caller waiting indefinitely. Always clears the\n * timer once the call settles (or once the timeout itself fires), and\n * always resolves/rejects exactly once - a synchronous throw from `run` is\n * treated the same as an asynchronous rejection.\n */\nexport type AdvisoryTimeoutWrapper = <T>(run: () => Promise<T>, timeoutMs: number) => Promise<T>;\n\nexport const withAdvisoryTimeout: AdvisoryTimeoutWrapper = <T>(\n\trun: () => Promise<T>,\n\ttimeoutMs: number,\n): Promise<T> => {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\treject(new Error(\"adaptorch-bridge: advisory call timed out\"));\n\t\t}, timeoutMs);\n\n\t\tlet pending: Promise<T>;\n\t\ttry {\n\t\t\tpending = run();\n\t\t} catch (err) {\n\t\t\tclearTimeout(timer);\n\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\treturn;\n\t\t}\n\n\t\tpending.then(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(err: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t},\n\t\t);\n\t});\n};\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\nconst MIN_TTL_MS = 30 * 1000;\nconst MAX_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_MAX_TURNS_PER_ENTRY = 10;\nconst DEFAULT_CACHE_SIZE = 16;\nconst DEFAULT_MAX_CONSULTS_PER_SESSION = 5;\nconst DEFAULT_MIN_INTERVAL_MS = 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 1500;\nconst MIN_TIMEOUT_MS = 1;\nconst MAX_TIMEOUT_MS = 30 * 1000;\nconst DEFAULT_FAILURE_THRESHOLD = 3;\nconst DEFAULT_COOLDOWN_MS = 10 * 60 * 1000;\n\n/**\n * Exact TTL/cache/budget/circuit-breaker defaults from the read-first plan,\n * section 4.8. Exported so a security/review lane can confirm the shipped\n * numbers without reading this module's internals.\n */\nexport const ADAPTORCH_BRIDGE_DEFAULTS = {\n\tttlMs: DEFAULT_TTL_MS,\n\tminTtlMs: MIN_TTL_MS,\n\tmaxTtlMs: MAX_TTL_MS,\n\tmaxTurnsPerEntry: DEFAULT_MAX_TURNS_PER_ENTRY,\n\tcacheSize: DEFAULT_CACHE_SIZE,\n\tmaxConsultsPerSession: DEFAULT_MAX_CONSULTS_PER_SESSION,\n\tminIntervalMs: DEFAULT_MIN_INTERVAL_MS,\n\ttimeoutMs: DEFAULT_TIMEOUT_MS,\n\tminTimeoutMs: MIN_TIMEOUT_MS,\n\tmaxTimeoutMs: MAX_TIMEOUT_MS,\n\tfailureThreshold: DEFAULT_FAILURE_THRESHOLD,\n\tcooldownMs: DEFAULT_COOLDOWN_MS,\n} as const;\n\n/**\n * Bridge configuration. `advisoryFn` is the ONLY seam to any external\n * system - this module never imports or calls an MCP client/transport\n * itself, so it is impossible for a unit test (or a misconfigured caller)\n * to reach a real MCP server through this module by accident.\n */\nexport interface AdaptorchBridgeConfig {\n\t/**\n\t * Caller-injected advisory call. Its return value is untrusted: `consult`\n\t * always runs it through `sanitizeAdvisoryResult` before using or caching\n\t * it, and a rejected/thrown/timed-out/schema-invalid response is treated\n\t * as an ordinary failure (counts toward the circuit breaker), never as a\n\t * value to fuse.\n\t */\n\treadonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\t/** Injectable clock; defaults to `Date.now`. Tests supply a fake to avoid real wall-clock waits. */\n\treadonly now?: () => number;\n\t/** Cache TTL in ms; clamped to [30s, 5min] (plan 4.8). Default 5 minutes. */\n\treadonly ttlMs?: number;\n\t/** A cache entry also expires after this many `getFreshHint` calls (\"turns\"). Default 10. */\n\treadonly maxTurnsPerEntry?: number;\n\t/** Max distinct cached payload keys (LRU eviction beyond this). Default 16. */\n\treadonly cacheSize?: number;\n\t/** Max advisory attempts for this bridge instance's lifetime (\"session\"). Default 5. */\n\treadonly maxConsultsPerSession?: number;\n\t/** Minimum ms between advisory attempts. Default 60000 (60s). */\n\treadonly minIntervalMs?: number;\n\t/** Per-attempt timeout in ms; clamped to [1ms, 30s]. Default 1500 (1.5s). */\n\treadonly timeoutMs?: number;\n\t/** Consecutive failures before the circuit opens. Default 3. */\n\treadonly failureThreshold?: number;\n\t/** Circuit-open duration in ms once opened. Default 600000 (10min). */\n\treadonly cooldownMs?: number;\n}\n\n/** Read-only, non-sensitive introspection snapshot - never exposes payload/result content. */\nexport interface AdaptorchBridgeStats {\n\treadonly consultCount: number;\n\treadonly failureStreak: number;\n\treadonly circuitOpen: boolean;\n\treadonly circuitOpenUntilMs: number;\n\treadonly cacheEntryCount: number;\n}\n\nexport interface AdaptorchBridge {\n\t/**\n\t * Synchronous, cache-read-only. Never blocks, never throws, and never\n\t * calls the advisory function - a cache miss or an expired/invalid entry\n\t * silently returns `null` (\"no hint\"), matching the turn-start path's\n\t * synchronous, I/O-free contract (plan 4.4).\n\t */\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null;\n\t/**\n\t * Fire-and-forget refresh for a LATER call to `getFreshHint`, never the\n\t * current one. Never throws synchronously and never rejects into the\n\t * caller - any failure is swallowed internally by `consult`.\n\t */\n\trequestRefresh(payload: AdaptorchConsultPayload): void;\n\t/**\n\t * The awaitable core operation `requestRefresh` fires without awaiting.\n\t * Gated by the circuit breaker, budget counter, and minimum interval (in\n\t * that order) before ever invoking `advisoryFn`; always resolves (never\n\t * rejects) to either a sanitized hint or `null`.\n\t */\n\tconsult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null>;\n\t/** Read-only introspection for tests/diagnostics; contains no payload or result content. */\n\tgetStats(): AdaptorchBridgeStats;\n}\n\ninterface CacheEntry {\n\treadonly result: AdaptorchAdvisoryResult;\n\treadonly insertedAtMs: number;\n\treadonly insertedAtTurn: number;\n}\n\n/** Deterministic cache key built field-by-field from a sanitized payload - never from raw prompt text. */\nfunction cacheKeyFor(payload: AdaptorchConsultPayload): string {\n\treturn JSON.stringify({\n\t\ttaskClass: payload.taskClass,\n\t\trunnerUp: payload.runnerUp,\n\t\tmarginBucket: payload.marginBucket,\n\t\tlenBucket: payload.lenBucket,\n\t\thadFence: payload.hadFence,\n\t\thadDiff: payload.hadDiff,\n\t\tpressureBucket: payload.pressureBucket,\n\t\tlaneType: payload.laneType ?? null,\n\t});\n}\n\nclass AdaptorchBridgeImpl implements AdaptorchBridge {\n\tprivate readonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\tprivate readonly now: () => number;\n\tprivate readonly ttlMs: number;\n\tprivate readonly maxTurnsPerEntry: number;\n\tprivate readonly cacheSize: number;\n\tprivate readonly maxConsultsPerSession: number;\n\tprivate readonly minIntervalMs: number;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly failureThreshold: number;\n\tprivate readonly cooldownMs: number;\n\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate turnCounter = 0;\n\tprivate consultCount = 0;\n\tprivate failureStreak = 0;\n\tprivate lastAttemptAtMs: number | null = null;\n\tprivate circuitOpenUntilMs = 0;\n\n\tconstructor(config: AdaptorchBridgeConfig) {\n\t\tthis.advisoryFn = config.advisoryFn;\n\t\tthis.now = config.now ?? Date.now;\n\t\tthis.ttlMs = clampInt(config.ttlMs ?? DEFAULT_TTL_MS, MIN_TTL_MS, MAX_TTL_MS);\n\t\tthis.maxTurnsPerEntry = boundedIntOrDefault(config.maxTurnsPerEntry, DEFAULT_MAX_TURNS_PER_ENTRY, 1);\n\t\tthis.cacheSize = boundedIntOrDefault(config.cacheSize, DEFAULT_CACHE_SIZE, 1);\n\t\tthis.maxConsultsPerSession = boundedIntOrDefault(\n\t\t\tconfig.maxConsultsPerSession,\n\t\t\tDEFAULT_MAX_CONSULTS_PER_SESSION,\n\t\t\t0,\n\t\t);\n\t\tthis.minIntervalMs = boundedIntOrDefault(config.minIntervalMs, DEFAULT_MIN_INTERVAL_MS, 0);\n\t\tthis.timeoutMs = clampInt(config.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);\n\t\tthis.failureThreshold = boundedIntOrDefault(config.failureThreshold, DEFAULT_FAILURE_THRESHOLD, 1);\n\t\tthis.cooldownMs = boundedIntOrDefault(config.cooldownMs, DEFAULT_COOLDOWN_MS, 0);\n\t}\n\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null {\n\t\tthis.turnCounter += 1;\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst key = cacheKeyFor(sanitizedPayload);\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry === undefined) return null;\n\t\tif (!this.isEntryFresh(entry)) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\treturn entry.result;\n\t}\n\n\trequestRefresh(payload: AdaptorchConsultPayload): void {\n\t\tvoid this.consult(payload).catch(() => {\n\t\t\t// consult() already swallows every failure and always resolves; this\n\t\t\t// catch only guards a future refactor from letting one escape, so a\n\t\t\t// fire-and-forget refresh can never surface an unhandled rejection.\n\t\t});\n\t}\n\n\tasync consult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null> {\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst nowMs = this.now();\n\t\tif (nowMs < this.circuitOpenUntilMs) return null;\n\t\tif (this.consultCount >= this.maxConsultsPerSession) return null;\n\t\tif (this.lastAttemptAtMs !== null && nowMs - this.lastAttemptAtMs < this.minIntervalMs) return null;\n\n\t\tthis.consultCount += 1;\n\t\tthis.lastAttemptAtMs = nowMs;\n\n\t\tlet raw: unknown;\n\t\ttry {\n\t\t\traw = await withAdvisoryTimeout(() => this.advisoryFn(sanitizedPayload), this.timeoutMs);\n\t\t} catch {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = sanitizeAdvisoryResult(raw);\n\t\tif (result === null) {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.failureStreak = 0;\n\t\tthis.cacheResult(sanitizedPayload, result);\n\t\treturn result;\n\t}\n\n\tgetStats(): AdaptorchBridgeStats {\n\t\tconst nowMs = this.now();\n\t\treturn {\n\t\t\tconsultCount: this.consultCount,\n\t\t\tfailureStreak: this.failureStreak,\n\t\t\tcircuitOpen: nowMs < this.circuitOpenUntilMs,\n\t\t\tcircuitOpenUntilMs: this.circuitOpenUntilMs,\n\t\t\tcacheEntryCount: this.cache.size,\n\t\t};\n\t}\n\n\tprivate recordFailure(): void {\n\t\tthis.failureStreak += 1;\n\t\tif (this.failureStreak >= this.failureThreshold) {\n\t\t\tthis.circuitOpenUntilMs = this.now() + this.cooldownMs;\n\t\t}\n\t}\n\n\tprivate isEntryFresh(entry: CacheEntry): boolean {\n\t\tconst ageMs = this.now() - entry.insertedAtMs;\n\t\tconst ageTurns = this.turnCounter - entry.insertedAtTurn;\n\t\treturn ageMs < this.ttlMs && ageTurns < this.maxTurnsPerEntry;\n\t}\n\n\t/** Eviction favors entries last refreshed by a successful consult (write-time LRU, not read-time). */\n\tprivate cacheResult(payload: AdaptorchConsultPayload, result: AdaptorchAdvisoryResult): void {\n\t\tconst key = cacheKeyFor(payload);\n\t\tif (this.cache.has(key)) this.cache.delete(key);\n\t\tthis.cache.set(key, { result, insertedAtMs: this.now(), insertedAtTurn: this.turnCounter });\n\t\twhile (this.cache.size > this.cacheSize) {\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey === undefined) break;\n\t\t\tthis.cache.delete(oldestKey);\n\t\t}\n\t}\n}\n\n/** Construct a default-off AdaptOrch advisory bridge. See `AdaptorchBridgeConfig` for tunables. */\nexport function createAdaptorchBridge(config: AdaptorchBridgeConfig): AdaptorchBridge {\n\treturn new AdaptorchBridgeImpl(config);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"adaptorch-bridge.js","sourceRoot":"","sources":["../../src/core/adaptorch-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reasoning-router AdaptOrch advisory bridge (Goal 009 Lane B).\n *\n * A default-off, advisory-only primitive wired into AgentSession's v4 resolver.\n * AgentSession reads only the bridge's bounded cache and uses a caller-injected\n * advisory function for refresh; the current production transport is a no-op,\n * so it returns no hint. This module owns payload/result validation, TTL cache,\n * budget counter, circuit breaker, and timeout handling. It never calls an MCP\n * transport directly or receives execution authority.\n *\n * Grounded in the read-first plan at\n * .omk/goals/008-reasoning-router-advanced-accuracy-plan/laneC-privacy-adaptorch.md\n * (Part B, sections 4.1-4.11) and the real AdaptOrch MCP tool surface\n * documented in packages/adaptorch-wpl/src/adaptorch-client.ts. Key\n * constraints carried over from that plan:\n *\n * - The router's turn-start path is synchronous and I/O-free today (plan\n * 4.4). This module never calls an MCP transport directly and exposes a\n * synchronous, cache-read-only accessor (`getFreshHint`) plus a\n * fire-and-forget refresh (`requestRefresh`) so the AgentSession integration\n * never blocks or awaits advisory I/O on the turn-start path.\n * - The real, documented AdaptOrch tool surface has no confidence or\n * reasoning-level field (plan 4.3); this module's inbound result type\n * (`AdaptorchAdvisoryResult`) therefore only carries a locally-relevant\n * `taskClass` and a `confidenceBand` - both closed enums - never a raw\n * numeric confidence or a `ThinkingLevel` value. AgentSession owns the\n * bounded fusion from this advisory shape into the v4 resolver.\n * - No product source is imported here. `AdaptorchTaskClass` /\n * `AdaptorchLaneType` intentionally mirror the v4 task/lane unions by\n * value, while AgentSession adapts them at the integration boundary.\n * - Every outbound/inbound field is a closed enum, a boolean, or a bounded\n * integer, derived from local features only. There is structurally no\n * field shaped to carry prompt text, a prompt hash, a file/working-\n * directory path, a model/provider identifier, a session/user identifier,\n * a tool name, or hook output - the sanitize/validate helpers below also\n * reject any candidate object exposing such a field by name, and reject\n * any key outside the exact closed schema, so a hand-built or forged\n * payload can never smuggle one through either type.\n * - The router-bridge tool allowlist (`ADAPTORCH_READONLY_TOOL_ALLOWLIST`)\n * only ever contains the two read/local AdaptOrch tools the plan\n * identifies as in scope (the session-start capabilities probe and the\n * per-consult topology-classification call, plan 4.2/4.6); the\n * run-submission and run-cancellation tools (and any other mutating\n * tool) can never appear in it, checked both by an exact-value test and\n * by `looksMutatingToolName`'s verb-segment guard.\n *\n * Pure aside from `Date.now`/`setTimeout` (both injectable/wrapped so tests\n * never depend on real wall-clock time or a real MCP call).\n */\n\n/** Closed set of task classes. Mirrors the v4 task-class union by value (see file header). */\nexport type AdaptorchTaskClass = \"trivial\" | \"simple-edit\" | \"code-gen\" | \"debug\" | \"refactor\" | \"review\" | \"plan\";\n\n/** Closed set of subagent lane types. Mirrors the v4 lane union by value. */\nexport type AdaptorchLaneType = \"planner\" | \"security\" | \"explorer\" | \"coder\" | \"reviewer\" | \"tester\";\n\n/** Local top1-vs-runner-up score-margin bucket (plan 4.5 ConsultPayloadV1.marginBucket). */\nexport type AdaptorchMarginBucket = \"tie\" | \"low\" | \"medium\" | \"high\";\n\n/** Locally-derived confidence band for a returned hint. Never a raw network-supplied float (plan 4.3). */\nexport type AdaptorchConfidenceBand = \"low\" | \"medium\" | \"high\";\n\n/** floor(log2(trimmedPromptLen+1)) clamped to [0,7]. */\nexport type AdaptorchLenBucket = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/** Context-pressure band 0..3 - same bucketing as agent-session.ts's _computePressureBucket. */\nexport type AdaptorchPressureBucket = 0 | 1 | 2 | 3;\n\n/**\n * Outbound advisory request (plan 4.5, field names adapted for this lane's\n * assignment: taskClass/runnerUp/marginBucket/lenBucket/hadFence/hadDiff/\n * pressureBucket/laneType). Every field is a closed enum, a boolean, or a\n * bounded integer built from local classifier features only. Structurally\n * excludes prompt text/hash, file paths, model/provider/session\n * identifiers, tool names, and hook output - there is no field shaped to\n * carry any of those.\n */\nexport interface AdaptorchConsultPayload {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly runnerUp: AdaptorchTaskClass;\n\treadonly marginBucket: AdaptorchMarginBucket;\n\treadonly lenBucket: AdaptorchLenBucket;\n\treadonly hadFence: boolean;\n\treadonly hadDiff: boolean;\n\treadonly pressureBucket: AdaptorchPressureBucket;\n\treadonly laneType?: AdaptorchLaneType;\n}\n\n/**\n * Inbound advisory result after local sanitation (plan 4.3, corrected\n * schema). `confidenceBand` is always synthesized/validated locally - the\n * real AdaptOrch tool contract carries no confidence or effort-level field,\n * so this type never trusts a raw numeric confidence from the advisory\n * function.\n */\nexport interface AdaptorchAdvisoryResult {\n\treadonly schemaVersion: 1;\n\treadonly taskClass: AdaptorchTaskClass;\n\treadonly confidenceBand: AdaptorchConfidenceBand;\n}\n\nconst TASK_CLASSES: readonly AdaptorchTaskClass[] = [\n\t\"trivial\",\n\t\"simple-edit\",\n\t\"code-gen\",\n\t\"debug\",\n\t\"refactor\",\n\t\"review\",\n\t\"plan\",\n];\nconst TASK_CLASS_SET: ReadonlySet<string> = new Set(TASK_CLASSES);\n\nconst LANE_TYPES: readonly AdaptorchLaneType[] = [\"planner\", \"security\", \"explorer\", \"coder\", \"reviewer\", \"tester\"];\nconst LANE_TYPE_SET: ReadonlySet<string> = new Set(LANE_TYPES);\n\nconst MARGIN_BUCKETS: readonly AdaptorchMarginBucket[] = [\"tie\", \"low\", \"medium\", \"high\"];\nconst MARGIN_BUCKET_SET: ReadonlySet<string> = new Set(MARGIN_BUCKETS);\n\nconst CONFIDENCE_BANDS: readonly AdaptorchConfidenceBand[] = [\"low\", \"medium\", \"high\"];\nconst CONFIDENCE_BAND_SET: ReadonlySet<string> = new Set(CONFIDENCE_BANDS);\n\nconst CONSULT_PAYLOAD_KEYS: ReadonlySet<string> = new Set([\n\t\"schemaVersion\",\n\t\"taskClass\",\n\t\"runnerUp\",\n\t\"marginBucket\",\n\t\"lenBucket\",\n\t\"hadFence\",\n\t\"hadDiff\",\n\t\"pressureBucket\",\n\t\"laneType\",\n]);\n\nconst ADVISORY_RESULT_KEYS: ReadonlySet<string> = new Set([\"schemaVersion\", \"taskClass\", \"confidenceBand\"]);\n\n/**\n * Field-name markers that must never appear on a payload or result object,\n * checked case-insensitively as a substring of every own key. This is\n * defense-in-depth alongside the exact-key allowlist below: even a candidate\n * that otherwise has every required field still fails if it also carries a\n * field shaped like a stored prompt, hash, path, model/provider identifier,\n * session identifier, tool name, or hook output.\n */\nconst FORBIDDEN_KEY_SUBSTRINGS: readonly string[] = [\n\t\"prompt\",\n\t\"hash\",\n\t\"path\",\n\t\"model\",\n\t\"session\",\n\t\"tool\",\n\t\"hook\",\n\t\"provider\",\n];\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * True if any own key of `record` contains a forbidden field-name marker\n * (case-insensitive). Typed `object` rather than `Record<string, unknown>` so\n * callers (including tests) can pass any concrete object shape - named\n * interface, ad-hoc literal, or an already-`Record`-typed value - without a\n * missing-index-signature type error; only `Object.keys` is used internally,\n * which accepts any `object`.\n */\nexport function containsForbiddenField(record: object): boolean {\n\tfor (const key of Object.keys(record)) {\n\t\tconst lowerKey = key.toLowerCase();\n\t\tif (FORBIDDEN_KEY_SUBSTRINGS.some((needle) => lowerKey.includes(needle))) return true;\n\t}\n\treturn false;\n}\n\nfunction hasOnlyAllowedKeys(record: Record<string, unknown>, allowed: ReadonlySet<string>): boolean {\n\treturn Object.keys(record).every((key) => allowed.has(key));\n}\n\nfunction clampInt(value: number, min: number, max: number): number {\n\tconst truncated = Number.isFinite(value) ? Math.trunc(value) : min;\n\tif (truncated < min) return min;\n\tif (truncated > max) return max;\n\treturn truncated;\n}\n\nfunction clampLenBucket(value: number): AdaptorchLenBucket {\n\treturn clampInt(value, 0, 7) as AdaptorchLenBucket;\n}\n\nfunction clampPressureBucket(value: number): AdaptorchPressureBucket {\n\treturn clampInt(value, 0, 3) as AdaptorchPressureBucket;\n}\n\n/** `value ?? fallback`, coerced to an integer and floored at `min` (used for config, not untrusted payloads). */\nfunction boundedIntOrDefault(value: number | undefined, fallback: number, min: number): number {\n\tconst raw = value ?? fallback;\n\tif (!Number.isFinite(raw)) return fallback;\n\treturn Math.max(min, Math.trunc(raw));\n}\n\n/**\n * Sanitize/validate an outbound advisory request. Returns `null` (never\n * throws) when `candidate` is not a plain object, carries a forbidden-named\n * field, carries any key outside the exact closed schema, or has a\n * wrong-typed/unknown-enum field. `lenBucket`/`pressureBucket` are clamped\n * into their bounded ranges rather than rejected, so a caller-side rounding\n * or edge-case computation can never smuggle an out-of-range integer through.\n */\nexport function sanitizeConsultPayload(candidate: unknown): AdaptorchConsultPayload | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, CONSULT_PAYLOAD_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst runnerUp = candidate.runnerUp;\n\tif (typeof runnerUp !== \"string\" || !TASK_CLASS_SET.has(runnerUp)) return null;\n\n\tconst marginBucket = candidate.marginBucket;\n\tif (typeof marginBucket !== \"string\" || !MARGIN_BUCKET_SET.has(marginBucket)) return null;\n\n\tconst lenBucketRaw = candidate.lenBucket;\n\tif (typeof lenBucketRaw !== \"number\") return null;\n\n\tconst pressureBucketRaw = candidate.pressureBucket;\n\tif (typeof pressureBucketRaw !== \"number\") return null;\n\n\tconst hadFence = candidate.hadFence;\n\tif (typeof hadFence !== \"boolean\") return null;\n\n\tconst hadDiff = candidate.hadDiff;\n\tif (typeof hadDiff !== \"boolean\") return null;\n\n\tconst laneTypeRaw = candidate.laneType;\n\tlet laneType: AdaptorchLaneType | undefined;\n\tif (laneTypeRaw !== undefined) {\n\t\tif (typeof laneTypeRaw !== \"string\" || !LANE_TYPE_SET.has(laneTypeRaw)) return null;\n\t\tlaneType = laneTypeRaw as AdaptorchLaneType;\n\t}\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\trunnerUp: runnerUp as AdaptorchTaskClass,\n\t\tmarginBucket: marginBucket as AdaptorchMarginBucket,\n\t\tlenBucket: clampLenBucket(lenBucketRaw),\n\t\thadFence,\n\t\thadDiff,\n\t\tpressureBucket: clampPressureBucket(pressureBucketRaw),\n\t\t...(laneType !== undefined ? { laneType } : {}),\n\t};\n}\n\n/**\n * Sanitize/validate an inbound advisory result (i.e. whatever the injected\n * advisory function returned). Returns `null` (never throws) on any shape,\n * forbidden-field, extra-key, or unknown-enum violation - the caller\n * (`AdaptorchBridge.consult`) treats `null` as \"no hint\" and falls back\n * silently, exactly like every other failure mode.\n */\nexport function sanitizeAdvisoryResult(candidate: unknown): AdaptorchAdvisoryResult | null {\n\tif (!isPlainObject(candidate)) return null;\n\tif (containsForbiddenField(candidate)) return null;\n\tif (!hasOnlyAllowedKeys(candidate, ADVISORY_RESULT_KEYS)) return null;\n\n\tif (candidate.schemaVersion !== 1) return null;\n\n\tconst taskClass = candidate.taskClass;\n\tif (typeof taskClass !== \"string\" || !TASK_CLASS_SET.has(taskClass)) return null;\n\n\tconst confidenceBand = candidate.confidenceBand;\n\tif (typeof confidenceBand !== \"string\" || !CONFIDENCE_BAND_SET.has(confidenceBand)) return null;\n\n\treturn {\n\t\tschemaVersion: 1,\n\t\ttaskClass: taskClass as AdaptorchTaskClass,\n\t\tconfidenceBand: confidenceBand as AdaptorchConfidenceBand,\n\t};\n}\n\n/**\n * Read-only/local AdaptOrch MCP tool names this bridge may ever reference,\n * grounded in the eleven-tool surface documented in\n * packages/adaptorch-wpl/src/adaptorch-client.ts (cross-checked there\n * against the AdaptOrch server's own docs) and narrowed to the two tools the\n * read-first plan puts in scope for a reasoning-effort hint (plan 4.2): the\n * session-start capabilities probe and the per-consult topology\n * classification call. This module does not call any MCP tool directly\n * today - the advisory function is fully caller-injected - so this\n * allowlist is not wired to a transport here; it exists so a future\n * transport, and this module's own tests, can assert by construction that\n * `adaptorch_run` (submit) and `adaptorch_cancel_run` (cancel), or any other\n * mutating tool, can never be reachable from router/bridge code.\n */\nexport const ADAPTORCH_READONLY_TOOL_ALLOWLIST = [\"adaptorch_capabilities\", \"adaptorch_route_topology\"] as const;\n\nexport type AdaptorchReadonlyToolName = (typeof ADAPTORCH_READONLY_TOOL_ALLOWLIST)[number];\n\n/** Underscore-segment verbs that mark an AdaptOrch-shaped tool name as mutating (write/execute/lifecycle). */\nconst MUTATING_TOOL_VERB_SEGMENTS: ReadonlySet<string> = new Set([\n\t\"run\",\n\t\"submit\",\n\t\"cancel\",\n\t\"dispatch\",\n\t\"delete\",\n\t\"execute\",\n\t\"mutate\",\n\t\"write\",\n\t\"create\",\n\t\"update\",\n\t\"remove\",\n]);\n\n/**\n * True if any underscore-delimited segment of an AdaptOrch-shaped tool name\n * is a known mutating verb. Segment-based rather than a raw substring/regex\n * match so \"adaptorch_route_topology\" is never falsely flagged (no segment\n * is a mutating verb), while \"adaptorch_run\" and \"adaptorch_cancel_run\" are\n * always caught regardless of future prefix/suffix changes.\n */\nexport function looksMutatingToolName(name: string): boolean {\n\treturn name\n\t\t.toLowerCase()\n\t\t.split(\"_\")\n\t\t.some((segment) => MUTATING_TOOL_VERB_SEGMENTS.has(segment));\n}\n\n/**\n * Type guard: true only for the two allowlisted read/local AdaptOrch tool\n * names. Also independently rejects any name `looksMutatingToolName` flags,\n * so a future accidental addition of a mutating name to the allowlist array\n * still cannot make this function report it as allowed.\n */\nexport function isAdaptorchToolAllowed(name: string): name is AdaptorchReadonlyToolName {\n\tif (looksMutatingToolName(name)) return false;\n\treturn (ADAPTORCH_READONLY_TOOL_ALLOWLIST as readonly string[]).includes(name);\n}\n\n/**\n * Timeout wrapper interface around any injected async advisory call. Races\n * the call against a timer; a slow or hung call rejects at `timeoutMs`\n * instead of leaving the caller waiting indefinitely. Always clears the\n * timer once the call settles (or once the timeout itself fires), and\n * always resolves/rejects exactly once - a synchronous throw from `run` is\n * treated the same as an asynchronous rejection.\n */\nexport type AdvisoryTimeoutWrapper = <T>(run: () => Promise<T>, timeoutMs: number) => Promise<T>;\n\nexport const withAdvisoryTimeout: AdvisoryTimeoutWrapper = <T>(\n\trun: () => Promise<T>,\n\ttimeoutMs: number,\n): Promise<T> => {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\treject(new Error(\"adaptorch-bridge: advisory call timed out\"));\n\t\t}, timeoutMs);\n\n\t\tlet pending: Promise<T>;\n\t\ttry {\n\t\t\tpending = run();\n\t\t} catch (err) {\n\t\t\tclearTimeout(timer);\n\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\treturn;\n\t\t}\n\n\t\tpending.then(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(err: unknown) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(err instanceof Error ? err : new Error(String(err)));\n\t\t\t},\n\t\t);\n\t});\n};\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\nconst MIN_TTL_MS = 30 * 1000;\nconst MAX_TTL_MS = 5 * 60 * 1000;\nconst DEFAULT_MAX_TURNS_PER_ENTRY = 10;\nconst DEFAULT_CACHE_SIZE = 16;\nconst DEFAULT_MAX_CONSULTS_PER_SESSION = 5;\nconst DEFAULT_MIN_INTERVAL_MS = 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 1500;\nconst MIN_TIMEOUT_MS = 1;\nconst MAX_TIMEOUT_MS = 30 * 1000;\nconst DEFAULT_FAILURE_THRESHOLD = 3;\nconst DEFAULT_COOLDOWN_MS = 10 * 60 * 1000;\n\n/**\n * Exact TTL/cache/budget/circuit-breaker defaults from the read-first plan,\n * section 4.8. Exported so a security/review lane can confirm the shipped\n * numbers without reading this module's internals.\n */\nexport const ADAPTORCH_BRIDGE_DEFAULTS = {\n\tttlMs: DEFAULT_TTL_MS,\n\tminTtlMs: MIN_TTL_MS,\n\tmaxTtlMs: MAX_TTL_MS,\n\tmaxTurnsPerEntry: DEFAULT_MAX_TURNS_PER_ENTRY,\n\tcacheSize: DEFAULT_CACHE_SIZE,\n\tmaxConsultsPerSession: DEFAULT_MAX_CONSULTS_PER_SESSION,\n\tminIntervalMs: DEFAULT_MIN_INTERVAL_MS,\n\ttimeoutMs: DEFAULT_TIMEOUT_MS,\n\tminTimeoutMs: MIN_TIMEOUT_MS,\n\tmaxTimeoutMs: MAX_TIMEOUT_MS,\n\tfailureThreshold: DEFAULT_FAILURE_THRESHOLD,\n\tcooldownMs: DEFAULT_COOLDOWN_MS,\n} as const;\n\n/**\n * Bridge configuration. `advisoryFn` is the ONLY seam to any external\n * system - this module never imports or calls an MCP client/transport\n * itself, so it is impossible for a unit test (or a misconfigured caller)\n * to reach a real MCP server through this module by accident.\n */\nexport interface AdaptorchBridgeConfig {\n\t/**\n\t * Caller-injected advisory call. Its return value is untrusted: `consult`\n\t * always runs it through `sanitizeAdvisoryResult` before using or caching\n\t * it, and a rejected/thrown/timed-out/schema-invalid response is treated\n\t * as an ordinary failure (counts toward the circuit breaker), never as a\n\t * value to fuse.\n\t */\n\treadonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\t/** Injectable clock; defaults to `Date.now`. Tests supply a fake to avoid real wall-clock waits. */\n\treadonly now?: () => number;\n\t/** Cache TTL in ms; clamped to [30s, 5min] (plan 4.8). Default 5 minutes. */\n\treadonly ttlMs?: number;\n\t/** A cache entry also expires after this many `getFreshHint` calls (\"turns\"). Default 10. */\n\treadonly maxTurnsPerEntry?: number;\n\t/** Max distinct cached payload keys (LRU eviction beyond this). Default 16. */\n\treadonly cacheSize?: number;\n\t/** Max advisory attempts for this bridge instance's lifetime (\"session\"). Default 5. */\n\treadonly maxConsultsPerSession?: number;\n\t/** Minimum ms between advisory attempts. Default 60000 (60s). */\n\treadonly minIntervalMs?: number;\n\t/** Per-attempt timeout in ms; clamped to [1ms, 30s]. Default 1500 (1.5s). */\n\treadonly timeoutMs?: number;\n\t/** Consecutive failures before the circuit opens. Default 3. */\n\treadonly failureThreshold?: number;\n\t/** Circuit-open duration in ms once opened. Default 600000 (10min). */\n\treadonly cooldownMs?: number;\n}\n\n/** Read-only, non-sensitive introspection snapshot - never exposes payload/result content. */\nexport interface AdaptorchBridgeStats {\n\treadonly consultCount: number;\n\treadonly failureStreak: number;\n\treadonly circuitOpen: boolean;\n\treadonly circuitOpenUntilMs: number;\n\treadonly cacheEntryCount: number;\n}\n\nexport interface AdaptorchBridge {\n\t/**\n\t * Synchronous, cache-read-only. Never blocks, never throws, and never\n\t * calls the advisory function - a cache miss or an expired/invalid entry\n\t * silently returns `null` (\"no hint\"), matching the turn-start path's\n\t * synchronous, I/O-free contract (plan 4.4).\n\t */\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null;\n\t/**\n\t * Fire-and-forget refresh for a LATER call to `getFreshHint`, never the\n\t * current one. Never throws synchronously and never rejects into the\n\t * caller - any failure is swallowed internally by `consult`.\n\t */\n\trequestRefresh(payload: AdaptorchConsultPayload): void;\n\t/**\n\t * The awaitable core operation `requestRefresh` fires without awaiting.\n\t * Gated by the circuit breaker, budget counter, and minimum interval (in\n\t * that order) before ever invoking `advisoryFn`; always resolves (never\n\t * rejects) to either a sanitized hint or `null`.\n\t */\n\tconsult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null>;\n\t/** Read-only introspection for tests/diagnostics; contains no payload or result content. */\n\tgetStats(): AdaptorchBridgeStats;\n}\n\ninterface CacheEntry {\n\treadonly result: AdaptorchAdvisoryResult;\n\treadonly insertedAtMs: number;\n\treadonly insertedAtTurn: number;\n}\n\n/** Deterministic cache key built field-by-field from a sanitized payload - never from raw prompt text. */\nfunction cacheKeyFor(payload: AdaptorchConsultPayload): string {\n\treturn JSON.stringify({\n\t\ttaskClass: payload.taskClass,\n\t\trunnerUp: payload.runnerUp,\n\t\tmarginBucket: payload.marginBucket,\n\t\tlenBucket: payload.lenBucket,\n\t\thadFence: payload.hadFence,\n\t\thadDiff: payload.hadDiff,\n\t\tpressureBucket: payload.pressureBucket,\n\t\tlaneType: payload.laneType ?? null,\n\t});\n}\n\nclass AdaptorchBridgeImpl implements AdaptorchBridge {\n\tprivate readonly advisoryFn: (payload: AdaptorchConsultPayload) => Promise<unknown>;\n\tprivate readonly now: () => number;\n\tprivate readonly ttlMs: number;\n\tprivate readonly maxTurnsPerEntry: number;\n\tprivate readonly cacheSize: number;\n\tprivate readonly maxConsultsPerSession: number;\n\tprivate readonly minIntervalMs: number;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly failureThreshold: number;\n\tprivate readonly cooldownMs: number;\n\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate turnCounter = 0;\n\tprivate consultCount = 0;\n\tprivate failureStreak = 0;\n\tprivate lastAttemptAtMs: number | null = null;\n\tprivate circuitOpenUntilMs = 0;\n\n\tconstructor(config: AdaptorchBridgeConfig) {\n\t\tthis.advisoryFn = config.advisoryFn;\n\t\tthis.now = config.now ?? Date.now;\n\t\tthis.ttlMs = clampInt(config.ttlMs ?? DEFAULT_TTL_MS, MIN_TTL_MS, MAX_TTL_MS);\n\t\tthis.maxTurnsPerEntry = boundedIntOrDefault(config.maxTurnsPerEntry, DEFAULT_MAX_TURNS_PER_ENTRY, 1);\n\t\tthis.cacheSize = boundedIntOrDefault(config.cacheSize, DEFAULT_CACHE_SIZE, 1);\n\t\tthis.maxConsultsPerSession = boundedIntOrDefault(\n\t\t\tconfig.maxConsultsPerSession,\n\t\t\tDEFAULT_MAX_CONSULTS_PER_SESSION,\n\t\t\t0,\n\t\t);\n\t\tthis.minIntervalMs = boundedIntOrDefault(config.minIntervalMs, DEFAULT_MIN_INTERVAL_MS, 0);\n\t\tthis.timeoutMs = clampInt(config.timeoutMs ?? DEFAULT_TIMEOUT_MS, MIN_TIMEOUT_MS, MAX_TIMEOUT_MS);\n\t\tthis.failureThreshold = boundedIntOrDefault(config.failureThreshold, DEFAULT_FAILURE_THRESHOLD, 1);\n\t\tthis.cooldownMs = boundedIntOrDefault(config.cooldownMs, DEFAULT_COOLDOWN_MS, 0);\n\t}\n\n\tgetFreshHint(payload: AdaptorchConsultPayload): AdaptorchAdvisoryResult | null {\n\t\tthis.turnCounter += 1;\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst key = cacheKeyFor(sanitizedPayload);\n\t\tconst entry = this.cache.get(key);\n\t\tif (entry === undefined) return null;\n\t\tif (!this.isEntryFresh(entry)) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\treturn entry.result;\n\t}\n\n\trequestRefresh(payload: AdaptorchConsultPayload): void {\n\t\tvoid this.consult(payload).catch(() => {\n\t\t\t// consult() already swallows every failure and always resolves; this\n\t\t\t// catch only guards a future refactor from letting one escape, so a\n\t\t\t// fire-and-forget refresh can never surface an unhandled rejection.\n\t\t});\n\t}\n\n\tasync consult(payload: AdaptorchConsultPayload): Promise<AdaptorchAdvisoryResult | null> {\n\t\tconst sanitizedPayload = sanitizeConsultPayload(payload);\n\t\tif (sanitizedPayload === null) return null;\n\n\t\tconst nowMs = this.now();\n\t\tif (nowMs < this.circuitOpenUntilMs) return null;\n\t\tif (this.consultCount >= this.maxConsultsPerSession) return null;\n\t\tif (this.lastAttemptAtMs !== null && nowMs - this.lastAttemptAtMs < this.minIntervalMs) return null;\n\n\t\tthis.consultCount += 1;\n\t\tthis.lastAttemptAtMs = nowMs;\n\n\t\tlet raw: unknown;\n\t\ttry {\n\t\t\traw = await withAdvisoryTimeout(() => this.advisoryFn(sanitizedPayload), this.timeoutMs);\n\t\t} catch {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = sanitizeAdvisoryResult(raw);\n\t\tif (result === null) {\n\t\t\tthis.recordFailure();\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.failureStreak = 0;\n\t\tthis.cacheResult(sanitizedPayload, result);\n\t\treturn result;\n\t}\n\n\tgetStats(): AdaptorchBridgeStats {\n\t\tconst nowMs = this.now();\n\t\treturn {\n\t\t\tconsultCount: this.consultCount,\n\t\t\tfailureStreak: this.failureStreak,\n\t\t\tcircuitOpen: nowMs < this.circuitOpenUntilMs,\n\t\t\tcircuitOpenUntilMs: this.circuitOpenUntilMs,\n\t\t\tcacheEntryCount: this.cache.size,\n\t\t};\n\t}\n\n\tprivate recordFailure(): void {\n\t\tthis.failureStreak += 1;\n\t\tif (this.failureStreak >= this.failureThreshold) {\n\t\t\tthis.circuitOpenUntilMs = this.now() + this.cooldownMs;\n\t\t}\n\t}\n\n\tprivate isEntryFresh(entry: CacheEntry): boolean {\n\t\tconst ageMs = this.now() - entry.insertedAtMs;\n\t\tconst ageTurns = this.turnCounter - entry.insertedAtTurn;\n\t\treturn ageMs < this.ttlMs && ageTurns < this.maxTurnsPerEntry;\n\t}\n\n\t/** Eviction favors entries last refreshed by a successful consult (write-time LRU, not read-time). */\n\tprivate cacheResult(payload: AdaptorchConsultPayload, result: AdaptorchAdvisoryResult): void {\n\t\tconst key = cacheKeyFor(payload);\n\t\tif (this.cache.has(key)) this.cache.delete(key);\n\t\tthis.cache.set(key, { result, insertedAtMs: this.now(), insertedAtTurn: this.turnCounter });\n\t\twhile (this.cache.size > this.cacheSize) {\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey === undefined) break;\n\t\t\tthis.cache.delete(oldestKey);\n\t\t}\n\t}\n}\n\n/** Construct a default-off AdaptOrch advisory bridge. See `AdaptorchBridgeConfig` for tunables. */\nexport function createAdaptorchBridge(config: AdaptorchBridgeConfig): AdaptorchBridge {\n\treturn new AdaptorchBridgeImpl(config);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session-runtime.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { AgentSession } from \"./agent-session.ts\";\nimport type { AgentSessionRuntimeDiagnostic, AgentSessionServices } from \"./agent-session-services.ts\";\nimport type { ReplacedSessionContext, SessionShutdownEvent, SessionStartEvent } from \"./extensions/index.ts\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.ts\";\nimport type { CreateAgentSessionResult } from \"./sdk.ts\";\nimport { assertSessionCwdExists } from \"./session-cwd.ts\";\nimport { SessionManager } from \"./session-manager.ts\";\n\n/**\n * Result returned by runtime creation.\n *\n * The caller gets the created session, its cwd-bound services, and all\n * diagnostics collected during setup.\n */\nexport interface CreateAgentSessionRuntimeResult extends CreateAgentSessionResult {\n\tservices: AgentSessionServices;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\n/**\n * Creates a full runtime for a target cwd and session manager.\n *\n * The factory closes over process-global fixed inputs, recreates cwd-bound\n * services for the effective cwd, resolves session options against those\n * services, and finally creates the AgentSession.\n */\nexport type CreateAgentSessionRuntimeFactory = (options: {\n\tcwd: string;\n\tagentDir: string;\n\tsessionManager: SessionManager;\n\tsessionStartEvent?: SessionStartEvent;\n}) => Promise<CreateAgentSessionRuntimeResult>;\n\n/**\n * Thrown when /import references a JSONL file path that does not exist.\n */\nexport class SessionImportFileNotFoundError extends Error {\n\treadonly filePath: string;\n\n\tconstructor(filePath: string) {\n\t\tsuper(`File not found: ${filePath}`);\n\t\tthis.name = \"SessionImportFileNotFoundError\";\n\t\tthis.filePath = filePath;\n\t}\n}\n\nfunction extractUserMessageText(content: string | Array<{ type: string; text?: string }>): string {\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\");\n}\n\n/**\n * Owns the current AgentSession plus its cwd-bound services.\n *\n * Session replacement methods tear down the current runtime first, then create\n * and apply the next runtime. If creation fails, the error is propagated to the\n * caller. The caller is responsible for user-facing error handling.\n */\nexport class AgentSessionRuntime {\n\tprivate rebindSession?: (session: AgentSession) => Promise<void>;\n\tprivate beforeSessionInvalidate?: () => void;\n\tprivate _session: AgentSession;\n\tprivate _services: AgentSessionServices;\n\tprivate readonly createRuntime: CreateAgentSessionRuntimeFactory;\n\tprivate _diagnostics: AgentSessionRuntimeDiagnostic[];\n\tprivate _modelFallbackMessage?: string;\n\n\tconstructor(\n\t\t_session: AgentSession,\n\t\t_services: AgentSessionServices,\n\t\tcreateRuntime: CreateAgentSessionRuntimeFactory,\n\t\t_diagnostics: AgentSessionRuntimeDiagnostic[] = [],\n\t\t_modelFallbackMessage?: string,\n\t) {\n\t\tthis._session = _session;\n\t\tthis._services = _services;\n\t\tthis.createRuntime = createRuntime;\n\t\tthis._diagnostics = _diagnostics;\n\t\tthis._modelFallbackMessage = _modelFallbackMessage;\n\t}\n\n\tget services(): AgentSessionServices {\n\t\treturn this._services;\n\t}\n\n\tget session(): AgentSession {\n\t\treturn this._session;\n\t}\n\n\tget cwd(): string {\n\t\treturn this._services.cwd;\n\t}\n\n\tget diagnostics(): readonly AgentSessionRuntimeDiagnostic[] {\n\t\treturn this._diagnostics;\n\t}\n\n\tget modelFallbackMessage(): string | undefined {\n\t\treturn this._modelFallbackMessage;\n\t}\n\n\tsetRebindSession(rebindSession?: (session: AgentSession) => Promise<void>): void {\n\t\tthis.rebindSession = rebindSession;\n\t}\n\n\t/**\n\t * Set a synchronous callback that runs after `session_shutdown` handlers finish\n\t * but before the current session is invalidated.\n\t *\n\t * This is for host-owned UI teardown that must not yield to the event loop,\n\t * such as detaching extension-provided TUI components before the old extension\n\t * context becomes stale.\n\t */\n\tsetBeforeSessionInvalidate(beforeSessionInvalidate?: () => void): void {\n\t\tthis.beforeSessionInvalidate = beforeSessionInvalidate;\n\t}\n\n\tprivate async emitBeforeSwitch(\n\t\treason: \"new\" | \"resume\",\n\t\ttargetSessionFile?: string,\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner.hasHandlers(\"session_before_switch\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_switch\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async emitBeforeFork(\n\t\tentryId: string,\n\t\toptions: { position: \"before\" | \"at\" },\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner.hasHandlers(\"session_before_fork\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_fork\",\n\t\t\tentryId,\n\t\t\t...options,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async teardownCurrent(reason: SessionShutdownEvent[\"reason\"], targetSessionFile?: string): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner, {\n\t\t\ttype: \"session_shutdown\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\tthis.beforeSessionInvalidate?.();\n\t\tthis.session.dispose();\n\t}\n\n\tprivate apply(result: CreateAgentSessionRuntimeResult): void {\n\t\tthis._session = result.session;\n\t\tthis._services = result.services;\n\t\tthis._diagnostics = result.diagnostics;\n\t\tthis._modelFallbackMessage = result.modelFallbackMessage;\n\t}\n\n\tprivate async finishSessionReplacement(withSession?: (ctx: ReplacedSessionContext) => Promise<void>): Promise<void> {\n\t\tif (this.rebindSession) {\n\t\t\tawait this.rebindSession(this.session);\n\t\t}\n\t\tif (withSession) {\n\t\t\tawait withSession(this.session.createReplacedSessionContext());\n\t\t}\n\t}\n\n\tasync switchSession(\n\t\tsessionPath: string,\n\t\toptions?: { cwdOverride?: string; withSession?: (ctx: ReplacedSessionContext) => Promise<void> },\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", sessionPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionManager = SessionManager.open(sessionPath, undefined, options?.cwdOverride);\n\t\tassertSessionCwdExists(sessionManager, this.cwd);\n\t\tawait this.teardownCurrent(\"resume\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync newSession(options?: {\n\t\tparentSession?: string;\n\t\tsetup?: (sessionManager: SessionManager) => Promise<void>;\n\t\twithSession?: (ctx: ReplacedSessionContext) => Promise<void>;\n\t}): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"new\");\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\tif (options?.parentSession) {\n\t\t\tsessionManager.newSession({ parentSession: options.parentSession });\n\t\t}\n\n\t\tawait this.teardownCurrent(\"new\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"new\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tif (options?.setup) {\n\t\t\tawait options.setup(this.session.sessionManager);\n\t\t\tthis.session.agent.state.messages = this.session.sessionManager.buildSessionContext().messages;\n\t\t}\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync fork(\n\t\tentryId: string,\n\t\toptions?: { position?: \"before\" | \"at\"; withSession?: (ctx: ReplacedSessionContext) => Promise<void> },\n\t): Promise<{ cancelled: boolean; selectedText?: string }> {\n\t\tconst position = options?.position ?? \"before\";\n\t\tconst beforeResult = await this.emitBeforeFork(entryId, { position });\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn { cancelled: true };\n\t\t}\n\t\tlet targetLeafId: string | null;\n\t\tlet selectedText: string | undefined;\n\n\t\tconst selectedEntry = this.session.sessionManager.getEntry(entryId);\n\t\tif (!selectedEntry) {\n\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t}\n\n\t\tif (position === \"at\") {\n\t\t\ttargetLeafId = selectedEntry.id;\n\t\t} else {\n\t\t\tif (selectedEntry.type !== \"message\" || selectedEntry.message.role !== \"user\") {\n\t\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t\t}\n\t\t\ttargetLeafId = selectedEntry.parentId;\n\t\t\tselectedText = extractUserMessageText(selectedEntry.message.content);\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tif (this.session.sessionManager.isPersisted()) {\n\t\t\tconst currentSessionFile = this.session.sessionFile;\n\t\t\tif (!currentSessionFile) {\n\t\t\t\tthrow new Error(\"Persisted session is missing a session file\");\n\t\t\t}\n\t\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\t\tif (!targetLeafId) {\n\t\t\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\t\t\tsessionManager.newSession({ parentSession: currentSessionFile });\n\t\t\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\t\t\tthis.apply(\n\t\t\t\t\tawait this.createRuntime({\n\t\t\t\t\t\tcwd: this.cwd,\n\t\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\t\tsessionManager,\n\t\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\t\t\treturn { cancelled: false, selectedText };\n\t\t\t}\n\n\t\t\tconst ownerLease = this.session.sessionManager.getOwnerLease();\n\t\t\tconst sessionManager = SessionManager.open(currentSessionFile, sessionDir, undefined, ownerLease);\n\t\t\tconst forkedSessionPath = sessionManager.createBranchedSession(targetLeafId);\n\t\t\tif (!forkedSessionPath) {\n\t\t\t\tthrow new Error(\"Failed to create forked session\");\n\t\t\t}\n\t\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\t\tif (sessionManager.getOwnerLease() === ownerLease) sessionManager.setOwnerLease(undefined);\n\t\t\tthis.apply(\n\t\t\t\tawait this.createRuntime({\n\t\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\tsessionManager,\n\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\t\treturn { cancelled: false, selectedText };\n\t\t}\n\n\t\tconst sessionManager = this.session.sessionManager;\n\t\tif (!targetLeafId) {\n\t\t\tsessionManager.newSession({ parentSession: this.session.sessionFile });\n\t\t} else {\n\t\t\tsessionManager.createBranchedSession(targetLeafId);\n\t\t}\n\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false, selectedText };\n\t}\n\n\t/**\n\t * Import a session JSONL file and switch runtime state to the imported session.\n\t *\n\t * @returns `{ cancelled: true }` when cancelled by `session_before_switch`, otherwise `{ cancelled: false }`.\n\t * @throws {SessionImportFileNotFoundError} When the input path does not exist.\n\t * @throws {MissingSessionCwdError} When the imported session cwd cannot be resolved and no override is provided.\n\t */\n\tasync importFromJsonl(inputPath: string, cwdOverride?: string): Promise<{ cancelled: boolean }> {\n\t\tconst resolvedPath = resolvePath(inputPath);\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tthrow new SessionImportFileNotFoundError(resolvedPath);\n\t\t}\n\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tif (!existsSync(sessionDir)) {\n\t\t\tmkdirSync(sessionDir, { recursive: true });\n\t\t}\n\n\t\tconst destinationPath = join(sessionDir, basename(resolvedPath));\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", destinationPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tif (resolve(destinationPath) !== resolvedPath) {\n\t\t\tcopyFileSync(resolvedPath, destinationPath);\n\t\t}\n\n\t\tconst sessionManager = SessionManager.open(destinationPath, sessionDir, cwdOverride);\n\t\tassertSessionCwdExists(sessionManager, this.cwd);\n\t\tawait this.teardownCurrent(\"resume\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement();\n\t\treturn { cancelled: false };\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner, {\n\t\t\ttype: \"session_shutdown\",\n\t\t\treason: \"quit\",\n\t\t});\n\t\tthis.beforeSessionInvalidate?.();\n\t\tthis.session.dispose();\n\t}\n}\n\n/**\n * Create the initial runtime from a runtime factory and initial session target.\n *\n * The same factory is stored on the returned AgentSessionRuntime and reused for\n * later /new, /resume, /fork, and import flows.\n */\nexport async function createAgentSessionRuntime(\n\tcreateRuntime: CreateAgentSessionRuntimeFactory,\n\toptions: {\n\t\tcwd: string;\n\t\tagentDir: string;\n\t\tsessionManager: SessionManager;\n\t\tsessionStartEvent?: SessionStartEvent;\n\t},\n): Promise<AgentSessionRuntime> {\n\tassertSessionCwdExists(options.sessionManager, options.cwd);\n\tconst result = await createRuntime(options);\n\treturn new AgentSessionRuntime(\n\t\tresult.session,\n\t\tresult.services,\n\t\tcreateRuntime,\n\t\tresult.diagnostics,\n\t\tresult.modelFallbackMessage,\n\t);\n}\n\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"agent-session-runtime.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants, copyFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { basename, join, parse, resolve } from \"node:path\";\nimport { resolvePath } from \"../utils/paths.ts\";\nimport type { AgentSession } from \"./agent-session.ts\";\nimport type { AgentSessionRuntimeDiagnostic, AgentSessionServices } from \"./agent-session-services.ts\";\nimport type { ReplacedSessionContext, SessionShutdownEvent, SessionStartEvent } from \"./extensions/index.ts\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.ts\";\nimport type { CreateAgentSessionResult } from \"./sdk.ts\";\nimport { assertSessionCwdExists } from \"./session-cwd.ts\";\nimport { SessionManager } from \"./session-manager.ts\";\nimport { extractUserMessageText } from \"./session-message-text.ts\";\n\n/**\n * Result returned by runtime creation.\n *\n * The caller gets the created session, its cwd-bound services, and all\n * diagnostics collected during setup.\n */\nexport interface CreateAgentSessionRuntimeResult extends CreateAgentSessionResult {\n\tservices: AgentSessionServices;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\n/**\n * Creates a full runtime for a target cwd and session manager.\n *\n * The factory closes over process-global fixed inputs, recreates cwd-bound\n * services for the effective cwd, resolves session options against those\n * services, and finally creates the AgentSession.\n */\nexport type CreateAgentSessionRuntimeFactory = (options: {\n\tcwd: string;\n\tagentDir: string;\n\tsessionManager: SessionManager;\n\tsessionStartEvent?: SessionStartEvent;\n}) => Promise<CreateAgentSessionRuntimeResult>;\n\n/**\n * Thrown when /import references a JSONL file path that does not exist.\n */\nexport class SessionImportFileNotFoundError extends Error {\n\treadonly filePath: string;\n\n\tconstructor(filePath: string) {\n\t\tsuper(`File not found: ${filePath}`);\n\t\tthis.name = \"SessionImportFileNotFoundError\";\n\t\tthis.filePath = filePath;\n\t}\n}\n\n/**\n * Owns the current AgentSession plus its cwd-bound services.\n *\n * Session replacement methods tear down the current runtime first, then create\n * and apply the next runtime. If creation fails, the error is propagated to the\n * caller. The caller is responsible for user-facing error handling.\n */\nexport class AgentSessionRuntime {\n\tprivate rebindSession?: (session: AgentSession) => Promise<void>;\n\tprivate beforeSessionInvalidate?: () => void;\n\tprivate _session: AgentSession;\n\tprivate _services: AgentSessionServices;\n\tprivate readonly createRuntime: CreateAgentSessionRuntimeFactory;\n\tprivate _diagnostics: AgentSessionRuntimeDiagnostic[];\n\tprivate _modelFallbackMessage?: string;\n\n\tconstructor(\n\t\t_session: AgentSession,\n\t\t_services: AgentSessionServices,\n\t\tcreateRuntime: CreateAgentSessionRuntimeFactory,\n\t\t_diagnostics: AgentSessionRuntimeDiagnostic[] = [],\n\t\t_modelFallbackMessage?: string,\n\t) {\n\t\tthis._session = _session;\n\t\tthis._services = _services;\n\t\tthis.createRuntime = createRuntime;\n\t\tthis._diagnostics = _diagnostics;\n\t\tthis._modelFallbackMessage = _modelFallbackMessage;\n\t}\n\n\tget services(): AgentSessionServices {\n\t\treturn this._services;\n\t}\n\n\tget session(): AgentSession {\n\t\treturn this._session;\n\t}\n\n\tget cwd(): string {\n\t\treturn this._services.cwd;\n\t}\n\n\tget diagnostics(): readonly AgentSessionRuntimeDiagnostic[] {\n\t\treturn this._diagnostics;\n\t}\n\n\tget modelFallbackMessage(): string | undefined {\n\t\treturn this._modelFallbackMessage;\n\t}\n\n\tsetRebindSession(rebindSession?: (session: AgentSession) => Promise<void>): void {\n\t\tthis.rebindSession = rebindSession;\n\t}\n\n\t/**\n\t * Set a synchronous callback that runs after `session_shutdown` handlers finish\n\t * but before the current session is invalidated.\n\t *\n\t * This is for host-owned UI teardown that must not yield to the event loop,\n\t * such as detaching extension-provided TUI components before the old extension\n\t * context becomes stale.\n\t */\n\tsetBeforeSessionInvalidate(beforeSessionInvalidate?: () => void): void {\n\t\tthis.beforeSessionInvalidate = beforeSessionInvalidate;\n\t}\n\n\tprivate async emitBeforeSwitch(\n\t\treason: \"new\" | \"resume\",\n\t\ttargetSessionFile?: string,\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner.hasHandlers(\"session_before_switch\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_switch\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async emitBeforeFork(\n\t\tentryId: string,\n\t\toptions: { position: \"before\" | \"at\" },\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst runner = this.session.extensionRunner;\n\t\tif (!runner.hasHandlers(\"session_before_fork\")) {\n\t\t\treturn { cancelled: false };\n\t\t}\n\n\t\tconst result = await runner.emit({\n\t\t\ttype: \"session_before_fork\",\n\t\t\tentryId,\n\t\t\t...options,\n\t\t});\n\t\treturn { cancelled: result?.cancel === true };\n\t}\n\n\tprivate async teardownCurrent(reason: SessionShutdownEvent[\"reason\"], targetSessionFile?: string): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner, {\n\t\t\ttype: \"session_shutdown\",\n\t\t\treason,\n\t\t\ttargetSessionFile,\n\t\t});\n\t\tthis.beforeSessionInvalidate?.();\n\t\tthis.session.dispose();\n\t}\n\n\tprivate apply(result: CreateAgentSessionRuntimeResult): void {\n\t\tthis._session = result.session;\n\t\tthis._services = result.services;\n\t\tthis._diagnostics = result.diagnostics;\n\t\tthis._modelFallbackMessage = result.modelFallbackMessage;\n\t}\n\n\tprivate async finishSessionReplacement(withSession?: (ctx: ReplacedSessionContext) => Promise<void>): Promise<void> {\n\t\tif (this.rebindSession) {\n\t\t\tawait this.rebindSession(this.session);\n\t\t}\n\t\tif (withSession) {\n\t\t\tawait withSession(this.session.createReplacedSessionContext());\n\t\t}\n\t}\n\n\tasync switchSession(\n\t\tsessionPath: string,\n\t\toptions?: { cwdOverride?: string; withSession?: (ctx: ReplacedSessionContext) => Promise<void> },\n\t): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", sessionPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionManager = SessionManager.open(sessionPath, undefined, options?.cwdOverride);\n\t\tassertSessionCwdExists(sessionManager, this.cwd);\n\t\tawait this.teardownCurrent(\"resume\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync newSession(options?: {\n\t\tparentSession?: string;\n\t\tsetup?: (sessionManager: SessionManager) => Promise<void>;\n\t\twithSession?: (ctx: ReplacedSessionContext) => Promise<void>;\n\t}): Promise<{ cancelled: boolean }> {\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"new\");\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\tif (options?.parentSession) {\n\t\t\tsessionManager.newSession({ parentSession: options.parentSession });\n\t\t}\n\n\t\tawait this.teardownCurrent(\"new\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"new\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tif (options?.setup) {\n\t\t\tawait options.setup(this.session.sessionManager);\n\t\t\tthis.session.agent.state.messages = this.session.sessionManager.buildSessionContext().messages;\n\t\t}\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false };\n\t}\n\n\tasync fork(\n\t\tentryId: string,\n\t\toptions?: { position?: \"before\" | \"at\"; withSession?: (ctx: ReplacedSessionContext) => Promise<void> },\n\t): Promise<{ cancelled: boolean; selectedText?: string }> {\n\t\tconst position = options?.position ?? \"before\";\n\t\tconst beforeResult = await this.emitBeforeFork(entryId, { position });\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn { cancelled: true };\n\t\t}\n\t\tlet targetLeafId: string | null;\n\t\tlet selectedText: string | undefined;\n\n\t\tconst selectedEntry = this.session.sessionManager.getEntry(entryId);\n\t\tif (!selectedEntry) {\n\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t}\n\n\t\tif (position === \"at\") {\n\t\t\ttargetLeafId = selectedEntry.id;\n\t\t} else {\n\t\t\tif (selectedEntry.type !== \"message\" || selectedEntry.message.role !== \"user\") {\n\t\t\t\tthrow new Error(\"Invalid entry ID for forking\");\n\t\t\t}\n\t\t\ttargetLeafId = selectedEntry.parentId;\n\t\t\tselectedText = extractUserMessageText(selectedEntry.message.content);\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tif (this.session.sessionManager.isPersisted()) {\n\t\t\tconst currentSessionFile = this.session.sessionFile;\n\t\t\tif (!currentSessionFile) {\n\t\t\t\tthrow new Error(\"Persisted session is missing a session file\");\n\t\t\t}\n\t\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\t\tif (!targetLeafId) {\n\t\t\t\tconst sessionManager = SessionManager.create(this.cwd, sessionDir);\n\t\t\t\tsessionManager.newSession({ parentSession: currentSessionFile });\n\t\t\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\t\t\tthis.apply(\n\t\t\t\t\tawait this.createRuntime({\n\t\t\t\t\t\tcwd: this.cwd,\n\t\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\t\tsessionManager,\n\t\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\t\t\treturn { cancelled: false, selectedText };\n\t\t\t}\n\n\t\t\tconst ownerLease = this.session.sessionManager.getOwnerLease();\n\t\t\tconst sessionManager = SessionManager.open(currentSessionFile, sessionDir, undefined, ownerLease);\n\t\t\tconst forkedSessionPath = sessionManager.createBranchedSession(targetLeafId);\n\t\t\tif (!forkedSessionPath) {\n\t\t\t\tthrow new Error(\"Failed to create forked session\");\n\t\t\t}\n\t\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\t\tif (sessionManager.getOwnerLease() === ownerLease) sessionManager.setOwnerLease(undefined);\n\t\t\tthis.apply(\n\t\t\t\tawait this.createRuntime({\n\t\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\t\tsessionManager,\n\t\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t\t}),\n\t\t\t);\n\t\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\t\treturn { cancelled: false, selectedText };\n\t\t}\n\n\t\tconst sessionManager = this.session.sessionManager;\n\t\tawait this.teardownCurrent(\"fork\", sessionManager.getSessionFile());\n\t\tif (!targetLeafId) {\n\t\t\tsessionManager.newSession({ parentSession: previousSessionFile });\n\t\t} else {\n\t\t\tsessionManager.createBranchedSession(targetLeafId);\n\t\t}\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: this.cwd,\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"fork\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement(options?.withSession);\n\t\treturn { cancelled: false, selectedText };\n\t}\n\n\t/**\n\t * Import a session JSONL file and switch runtime state to the imported session.\n\t *\n\t * @returns `{ cancelled: true }` when cancelled by `session_before_switch`, otherwise `{ cancelled: false }`.\n\t * @throws {SessionImportFileNotFoundError} When the input path does not exist.\n\t * @throws {MissingSessionCwdError} When the imported session cwd cannot be resolved and no override is provided.\n\t */\n\tasync importFromJsonl(inputPath: string, cwdOverride?: string): Promise<{ cancelled: boolean }> {\n\t\tconst resolvedPath = resolvePath(inputPath);\n\t\tif (!existsSync(resolvedPath)) {\n\t\t\tthrow new SessionImportFileNotFoundError(resolvedPath);\n\t\t}\n\n\t\tconst sessionDir = this.session.sessionManager.getSessionDir();\n\t\tif (!existsSync(sessionDir)) {\n\t\t\tmkdirSync(sessionDir, { recursive: true });\n\t\t}\n\n\t\tlet destinationPath = join(sessionDir, basename(resolvedPath));\n\t\tconst sourceAlreadyStored = resolve(destinationPath) === resolvedPath;\n\t\tif (!sourceAlreadyStored) {\n\t\t\tconst { name, ext } = parse(destinationPath);\n\t\t\tlet suffix = 1;\n\t\t\twhile (existsSync(destinationPath)) {\n\t\t\t\tdestinationPath = join(sessionDir, `${name}-${suffix++}${ext}`);\n\t\t\t}\n\t\t}\n\t\tconst beforeResult = await this.emitBeforeSwitch(\"resume\", destinationPath);\n\t\tif (beforeResult.cancelled) {\n\t\t\treturn beforeResult;\n\t\t}\n\n\t\tconst previousSessionFile = this.session.sessionFile;\n\t\tif (!sourceAlreadyStored) {\n\t\t\tcopyFileSync(resolvedPath, destinationPath, constants.COPYFILE_EXCL);\n\t\t}\n\n\t\tconst sessionManager = SessionManager.open(destinationPath, sessionDir, cwdOverride);\n\t\tassertSessionCwdExists(sessionManager, this.cwd);\n\t\tawait this.teardownCurrent(\"resume\", sessionManager.getSessionFile());\n\t\tthis.apply(\n\t\t\tawait this.createRuntime({\n\t\t\t\tcwd: sessionManager.getCwd(),\n\t\t\t\tagentDir: this.services.agentDir,\n\t\t\t\tsessionManager,\n\t\t\t\tsessionStartEvent: { type: \"session_start\", reason: \"resume\", previousSessionFile },\n\t\t\t}),\n\t\t);\n\t\tawait this.finishSessionReplacement();\n\t\treturn { cancelled: false };\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tawait emitSessionShutdownEvent(this.session.extensionRunner, {\n\t\t\ttype: \"session_shutdown\",\n\t\t\treason: \"quit\",\n\t\t});\n\t\tthis.beforeSessionInvalidate?.();\n\t\tthis.session.dispose();\n\t}\n}\n\n/**\n * Create the initial runtime from a runtime factory and initial session target.\n *\n * The same factory is stored on the returned AgentSessionRuntime and reused for\n * later /new, /resume, /fork, and import flows.\n */\nexport async function createAgentSessionRuntime(\n\tcreateRuntime: CreateAgentSessionRuntimeFactory,\n\toptions: {\n\t\tcwd: string;\n\t\tagentDir: string;\n\t\tsessionManager: SessionManager;\n\t\tsessionStartEvent?: SessionStartEvent;\n\t},\n): Promise<AgentSessionRuntime> {\n\tassertSessionCwdExists(options.sessionManager, options.cwd);\n\tconst result = await createRuntime(options);\n\treturn new AgentSessionRuntime(\n\t\tresult.session,\n\t\tresult.services,\n\t\tcreateRuntime,\n\t\tresult.diagnostics,\n\t\tresult.modelFallbackMessage,\n\t);\n}\n\nexport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\ttype CreateAgentSessionFromServicesOptions,\n\ttype CreateAgentSessionServicesOptions,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./agent-session-services.ts\";\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
-
import { basename, join, resolve } from "node:path";
|
|
1
|
+
import { constants, copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { basename, join, parse, resolve } from "node:path";
|
|
3
3
|
import { resolvePath } from "../utils/paths.js";
|
|
4
4
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
5
5
|
import { assertSessionCwdExists } from "./session-cwd.js";
|
|
6
6
|
import { SessionManager } from "./session-manager.js";
|
|
7
|
+
import { extractUserMessageText } from "./session-message-text.js";
|
|
7
8
|
/**
|
|
8
9
|
* Thrown when /import references a JSONL file path that does not exist.
|
|
9
10
|
*/
|
|
@@ -15,15 +16,6 @@ export class SessionImportFileNotFoundError extends Error {
|
|
|
15
16
|
this.filePath = filePath;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
|
-
function extractUserMessageText(content) {
|
|
19
|
-
if (typeof content === "string") {
|
|
20
|
-
return content;
|
|
21
|
-
}
|
|
22
|
-
return content
|
|
23
|
-
.filter((part) => part.type === "text" && typeof part.text === "string")
|
|
24
|
-
.map((part) => part.text)
|
|
25
|
-
.join("");
|
|
26
|
-
}
|
|
27
19
|
/**
|
|
28
20
|
* Owns the current AgentSession plus its cwd-bound services.
|
|
29
21
|
*
|
|
@@ -226,13 +218,13 @@ export class AgentSessionRuntime {
|
|
|
226
218
|
return { cancelled: false, selectedText };
|
|
227
219
|
}
|
|
228
220
|
const sessionManager = this.session.sessionManager;
|
|
221
|
+
await this.teardownCurrent("fork", sessionManager.getSessionFile());
|
|
229
222
|
if (!targetLeafId) {
|
|
230
|
-
sessionManager.newSession({ parentSession:
|
|
223
|
+
sessionManager.newSession({ parentSession: previousSessionFile });
|
|
231
224
|
}
|
|
232
225
|
else {
|
|
233
226
|
sessionManager.createBranchedSession(targetLeafId);
|
|
234
227
|
}
|
|
235
|
-
await this.teardownCurrent("fork", sessionManager.getSessionFile());
|
|
236
228
|
this.apply(await this.createRuntime({
|
|
237
229
|
cwd: this.cwd,
|
|
238
230
|
agentDir: this.services.agentDir,
|
|
@@ -258,14 +250,22 @@ export class AgentSessionRuntime {
|
|
|
258
250
|
if (!existsSync(sessionDir)) {
|
|
259
251
|
mkdirSync(sessionDir, { recursive: true });
|
|
260
252
|
}
|
|
261
|
-
|
|
253
|
+
let destinationPath = join(sessionDir, basename(resolvedPath));
|
|
254
|
+
const sourceAlreadyStored = resolve(destinationPath) === resolvedPath;
|
|
255
|
+
if (!sourceAlreadyStored) {
|
|
256
|
+
const { name, ext } = parse(destinationPath);
|
|
257
|
+
let suffix = 1;
|
|
258
|
+
while (existsSync(destinationPath)) {
|
|
259
|
+
destinationPath = join(sessionDir, `${name}-${suffix++}${ext}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
262
|
const beforeResult = await this.emitBeforeSwitch("resume", destinationPath);
|
|
263
263
|
if (beforeResult.cancelled) {
|
|
264
264
|
return beforeResult;
|
|
265
265
|
}
|
|
266
266
|
const previousSessionFile = this.session.sessionFile;
|
|
267
|
-
if (
|
|
268
|
-
copyFileSync(resolvedPath, destinationPath);
|
|
267
|
+
if (!sourceAlreadyStored) {
|
|
268
|
+
copyFileSync(resolvedPath, destinationPath, constants.COPYFILE_EXCL);
|
|
269
269
|
}
|
|
270
270
|
const sessionManager = SessionManager.open(destinationPath, sessionDir, cwdOverride);
|
|
271
271
|
assertSessionCwdExists(sessionManager, this.cwd);
|