@apnex/network-adapter 0.1.2 → 0.1.6

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.
Files changed (86) hide show
  1. package/dist/build-info.json +6 -0
  2. package/dist/file-logger.d.ts +42 -0
  3. package/dist/file-logger.js +122 -0
  4. package/dist/file-logger.js.map +1 -0
  5. package/dist/index.d.ts +37 -13
  6. package/dist/index.js +42 -15
  7. package/dist/index.js.map +1 -1
  8. package/dist/kernel/adapter-config.d.ts +47 -0
  9. package/dist/kernel/adapter-config.js +90 -0
  10. package/dist/kernel/adapter-config.js.map +1 -0
  11. package/dist/kernel/agent-client.d.ts +25 -20
  12. package/dist/kernel/build-identity.d.ts +24 -0
  13. package/dist/kernel/build-identity.js +45 -0
  14. package/dist/kernel/build-identity.js.map +1 -0
  15. package/dist/kernel/event-router.d.ts +22 -1
  16. package/dist/kernel/event-router.js +45 -7
  17. package/dist/kernel/event-router.js.map +1 -1
  18. package/dist/kernel/handshake.d.ts +40 -4
  19. package/dist/kernel/handshake.js +46 -7
  20. package/dist/kernel/handshake.js.map +1 -1
  21. package/dist/kernel/harness-manifest.d.ts +51 -0
  22. package/dist/kernel/harness-manifest.js +96 -0
  23. package/dist/kernel/harness-manifest.js.map +1 -0
  24. package/dist/kernel/liveness-signal.d.ts +35 -0
  25. package/dist/kernel/liveness-signal.js +61 -0
  26. package/dist/kernel/liveness-signal.js.map +1 -0
  27. package/dist/kernel/liveness-watchdog.d.ts +78 -0
  28. package/dist/kernel/liveness-watchdog.js +115 -0
  29. package/dist/kernel/liveness-watchdog.js.map +1 -0
  30. package/dist/kernel/mcp-agent-client.d.ts +2 -2
  31. package/dist/kernel/mcp-agent-client.js +49 -3
  32. package/dist/kernel/mcp-agent-client.js.map +1 -1
  33. package/dist/kernel/poll-backstop.d.ts +134 -4
  34. package/dist/kernel/poll-backstop.js +254 -24
  35. package/dist/kernel/poll-backstop.js.map +1 -1
  36. package/dist/kernel/state-sync.d.ts +2 -13
  37. package/dist/kernel/state-sync.js.map +1 -1
  38. package/dist/notification-log.d.ts +8 -0
  39. package/dist/notification-log.js +10 -0
  40. package/dist/notification-log.js.map +1 -1
  41. package/dist/observability.d.ts +24 -0
  42. package/dist/observability.js +52 -0
  43. package/dist/observability.js.map +1 -0
  44. package/dist/prompt-format.js +82 -9
  45. package/dist/prompt-format.js.map +1 -1
  46. package/dist/tool-manager/catalog/health-revision.d.ts +43 -0
  47. package/dist/tool-manager/catalog/health-revision.js +61 -0
  48. package/dist/tool-manager/catalog/health-revision.js.map +1 -0
  49. package/dist/tool-manager/{tool-catalog-cache.d.ts → catalog/tool-catalog-cache.d.ts} +39 -19
  50. package/dist/tool-manager/{tool-catalog-cache.js → catalog/tool-catalog-cache.js} +53 -26
  51. package/dist/tool-manager/catalog/tool-catalog-cache.js.map +1 -0
  52. package/dist/tool-manager/catalog/tool-surface-reconciler.d.ts +93 -0
  53. package/dist/tool-manager/catalog/tool-surface-reconciler.js +101 -0
  54. package/dist/tool-manager/catalog/tool-surface-reconciler.js.map +1 -0
  55. package/dist/tool-manager/contracts.d.ts +91 -0
  56. package/dist/tool-manager/contracts.js +20 -0
  57. package/dist/tool-manager/contracts.js.map +1 -0
  58. package/dist/tool-manager/dispatch/dispatch.d.ts +77 -0
  59. package/dist/tool-manager/dispatch/dispatch.js +170 -0
  60. package/dist/tool-manager/dispatch/dispatch.js.map +1 -0
  61. package/dist/tool-manager/dispatch/tool-call-policy.d.ts +32 -0
  62. package/dist/tool-manager/dispatch/tool-call-policy.js +56 -0
  63. package/dist/tool-manager/dispatch/tool-call-policy.js.map +1 -0
  64. package/dist/tool-manager/{dispatcher.d.ts → orchestrator/dispatcher.d.ts} +110 -31
  65. package/dist/tool-manager/orchestrator/dispatcher.js +559 -0
  66. package/dist/tool-manager/orchestrator/dispatcher.js.map +1 -0
  67. package/dist/tool-manager/work-protocol/claimable-digest-tracker.d.ts +60 -0
  68. package/dist/tool-manager/work-protocol/claimable-digest-tracker.js +66 -0
  69. package/dist/tool-manager/work-protocol/claimable-digest-tracker.js.map +1 -0
  70. package/dist/tool-manager/work-protocol/work-lease-tracker.d.ts +70 -0
  71. package/dist/tool-manager/work-protocol/work-lease-tracker.js +146 -0
  72. package/dist/tool-manager/work-protocol/work-lease-tracker.js.map +1 -0
  73. package/dist/wire/mcp-transport.d.ts +1 -1
  74. package/dist/wire/mcp-transport.js +31 -7
  75. package/dist/wire/mcp-transport.js.map +1 -1
  76. package/dist/wire/slot-gate.d.ts +40 -0
  77. package/dist/wire/slot-gate.js +61 -0
  78. package/dist/wire/slot-gate.js.map +1 -0
  79. package/dist/wire/transport.d.ts +18 -0
  80. package/package.json +6 -3
  81. package/dist/kernel/instance.d.ts +0 -40
  82. package/dist/kernel/instance.js +0 -79
  83. package/dist/kernel/instance.js.map +0 -1
  84. package/dist/tool-manager/dispatcher.js +0 -379
  85. package/dist/tool-manager/dispatcher.js.map +0 -1
  86. package/dist/tool-manager/tool-catalog-cache.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ /**
2
+ * tool-surface-reconciler.ts — declared-vs-applied reconcile for the Hub
3
+ * MCP tool surface (bug-180 / FR-21).
4
+ *
5
+ * Problem this closes: the host (e.g. Claude Code) enumerates `tools/list`
6
+ * ONCE at startup. On the pre-identity probe path the dispatcher serves the
7
+ * persisted on-disk catalog (tool-catalog-cache). When a Hub redeploy changes
8
+ * the tool surface, the in-memory catalog the host holds goes stale and
9
+ * nothing re-enumerates it — neither a client hard-reset nor /reload-plugins,
10
+ * because the long-lived proxy process never re-fetches and the redeploy emits
11
+ * no in-life `notifications/tools/list_changed` to already-connected hosts.
12
+ *
13
+ * The fix treats tool-surface delivery as a reconcile, mirroring the storage
14
+ * substrate's SchemaDef-reconciler:
15
+ * - desired-state = the Hub's tool surface, versioned by `toolSurfaceRevision`
16
+ * (a deterministic hash of the registered tool set, served
17
+ * on /health; Hub-side since bug-114).
18
+ * - applied-state = the revision the host most recently enumerated.
19
+ * - watch/notify = MCP `notifications/tools/list_changed` (the Hub already
20
+ * advertises `tools.listChanged: true`).
21
+ *
22
+ * On drift between applied and desired, emit `list_changed`; the host re-calls
23
+ * `tools/list` and — identity now resolved — the dispatcher serves the live
24
+ * surface (it skips the probe-cache path once identityReady is true).
25
+ *
26
+ * This class owns ONLY the reconcile decision + the applied-revision marker.
27
+ * All I/O is injected so it is unit-testable without a live Hub:
28
+ * - fetchLiveRevision: resolve the Hub's current /health `toolSurfaceRevision`
29
+ * (null on fetch failure / a Hub that doesn't return the field).
30
+ * - readServedRevision: the revision the host's startup enumeration was
31
+ * served from — the on-disk cache's `toolSurfaceRevision` (null when no
32
+ * cache exists, i.e. a fresh install that bootstrapped live).
33
+ * - emitListChanged: emit `notifications/tools/list_changed` to the host.
34
+ *
35
+ * Two trigger points share one `reconcile()`:
36
+ * - L1 (primary): on the shim's `identityReady` event — catches the
37
+ * redeploy-then-reconnect case that caused bug-180.
38
+ * - L2 (backstop): on the existing PollBackstop heartbeat cadence — catches
39
+ * a redeploy WHILE a session stays connected (no reconnect, so no fresh
40
+ * identityReady).
41
+ *
42
+ * The applied-revision baseline is established on the first successful
43
+ * reconcile (whichever trigger fires first), seeded from the on-disk cache so
44
+ * a stale-cache-vs-live delta is detected even if L2 races ahead of L1.
45
+ */
46
+ export class ToolSurfaceReconciler {
47
+ deps;
48
+ /**
49
+ * The revision the host is believed to currently have applied. null until
50
+ * the first successful reconcile establishes the baseline.
51
+ */
52
+ appliedRevision = null;
53
+ log;
54
+ constructor(deps) {
55
+ this.deps = deps;
56
+ this.log = deps.log ?? (() => { });
57
+ }
58
+ /** Test/diagnostic accessor for the current applied-state marker. */
59
+ getAppliedRevision() {
60
+ return this.appliedRevision;
61
+ }
62
+ /**
63
+ * One reconcile pass. Idempotent and safe to call from either trigger:
64
+ * 1. Resolve the live revision; no-op on unknown (fetch failed).
65
+ * 2. On first pass, seed the applied baseline from the on-disk cache
66
+ * (what the host's startup probe served) — or from live if no cache.
67
+ * 3. On applied-vs-live drift, emit `list_changed` + advance the baseline.
68
+ *
69
+ * @param reason short label for logs (e.g. "identityReady", "heartbeat").
70
+ */
71
+ async reconcile(reason = "reconcile") {
72
+ const live = await this.deps.fetchLiveRevision();
73
+ if (live === null) {
74
+ this.log(`[tool-surface-reconcile] ${reason}: live revision unknown — skipping (cache trusted)`);
75
+ return { emitted: false, live: null };
76
+ }
77
+ // Establish the applied-state baseline on the first successful pass. Prefer
78
+ // the on-disk cache revision (the surface the pre-identity probe served);
79
+ // fall back to live when no cache exists (fresh install already enumerated
80
+ // live, so there is nothing to reconcile against).
81
+ if (this.appliedRevision === null) {
82
+ const served = this.deps.readServedRevision();
83
+ this.appliedRevision = served ?? live;
84
+ this.log(`[tool-surface-reconcile] ${reason}: baseline applied=${this.appliedRevision} (served=${served ?? "none"}, live=${live})`);
85
+ }
86
+ if (live !== this.appliedRevision) {
87
+ this.log(`[tool-surface-reconcile] ${reason}: drift applied=${this.appliedRevision} → live=${live} — emitting tools/list_changed`);
88
+ try {
89
+ this.deps.emitListChanged();
90
+ }
91
+ catch (err) {
92
+ // Never let a host-emit failure escape the reconcile loop.
93
+ this.log(`[tool-surface-reconcile] ${reason}: emitListChanged threw (non-fatal): ${err?.message ?? String(err)}`);
94
+ }
95
+ this.appliedRevision = live;
96
+ return { emitted: true, live };
97
+ }
98
+ return { emitted: false, live };
99
+ }
100
+ }
101
+ //# sourceMappingURL=tool-surface-reconciler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-surface-reconciler.js","sourceRoot":"","sources":["../../../src/tool-manager/catalog/tool-surface-reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAiCH,MAAM,OAAO,qBAAqB;IAQH;IAP7B;;;OAGG;IACK,eAAe,GAAkB,IAAI,CAAC;IAC7B,GAAG,CAAwB;IAE5C,YAA6B,IAA+B;QAA/B,SAAI,GAAJ,IAAI,CAA2B;QAC1D,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,qEAAqE;IACrE,kBAAkB;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,WAAW;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACjD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CACN,4BAA4B,MAAM,oDAAoD,CACvF,CAAC;YACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACxC,CAAC;QAED,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,mDAAmD;QACnD,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC9C,IAAI,CAAC,eAAe,GAAG,MAAM,IAAI,IAAI,CAAC;YACtC,IAAI,CAAC,GAAG,CACN,4BAA4B,MAAM,sBAAsB,IAAI,CAAC,eAAe,YAAY,MAAM,IAAI,MAAM,UAAU,IAAI,GAAG,CAC1H,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,CACN,4BAA4B,MAAM,mBAAmB,IAAI,CAAC,eAAe,WAAW,IAAI,gCAAgC,CACzH,CAAC;YACF,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,2DAA2D;gBAC3D,IAAI,CAAC,GAAG,CACN,4BAA4B,MAAM,wCAAyC,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CACnH,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACjC,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;CACF"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * contracts.ts — the agnostic Tool-Manager contract (Slice A).
3
+ *
4
+ * The internal-sovereign-module boundary for tool catalog + dispatch. These
5
+ * interfaces are host- AND transport-agnostic BY CONSTRUCTION: nothing here
6
+ * imports `@modelcontextprotocol/sdk`, `McpAgentClient`, or any binding. A
7
+ * consumer (the MCP binding, pi's native binding, a future ACP binding) depends
8
+ * ONLY on these types — never on the concrete kernel classes (A3 Air-Gap).
9
+ *
10
+ * Design: docs/designs/m-sovereign-tool-manager-design.md
11
+ * Axioms: A3 Sovereign Composition (Law of One, Air-Gap, Semantic Bit-Masking,
12
+ * Earned Exposure — this is an INTERNAL module, not a published package).
13
+ *
14
+ * SLICE A INVARIANT: purely additive. `McpAgentClient` (via `IAgentClient`)
15
+ * structurally satisfies `IToolDispatchAgent` today with zero changes — this
16
+ * file only NAMES the subset the tool-manager actually needs, so the dispatch
17
+ * authority (Slice B) can depend on the interface instead of the class.
18
+ */
19
+ /**
20
+ * Neutral tool descriptor — MCP-shaped fields (name / description / JSON-Schema
21
+ * inputSchema) but NOT an MCP SDK type. Every binding materializes its own host
22
+ * representation from this (MCP `Tool`, pi typebox `parameters`, …). This is the
23
+ * bit-perfect interface both sides agree on (A3 Semantic Bit-Masking).
24
+ *
25
+ * Structurally identical to `@apnex/cognitive-layer`'s `Tool`, deliberately
26
+ * re-declared here so the contract owns its own shape and carries no dependency
27
+ * on the cognitive layer.
28
+ */
29
+ export interface ToolDescriptor {
30
+ name: string;
31
+ description?: string;
32
+ inputSchema?: unknown;
33
+ [key: string]: unknown;
34
+ }
35
+ /**
36
+ * Options forwarded to a dispatch-time agent call. Mirrors the kernel's
37
+ * `AgentCallOptions` subset the tool-manager relies on.
38
+ */
39
+ export interface ToolDispatchCallOptions {
40
+ /** Mark this call internal-machinery — skip LLM-facing result transforms. */
41
+ internal?: boolean;
42
+ }
43
+ /**
44
+ * The minimal agent surface the Tool-Manager needs. `McpAgentClient` /
45
+ * `IAgentClient` satisfy this structurally — the tool-manager imports THIS,
46
+ * never the class, which is what keeps the module carve-able and breaks the
47
+ * would-be circular dependency (tool-manager → kernel).
48
+ *
49
+ * `listTools()` already returns the tier-filtered, cognitively-enriched surface
50
+ * (the kernel owns that filtering) — the tool-manager consumes the finished
51
+ * descriptor and does not re-derive it.
52
+ */
53
+ export interface IToolDispatchAgent {
54
+ readonly state: string;
55
+ readonly isConnected: boolean;
56
+ call(method: string, params: Record<string, unknown>, opts?: ToolDispatchCallOptions): Promise<unknown>;
57
+ listTools(): Promise<ToolDescriptor[]>;
58
+ getMetrics?(): {
59
+ agentId?: string;
60
+ };
61
+ }
62
+ /**
63
+ * Neutral dispatch result. Bindings format this into their host shape (MCP
64
+ * `content[]` + `isError`, pi `ToolResult`, …). NOT an MCP `CallToolResult`.
65
+ */
66
+ export interface ToolDispatchResult {
67
+ /** Raw dispatch return — a string, or a JSON-serializable value. */
68
+ value: unknown;
69
+ isError: boolean;
70
+ /** Present when `isError` — the normalized failure message. */
71
+ errorMessage?: string;
72
+ }
73
+ /**
74
+ * The Tool-Manager authority (Slice B fills the impl). Owns exactly two
75
+ * concerns (A3 Law of One): the tool CATALOG (what tools exist, projected for a
76
+ * host) and tool DISPATCH (the per-call behavior wrapper currently trapped in
77
+ * the MCP `CallTool` handler body). Every binding terminates in `dispatch()`.
78
+ */
79
+ export interface IToolManager {
80
+ /** Catalog authority — the derive-from-one-walk surface (A2). */
81
+ listTools(agent: IToolDispatchAgent): Promise<ToolDescriptor[]>;
82
+ /**
83
+ * Dispatch authority — the per-call behavior wrapper. Owns signal-FSM
84
+ * wrapping, queueItemId injection, active-call idle-gate bookkeeping, work-
85
+ * lease observation, and error normalization. (Slice B.)
86
+ */
87
+ dispatch(agent: IToolDispatchAgent, name: string, args: Record<string, unknown>): Promise<ToolDispatchResult>;
88
+ /** idle-gate reads (the wake/stall reconciler consumes these). */
89
+ getActiveCallCount(): number;
90
+ isIdle(): boolean;
91
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * contracts.ts — the agnostic Tool-Manager contract (Slice A).
3
+ *
4
+ * The internal-sovereign-module boundary for tool catalog + dispatch. These
5
+ * interfaces are host- AND transport-agnostic BY CONSTRUCTION: nothing here
6
+ * imports `@modelcontextprotocol/sdk`, `McpAgentClient`, or any binding. A
7
+ * consumer (the MCP binding, pi's native binding, a future ACP binding) depends
8
+ * ONLY on these types — never on the concrete kernel classes (A3 Air-Gap).
9
+ *
10
+ * Design: docs/designs/m-sovereign-tool-manager-design.md
11
+ * Axioms: A3 Sovereign Composition (Law of One, Air-Gap, Semantic Bit-Masking,
12
+ * Earned Exposure — this is an INTERNAL module, not a published package).
13
+ *
14
+ * SLICE A INVARIANT: purely additive. `McpAgentClient` (via `IAgentClient`)
15
+ * structurally satisfies `IToolDispatchAgent` today with zero changes — this
16
+ * file only NAMES the subset the tool-manager actually needs, so the dispatch
17
+ * authority (Slice B) can depend on the interface instead of the class.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/tool-manager/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * dispatch.ts — the tool DISPATCH authority (Slice B).
3
+ *
4
+ * This is the per-call behavior wrapper extracted VERBATIM from the MCP
5
+ * `CallTool` handler body in `dispatcher.ts` (the god-object). It owns the
6
+ * OIS-Hub dispatch policy that EVERY host binding must share:
7
+ * - idle-gate bookkeeping (activeCallCount) for the wake/stall reconciler
8
+ * - callToolGate wait + bug-126 timeout
9
+ * - "Hub not connected" pre-check
10
+ * - injectQueueItemId (ADR-017)
11
+ * - signal_working_* FSM wrapping (mission-62)
12
+ * - workLeases.observe (idea-353)
13
+ * - onToolCallResult host hook
14
+ * - error normalization + W10 terminal-log discipline
15
+ *
16
+ * SLICE B DISCIPLINE: this is a faithful EXTRACT-AND-DELEGATE. The logic, its
17
+ * order, the log lines, and the return shape are preserved byte-for-byte from
18
+ * the original handler so the 272-test regression suite certifies behavioral
19
+ * identity. The MCP `CallTool` handler in `dispatcher.ts` now calls
20
+ * `runToolDispatch(...)` and returns its result directly.
21
+ *
22
+ * Deliberately NOT done here (deferred to Slice C, per the two-phase rule):
23
+ * - converting the MCP-shaped return to the neutral `ToolDispatchResult`
24
+ * - relocating callToolGate / "Hub not connected" to the MCP binding
25
+ * - collapsing the closure / renaming
26
+ * Faithful move first; structural cleanup when files move.
27
+ *
28
+ * Design: docs/designs/m-sovereign-tool-manager-design.md (Slice B)
29
+ * Axioms: A3 (Air-Gap — depends on injected deps, not the closure), A8 (certify
30
+ * before ascending — regression parity is the gate).
31
+ */
32
+ import type { IToolDispatchAgent } from "../contracts.js";
33
+ import { WorkLeaseTracker } from "../work-protocol/work-lease-tracker.js";
34
+ /**
35
+ * The MCP-shaped result the CallTool handler returns today. Preserved verbatim
36
+ * in Slice B (the neutral `ToolDispatchResult` conversion is a Slice-C concern).
37
+ */
38
+ export interface McpToolCallResult {
39
+ content: Array<{
40
+ type: "text";
41
+ text: string;
42
+ }>;
43
+ isError?: boolean;
44
+ }
45
+ /**
46
+ * Explicit dependency context for a single tool dispatch. In the god-object
47
+ * these were closure variables; making them injected params is the A3 Air-Gap
48
+ * boundary being realized — `runToolDispatch` is now understandable and testable
49
+ * from its inputs alone (A3 Local Reasoning), with no hidden closure state.
50
+ */
51
+ export interface ToolDispatchContext {
52
+ /** Late-bound agent accessor (null = not connected yet). */
53
+ getAgent: () => IToolDispatchAgent | null;
54
+ /** ADR-017 queueItemId tracking map (shared with the notification path). */
55
+ pendingActionMap: Map<string, string>;
56
+ /** idea-353 lease tracker (shared with the wake/stall reconciler). */
57
+ workLeases: WorkLeaseTracker;
58
+ /** Increment on dispatch entry (idle-gate); returns nothing. */
59
+ onCallStart: () => void;
60
+ /** Decrement on dispatch exit (idle-gate). */
61
+ onCallEnd: () => void;
62
+ /** Optional gate the dispatch awaits before touching the Hub. */
63
+ callToolGate?: Promise<void>;
64
+ /** bug-126 gate timeout (ms). Default 30000; 0 disables. */
65
+ callToolGateTimeoutMs?: number;
66
+ /** idea-353 W2 host observation hook (best-effort). */
67
+ onToolCallResult?: (method: string, args: Record<string, unknown>, result: unknown) => void;
68
+ /** Diagnostic logger. */
69
+ log: (msg: string) => void;
70
+ }
71
+ /**
72
+ * Execute one host-driven tool call through the full OIS dispatch policy.
73
+ *
74
+ * Extracted verbatim from the CallTool handler; `ctx` supplies what were closure
75
+ * variables. Returns the same MCP-shaped result the handler returned.
76
+ */
77
+ export declare function runToolDispatch(ctx: ToolDispatchContext, requestedTool: string, incomingArgs: Record<string, unknown>): Promise<McpToolCallResult>;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * dispatch.ts — the tool DISPATCH authority (Slice B).
3
+ *
4
+ * This is the per-call behavior wrapper extracted VERBATIM from the MCP
5
+ * `CallTool` handler body in `dispatcher.ts` (the god-object). It owns the
6
+ * OIS-Hub dispatch policy that EVERY host binding must share:
7
+ * - idle-gate bookkeeping (activeCallCount) for the wake/stall reconciler
8
+ * - callToolGate wait + bug-126 timeout
9
+ * - "Hub not connected" pre-check
10
+ * - injectQueueItemId (ADR-017)
11
+ * - signal_working_* FSM wrapping (mission-62)
12
+ * - workLeases.observe (idea-353)
13
+ * - onToolCallResult host hook
14
+ * - error normalization + W10 terminal-log discipline
15
+ *
16
+ * SLICE B DISCIPLINE: this is a faithful EXTRACT-AND-DELEGATE. The logic, its
17
+ * order, the log lines, and the return shape are preserved byte-for-byte from
18
+ * the original handler so the 272-test regression suite certifies behavioral
19
+ * identity. The MCP `CallTool` handler in `dispatcher.ts` now calls
20
+ * `runToolDispatch(...)` and returns its result directly.
21
+ *
22
+ * Deliberately NOT done here (deferred to Slice C, per the two-phase rule):
23
+ * - converting the MCP-shaped return to the neutral `ToolDispatchResult`
24
+ * - relocating callToolGate / "Hub not connected" to the MCP binding
25
+ * - collapsing the closure / renaming
26
+ * Faithful move first; structural cleanup when files move.
27
+ *
28
+ * Design: docs/designs/m-sovereign-tool-manager-design.md (Slice B)
29
+ * Axioms: A3 (Air-Gap — depends on injected deps, not the closure), A8 (certify
30
+ * before ascending — regression parity is the gate).
31
+ */
32
+ import { injectQueueItemId, TOOL_CALL_SIGNAL_SKIP } from "./tool-call-policy.js";
33
+ /** True when the agent is present and not explicitly disconnected. */
34
+ function isUsableAgent(agent) {
35
+ return !!agent && agent.isConnected !== false;
36
+ }
37
+ /**
38
+ * Execute one host-driven tool call through the full OIS dispatch policy.
39
+ *
40
+ * Extracted verbatim from the CallTool handler; `ctx` supplies what were closure
41
+ * variables. Returns the same MCP-shaped result the handler returned.
42
+ */
43
+ export async function runToolDispatch(ctx, requestedTool, incomingArgs) {
44
+ const { log } = ctx;
45
+ const callStartedAt = Date.now();
46
+ log(`[CallTool] ${requestedTool} entered`);
47
+ // idea-353 W1 idle-gate: mark the agent busy for the full lifespan of this
48
+ // host CallTool (gate-wait included), cleared in the finally below.
49
+ ctx.onCallStart();
50
+ try {
51
+ if (ctx.callToolGate) {
52
+ log(`[CallTool] ${requestedTool} awaiting callToolGate`);
53
+ // mission-88 W10 (bug-126 fix): timeout the gate await to prevent
54
+ // indefinite hang on pending-forever sessionReady. Default 30s; set
55
+ // callToolGateTimeoutMs=0 to disable (test-only). Structured
56
+ // `gate-timeout` log emitted on timeout; isError response returned.
57
+ const timeoutMs = ctx.callToolGateTimeoutMs ?? 30000;
58
+ if (timeoutMs > 0) {
59
+ let timeoutHandle;
60
+ const timeoutPromise = new Promise((_resolve, reject) => {
61
+ timeoutHandle = setTimeout(() => {
62
+ reject(new Error(`callToolGate timeout after ${timeoutMs}ms`));
63
+ }, timeoutMs);
64
+ });
65
+ try {
66
+ await Promise.race([ctx.callToolGate, timeoutPromise]);
67
+ }
68
+ finally {
69
+ if (timeoutHandle)
70
+ clearTimeout(timeoutHandle);
71
+ }
72
+ }
73
+ else {
74
+ await ctx.callToolGate;
75
+ }
76
+ log(`[CallTool] ${requestedTool} gate passed (+${Date.now() - callStartedAt}ms)`);
77
+ }
78
+ const agent = ctx.getAgent();
79
+ if (!isUsableAgent(agent)) {
80
+ log(`[CallTool] ${requestedTool} aborted — Hub not connected`);
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: JSON.stringify({
86
+ error: "Hub not connected",
87
+ message: "The Hub adapter is not currently connected.",
88
+ }),
89
+ },
90
+ ],
91
+ isError: true,
92
+ };
93
+ }
94
+ const name = requestedTool;
95
+ const outgoingArgs = injectQueueItemId(name, incomingArgs, ctx.pendingActionMap);
96
+ // ── Mission-62 W3 — activity FSM signal wrapping ─────────────
97
+ // Wrap each LLM-driven tool call with signal_working_started +
98
+ // signal_working_completed RPCs (fire-and-forget). Skip-list prevents
99
+ // recursion + handshake/lifecycle tools.
100
+ const wrapWithSignal = !TOOL_CALL_SIGNAL_SKIP.has(name);
101
+ if (wrapWithSignal) {
102
+ agent.call("signal_working_started", { toolName: name }).catch((err) => {
103
+ log(`[mission-62] signal_working_started fire-and-forget failed (non-fatal): ${err?.message ?? err}`);
104
+ });
105
+ }
106
+ let result;
107
+ const agentCallStart = Date.now();
108
+ log(`[CallTool] ${name} dispatching to agent.call (wrapWithSignal=${wrapWithSignal})`);
109
+ try {
110
+ result = await agent.call(name, outgoingArgs);
111
+ log(`[CallTool] ${name} agent.call returned in ${Date.now() - agentCallStart}ms`);
112
+ // idea-355 §4.3 (invariant #3) — kernel-side W2 observer. Feed this
113
+ // agent's own work-verb results to the lease tracker so the heartbeat
114
+ // tick's stall-prompt has a populated lease map. Best-effort.
115
+ try {
116
+ ctx.workLeases.observe(name, outgoingArgs, result, Date.now());
117
+ }
118
+ catch (obsErr) {
119
+ log(`[idea-353] kernel lease observe threw (non-fatal): ${obsErr?.message ?? obsErr}`);
120
+ }
121
+ // idea-353 W2: optional host hook for host-specific observation.
122
+ if (ctx.onToolCallResult) {
123
+ try {
124
+ ctx.onToolCallResult(name, outgoingArgs, result);
125
+ }
126
+ catch (hookErr) {
127
+ log(`[idea-353] onToolCallResult hook threw (non-fatal): ${hookErr?.message ?? hookErr}`);
128
+ }
129
+ }
130
+ }
131
+ finally {
132
+ if (wrapWithSignal) {
133
+ agent.call("signal_working_completed", {}).catch((err) => {
134
+ log(`[mission-62] signal_working_completed fire-and-forget failed (non-fatal): ${err?.message ?? err}`);
135
+ });
136
+ }
137
+ }
138
+ log(`[CallTool] ${name} completed in ${Date.now() - callStartedAt}ms total`);
139
+ return {
140
+ content: [
141
+ {
142
+ type: "text",
143
+ text: typeof result === "string"
144
+ ? result
145
+ : JSON.stringify(result, null, 2),
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ catch (err) {
151
+ const message = err instanceof Error ? err.message : String(err);
152
+ // mission-88 W10 (bug-126 fix): post-condition logging discipline — every
153
+ // CallTool entry MUST emit exactly one terminal log line.
154
+ const outcome = message.startsWith("callToolGate timeout")
155
+ ? "gate-timeout"
156
+ : "error-response";
157
+ log(`[CallTool] ${requestedTool} terminal: outcome=${outcome} elapsed=${Date.now() - callStartedAt}ms message="${message}"`);
158
+ return {
159
+ content: [
160
+ { type: "text", text: JSON.stringify({ error: message, w10_outcome: outcome }) },
161
+ ],
162
+ isError: true,
163
+ };
164
+ }
165
+ finally {
166
+ // idea-353 W1 idle-gate: this CallTool is no longer in flight.
167
+ ctx.onCallEnd();
168
+ }
169
+ }
170
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../../src/tool-manager/dispatch/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * tool-call-policy.ts — pure OIS-Hub tool-call policy helpers (Slice B).
3
+ *
4
+ * Extracted VERBATIM from `dispatcher.ts` so both the dispatch authority
5
+ * (`dispatch.ts`) and the god-object shell can share them without a circular
6
+ * import. Pure functions + one constant; no closure, no MCP, no kernel.
7
+ *
8
+ * These encode OIS-Hub-specific dispatch POLICY (the §8 debt in the design):
9
+ * the thread/queue queueItemId settlement rule + the signal-FSM skip-list. They
10
+ * live in the tool-manager module (isolated), NOT in a generic core — which is
11
+ * exactly why the module is not a publishable package (Earned Exposure / the
12
+ * standalone-utility test).
13
+ */
14
+ /**
15
+ * Mission-62 W3: tools that should NOT trigger signal_working_* wrapping. The
16
+ * signal_* tools themselves would recurse infinitely; register_role +
17
+ * claim_session + drain_pending_actions are lifecycle tools, not semantic
18
+ * tool-call-work.
19
+ */
20
+ export declare const TOOL_CALL_SIGNAL_SKIP: ReadonlySet<string>;
21
+ /** Compose the pendingActionMap key. Pure helper; exported for tests. */
22
+ export declare function pendingKey(dispatchType: string, entityRef: string): string;
23
+ /**
24
+ * Inject `sourceQueueItemId` into a settling tool call's arguments when a
25
+ * pendingActionMap entry is registered for the call's target. Currently only
26
+ * `create_thread_reply` settles a thread_message dispatch; extend this set as
27
+ * new auto-injection rules are ratified.
28
+ *
29
+ * Side effect: deletes the consumed map entry. Explicit sourceQueueItemId in the
30
+ * args wins over the map (no rewrite).
31
+ */
32
+ export declare function injectQueueItemId(name: string, args: Record<string, unknown>, pendingActionMap: Map<string, string>): Record<string, unknown>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * tool-call-policy.ts — pure OIS-Hub tool-call policy helpers (Slice B).
3
+ *
4
+ * Extracted VERBATIM from `dispatcher.ts` so both the dispatch authority
5
+ * (`dispatch.ts`) and the god-object shell can share them without a circular
6
+ * import. Pure functions + one constant; no closure, no MCP, no kernel.
7
+ *
8
+ * These encode OIS-Hub-specific dispatch POLICY (the §8 debt in the design):
9
+ * the thread/queue queueItemId settlement rule + the signal-FSM skip-list. They
10
+ * live in the tool-manager module (isolated), NOT in a generic core — which is
11
+ * exactly why the module is not a publishable package (Earned Exposure / the
12
+ * standalone-utility test).
13
+ */
14
+ /**
15
+ * Mission-62 W3: tools that should NOT trigger signal_working_* wrapping. The
16
+ * signal_* tools themselves would recurse infinitely; register_role +
17
+ * claim_session + drain_pending_actions are lifecycle tools, not semantic
18
+ * tool-call-work.
19
+ */
20
+ export const TOOL_CALL_SIGNAL_SKIP = new Set([
21
+ "signal_working_started",
22
+ "signal_working_completed",
23
+ "signal_quota_blocked",
24
+ "signal_quota_recovered",
25
+ "register_role",
26
+ "claim_session",
27
+ "drain_pending_actions",
28
+ ]);
29
+ /** Compose the pendingActionMap key. Pure helper; exported for tests. */
30
+ export function pendingKey(dispatchType, entityRef) {
31
+ return `${dispatchType}:${entityRef}`;
32
+ }
33
+ /**
34
+ * Inject `sourceQueueItemId` into a settling tool call's arguments when a
35
+ * pendingActionMap entry is registered for the call's target. Currently only
36
+ * `create_thread_reply` settles a thread_message dispatch; extend this set as
37
+ * new auto-injection rules are ratified.
38
+ *
39
+ * Side effect: deletes the consumed map entry. Explicit sourceQueueItemId in the
40
+ * args wins over the map (no rewrite).
41
+ */
42
+ export function injectQueueItemId(name, args, pendingActionMap) {
43
+ if (name !== "create_thread_reply")
44
+ return args;
45
+ const threadId = args.threadId;
46
+ if (typeof threadId !== "string")
47
+ return args;
48
+ if ("sourceQueueItemId" in args)
49
+ return args;
50
+ const queueItemId = pendingActionMap.get(pendingKey("thread_message", threadId));
51
+ if (!queueItemId)
52
+ return args;
53
+ pendingActionMap.delete(pendingKey("thread_message", threadId));
54
+ return { ...args, sourceQueueItemId: queueItemId };
55
+ }
56
+ //# sourceMappingURL=tool-call-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-call-policy.js","sourceRoot":"","sources":["../../../src/tool-manager/dispatch/tool-call-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC;IAChE,wBAAwB;IACxB,0BAA0B;IAC1B,sBAAsB;IACtB,wBAAwB;IACxB,eAAe;IACf,eAAe;IACf,uBAAuB;CACxB,CAAC,CAAC;AAEH,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,YAAoB,EAAE,SAAiB;IAChE,OAAO,GAAG,YAAY,IAAI,SAAS,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,IAA6B,EAC7B,gBAAqC;IAErC,IAAI,IAAI,KAAK,qBAAqB;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC/B,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,mBAAmB,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjF,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,GAAG,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC;AACrD,CAAC"}