@apnex/network-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/hub-error.d.ts +42 -0
  2. package/dist/hub-error.js +74 -0
  3. package/dist/hub-error.js.map +1 -0
  4. package/dist/index.d.ts +29 -0
  5. package/dist/index.js +31 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/kernel/agent-client.d.ts +192 -0
  8. package/dist/kernel/agent-client.js +44 -0
  9. package/dist/kernel/agent-client.js.map +1 -0
  10. package/dist/kernel/event-router.d.ts +49 -0
  11. package/dist/kernel/event-router.js +150 -0
  12. package/dist/kernel/event-router.js.map +1 -0
  13. package/dist/kernel/handshake.d.ts +161 -0
  14. package/dist/kernel/handshake.js +257 -0
  15. package/dist/kernel/handshake.js.map +1 -0
  16. package/dist/kernel/instance.d.ts +40 -0
  17. package/dist/kernel/instance.js +79 -0
  18. package/dist/kernel/instance.js.map +1 -0
  19. package/dist/kernel/mcp-agent-client.d.ts +139 -0
  20. package/dist/kernel/mcp-agent-client.js +505 -0
  21. package/dist/kernel/mcp-agent-client.js.map +1 -0
  22. package/dist/kernel/poll-backstop.d.ts +108 -0
  23. package/dist/kernel/poll-backstop.js +243 -0
  24. package/dist/kernel/poll-backstop.js.map +1 -0
  25. package/dist/kernel/session-claim.d.ts +67 -0
  26. package/dist/kernel/session-claim.js +106 -0
  27. package/dist/kernel/session-claim.js.map +1 -0
  28. package/dist/kernel/state-sync.d.ts +43 -0
  29. package/dist/kernel/state-sync.js +85 -0
  30. package/dist/kernel/state-sync.js.map +1 -0
  31. package/dist/logger.d.ts +82 -0
  32. package/dist/logger.js +114 -0
  33. package/dist/logger.js.map +1 -0
  34. package/dist/notification-log.d.ts +29 -0
  35. package/dist/notification-log.js +66 -0
  36. package/dist/notification-log.js.map +1 -0
  37. package/dist/prompt-format.d.ts +38 -0
  38. package/dist/prompt-format.js +220 -0
  39. package/dist/prompt-format.js.map +1 -0
  40. package/dist/tool-manager/dispatcher.d.ts +180 -0
  41. package/dist/tool-manager/dispatcher.js +379 -0
  42. package/dist/tool-manager/dispatcher.js.map +1 -0
  43. package/dist/tool-manager/tool-catalog-cache.d.ts +85 -0
  44. package/dist/tool-manager/tool-catalog-cache.js +137 -0
  45. package/dist/tool-manager/tool-catalog-cache.js.map +1 -0
  46. package/dist/wire/mcp-transport.d.ts +120 -0
  47. package/dist/wire/mcp-transport.js +447 -0
  48. package/dist/wire/mcp-transport.js.map +1 -0
  49. package/dist/wire/transport.d.ts +174 -0
  50. package/dist/wire/transport.js +43 -0
  51. package/dist/wire/transport.js.map +1 -0
  52. package/package.json +32 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * HubReturnedError — wraps an MCP isError envelope as a thrown Error.
3
+ *
4
+ * MCP tool calls can return two error shapes:
5
+ *
6
+ * 1. Transport-layer faults (network, 5xx, connection refused) —
7
+ * these throw naturally from the transport layer.
8
+ *
9
+ * 2. Hub-layer application errors (Zod validation, unknown tool,
10
+ * cascade-schema drift, authorization) — these come back as
11
+ * `{ isError: true, content: [{ type: "text", text: "..." }] }`
12
+ * envelopes. They do NOT throw by default — the legacy agent.call
13
+ * return path delivers them as typed values.
14
+ *
15
+ * The cognitive pipeline's `onToolError` only fires on thrown errors.
16
+ * For ErrorNormalizer to observe Hub-layer application errors, the
17
+ * envelope must be converted to a throw. `McpAgentClient` does this
18
+ * in its cognitive path by wrapping the envelope in this error class
19
+ * before propagating.
20
+ *
21
+ * Legacy callers (no cognitive pipeline) continue to see envelope-as-
22
+ * return-value — unchanged contract.
23
+ */
24
+ export declare class HubReturnedError extends Error {
25
+ readonly name = "HubReturnedError";
26
+ /** Original envelope `{ isError, content }` from the transport. */
27
+ readonly envelope: unknown;
28
+ constructor(envelope: unknown);
29
+ }
30
+ /**
31
+ * Type-guard: does this value look like an MCP isError envelope?
32
+ * Deliberately narrow — only `isError === true` + `content[0].text`
33
+ * shape qualifies. Avoids false positives on legitimate response
34
+ * payloads that happen to carry an `error` field.
35
+ */
36
+ export declare function isErrorEnvelope(value: unknown): value is {
37
+ isError: true;
38
+ content: Array<{
39
+ type?: string;
40
+ text?: string;
41
+ }>;
42
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * HubReturnedError — wraps an MCP isError envelope as a thrown Error.
3
+ *
4
+ * MCP tool calls can return two error shapes:
5
+ *
6
+ * 1. Transport-layer faults (network, 5xx, connection refused) —
7
+ * these throw naturally from the transport layer.
8
+ *
9
+ * 2. Hub-layer application errors (Zod validation, unknown tool,
10
+ * cascade-schema drift, authorization) — these come back as
11
+ * `{ isError: true, content: [{ type: "text", text: "..." }] }`
12
+ * envelopes. They do NOT throw by default — the legacy agent.call
13
+ * return path delivers them as typed values.
14
+ *
15
+ * The cognitive pipeline's `onToolError` only fires on thrown errors.
16
+ * For ErrorNormalizer to observe Hub-layer application errors, the
17
+ * envelope must be converted to a throw. `McpAgentClient` does this
18
+ * in its cognitive path by wrapping the envelope in this error class
19
+ * before propagating.
20
+ *
21
+ * Legacy callers (no cognitive pipeline) continue to see envelope-as-
22
+ * return-value — unchanged contract.
23
+ */
24
+ export class HubReturnedError extends Error {
25
+ name = "HubReturnedError";
26
+ /** Original envelope `{ isError, content }` from the transport. */
27
+ envelope;
28
+ constructor(envelope) {
29
+ super(extractErrorMessage(envelope));
30
+ this.envelope = envelope;
31
+ }
32
+ }
33
+ /**
34
+ * Type-guard: does this value look like an MCP isError envelope?
35
+ * Deliberately narrow — only `isError === true` + `content[0].text`
36
+ * shape qualifies. Avoids false positives on legitimate response
37
+ * payloads that happen to carry an `error` field.
38
+ */
39
+ export function isErrorEnvelope(value) {
40
+ if (!value || typeof value !== "object")
41
+ return false;
42
+ const v = value;
43
+ if (v.isError !== true)
44
+ return false;
45
+ if (!Array.isArray(v.content))
46
+ return false;
47
+ return true;
48
+ }
49
+ /**
50
+ * Extract a human-readable message from an isError envelope. If the
51
+ * content[0].text is valid JSON with an `error` field, return that;
52
+ * otherwise return the raw text. Falls back to a generic message
53
+ * when the envelope lacks a text block.
54
+ */
55
+ function extractErrorMessage(envelope) {
56
+ if (!envelope || typeof envelope !== "object")
57
+ return "Hub returned an error";
58
+ const content = envelope.content;
59
+ const text = Array.isArray(content) && content[0]?.text;
60
+ if (!text)
61
+ return "Hub returned an error";
62
+ try {
63
+ const parsed = JSON.parse(text);
64
+ if (typeof parsed?.error === "string")
65
+ return parsed.error;
66
+ if (typeof parsed?.message === "string")
67
+ return parsed.message;
68
+ return text;
69
+ }
70
+ catch {
71
+ return text;
72
+ }
73
+ }
74
+ //# sourceMappingURL=hub-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub-error.js","sourceRoot":"","sources":["../src/hub-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,GAAG,kBAAkB,CAAC;IACnC,mEAAmE;IAC1D,QAAQ,CAAU;IAE3B,YAAY,QAAiB;QAC3B,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAI5C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,KAGT,CAAC;IACF,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,QAAiB;IAC5C,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,uBAAuB,CAAC;IAC9E,MAAM,OAAO,GAAI,QAAmD,CAAC,OAAO,CAAC;IAC7E,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,uBAAuB,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,OAAO,MAAM,EAAE,KAAK,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC;QAC3D,IAAI,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ export type { ITransport, TransportConfig, TransportMetrics, WireState, WireReconnectCause, WireEvent, WireEventHandler, } from "./wire/transport.js";
2
+ export { McpTransport } from "./wire/mcp-transport.js";
3
+ export { McpAgentClient } from "./kernel/mcp-agent-client.js";
4
+ export type { McpAgentClientOptions } from "./kernel/mcp-agent-client.js";
5
+ export type { IAgentClient, AgentClientConfig, AgentClientCallbacks, AgentClientMetrics, AgentEvent, AgentHandshakeConfig, SessionState, SessionReconnectReason, } from "./kernel/agent-client.js";
6
+ export type { HubEventType, HubEvent, EventDisposition, } from "./kernel/event-router.js";
7
+ export { classifyEvent, parseHubEvent, createDedupFilter, } from "./kernel/event-router.js";
8
+ export { loadOrCreateGlobalInstanceId } from "./kernel/instance.js";
9
+ export type { LoadInstanceOptions } from "./kernel/instance.js";
10
+ export { FATAL_CODES, parseHandshakeError, parseHandshakeResponse, buildHandshakePayload, performHandshake, makeStdioFatalHalt, } from "./kernel/handshake.js";
11
+ export type { HandshakeClientMetadata, HandshakeAdvisoryTags, HandshakePayload, HandshakeResponse, HandshakeFatalError, HandshakeConfig, HandshakeContext, HandshakeResult, } from "./kernel/handshake.js";
12
+ export { performStateSync } from "./kernel/state-sync.js";
13
+ export type { StateSyncContext, DrainedPendingAction } from "./kernel/state-sync.js";
14
+ export { PollBackstop, defaultCursorFile, readCursor, writeCursor, } from "./kernel/poll-backstop.js";
15
+ export type { PollBackstopOptions } from "./kernel/poll-backstop.js";
16
+ export { isEagerWarmupEnabled, parseClaimSessionResponse, formatSessionClaimedLogLine, } from "./kernel/session-claim.js";
17
+ export type { ClaimSessionParsed } from "./kernel/session-claim.js";
18
+ export { createSharedDispatcher, pendingKey, injectQueueItemId, } from "./tool-manager/dispatcher.js";
19
+ export type { DispatcherClientInfo, DispatcherNotificationHooks, SharedDispatcherOptions, SharedDispatcher, } from "./tool-manager/dispatcher.js";
20
+ export { CATALOG_SCHEMA_VERSION, cachePathFor, readCache, writeCache, isCacheValid, } from "./tool-manager/tool-catalog-cache.js";
21
+ export type { ToolCatalog, CachedCatalog, } from "./tool-manager/tool-catalog-cache.js";
22
+ export { HubReturnedError, isErrorEnvelope } from "./hub-error.js";
23
+ export type { ILogger, LegacyStringLogger, LogField, LogFields } from "./logger.js";
24
+ export { getActionText, buildPromptText, buildToastMessage, } from "./prompt-format.js";
25
+ export type { PromptFormatConfig } from "./prompt-format.js";
26
+ export { appendNotification } from "./notification-log.js";
27
+ export type { NotificationLogEntry, NotificationLogOptions, } from "./notification-log.js";
28
+ export { CognitivePipeline, CognitiveTelemetry, CircuitBreaker, HubUnavailableError, WriteCallDedup, DedupTimeoutError, ToolResultCache, FlushAllOnWriteStrategy, ToolDescriptionEnricher, ErrorNormalizer, NormalizedError, ResponseSummarizer, summarizeResult, buildPaginationHint, } from "@apnex/cognitive-layer";
29
+ export type { CognitiveMiddleware, ToolCallContext, ListToolsContext, ToolErrorContext, Tool as CognitiveTool, StandardPipelineConfig, CognitiveTelemetryConfig, TelemetryEvent, TelemetryEventKind, CircuitBreakerConfig, CircuitState, CircuitStateChange, WriteCallDedupConfig, ToolResultCacheConfig, InvalidationStrategy, InvalidationDirective, CacheKey, ToolDescriptionEnricherConfig, ToolHints, ErrorNormalizerConfig, ErrorRule, CascadeDriftRule, ResponseSummarizerConfig, } from "@apnex/cognitive-layer";
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ // ── Layer 1a: Wire (transport / wire FSM) ──────────────────────────
2
+ export { McpTransport } from "./wire/mcp-transport.js";
3
+ // ── Layer 1b: Kernel (handshake / session FSM / agent client) ──────
4
+ export { McpAgentClient } from "./kernel/mcp-agent-client.js";
5
+ export { classifyEvent, parseHubEvent, createDedupFilter, } from "./kernel/event-router.js";
6
+ export { loadOrCreateGlobalInstanceId } from "./kernel/instance.js";
7
+ export { FATAL_CODES, parseHandshakeError, parseHandshakeResponse, buildHandshakePayload, performHandshake, makeStdioFatalHalt, } from "./kernel/handshake.js";
8
+ export { performStateSync } from "./kernel/state-sync.js";
9
+ export { PollBackstop, defaultCursorFile, readCursor, writeCursor, } from "./kernel/poll-backstop.js";
10
+ export { isEagerWarmupEnabled, parseClaimSessionResponse, formatSessionClaimedLogLine, } from "./kernel/session-claim.js";
11
+ // ── Layer 1c: tool-manager (Initialize/ListTools/CallTool factory) ──
12
+ //
13
+ // The MCP protocol tool-manager per Design v1.2 §4 naming discipline
14
+ // (Director-ratified rename from "MCP-boundary dispatcher" 2026-04-26).
15
+ // Distinct from the future Message-router (sovereign-package #6,
16
+ // `@apnex/message-router`, M-Push-Foundation W4). Always qualify
17
+ // ("tool-manager" or "Message-router") in new code; avoid bare
18
+ // "dispatcher".
19
+ export { createSharedDispatcher, pendingKey, injectQueueItemId, } from "./tool-manager/dispatcher.js";
20
+ export { CATALOG_SCHEMA_VERSION, cachePathFor, readCache, writeCache, isCacheValid, } from "./tool-manager/tool-catalog-cache.js";
21
+ // ── Cross-cutting primitives (root) ─────────────────────────────────
22
+ export { HubReturnedError, isErrorEnvelope } from "./hub-error.js";
23
+ export { getActionText, buildPromptText, buildToastMessage, } from "./prompt-format.js";
24
+ export { appendNotification } from "./notification-log.js";
25
+ // ── Cognitive layer re-exports (ADR-018) ────────────────────────────
26
+ // The `cognitive` option on `McpAgentClient` accepts any
27
+ // `@apnex/cognitive-layer` `CognitivePipeline`. Re-exporting the
28
+ // essentials keeps downstream consumers from needing a separate
29
+ // dependency declaration for the standard-pipeline pattern.
30
+ export { CognitivePipeline, CognitiveTelemetry, CircuitBreaker, HubUnavailableError, WriteCallDedup, DedupTimeoutError, ToolResultCache, FlushAllOnWriteStrategy, ToolDescriptionEnricher, ErrorNormalizer, NormalizedError, ResponseSummarizer, summarizeResult, buildPaginationHint, } from "@apnex/cognitive-layer";
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAYtE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,sEAAsE;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAoB9D,OAAO,EACL,aAAa,EACb,aAAa,EACb,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAGpE,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAY/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,WAAW,GACZ,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AAGnC,uEAAuE;AACvE,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,iEAAiE;AACjE,iEAAiE;AACjE,+DAA+D;AAC/D,gBAAgB;AAEhB,OAAO,EACL,sBAAsB,EACtB,UAAU,EACV,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AAQtC,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,sCAAsC,CAAC;AAM9C,uEAAuE;AAEvE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAInE,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAM3D,uEAAuE;AACvE,yDAAyD;AACzD,iEAAiE;AACjE,gEAAgE;AAChE,4DAA4D;AAE5D,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,GACpB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * IAgentClient — Layer-7 session surface for the network-adapter package.
3
+ *
4
+ * Phase 1: types only. Zero runtime change. The implementation that
5
+ * satisfies this interface is the current `UniversalClientAdapter`,
6
+ * which will be refactored in Phase 3 to own the session FSM itself
7
+ * (currently delegated down to McpConnectionManager).
8
+ *
9
+ * ## Responsibility boundary
10
+ *
11
+ * An AgentClient sits between a shim (the application-side adapter for
12
+ * Claude Code, OpenCode, etc.) and an `ITransport`. It owns:
13
+ *
14
+ * 1. The session FSM (disconnected → connecting → synchronizing →
15
+ * streaming → reconnecting). Phase 3 moves this out of
16
+ * `McpConnectionManager` and into the AgentClient implementation.
17
+ * 2. Role registration and the enriched handshake that binds a session
18
+ * to an agentId + sessionEpoch.
19
+ * 3. State sync on entry to `synchronizing` (get_task,
20
+ * get_pending_actions, state reconciliation).
21
+ * 4. Session-level reconnect policy on top of the wire: retry-once on
22
+ * `session_invalid`, drive the FSM across wire bounces, coordinate
23
+ * displacement checks (wasCreated / sessionEpoch monotonicity).
24
+ * 5. Event classification, dedup, and routing into the shim's
25
+ * actionable/informational/state callbacks.
26
+ * 6. Discovery translation — turn `ITransport.listMethods()` into the
27
+ * typed tool surface the shim consumes.
28
+ *
29
+ * It is NOT responsible for:
30
+ *
31
+ * - Wire framing, SSE keepalive watchdogs, HTTP retries. All of that
32
+ * lives in the Transport below.
33
+ * - Shim-specific UX or side effects (toast formatting, prompt
34
+ * injection, persistence). Those live in the shim above.
35
+ *
36
+ * ## Shim-facing surface
37
+ *
38
+ * A shim is handed an `IAgentClient` and sees only `call(method, ...)`,
39
+ * the session-state stream, and the classified-event callbacks. It
40
+ * does not see `ITransport` and must not. Phase 6 renames the existing
41
+ * `IClientShim` consumers onto `IAgentClient` to make this explicit.
42
+ */
43
+ import type { ILogger, LegacyStringLogger } from "../logger.js";
44
+ import type { ITransport } from "../wire/transport.js";
45
+ import type { HandshakeFatalError, HandshakeResponse } from "./handshake.js";
46
+ import type { DrainedPendingAction } from "./state-sync.js";
47
+ /**
48
+ * Session FSM state exposed to shims.
49
+ *
50
+ * disconnected → connecting → synchronizing → streaming
51
+ * ↑ ↓
52
+ * reconnecting ← (wire death)
53
+ *
54
+ * Guarantees (when state === "streaming"):
55
+ * G1: SSE stream is proven alive (at least one keepalive received)
56
+ * G2: If SSE never opens, detected within firstKeepaliveDeadline
57
+ * G3: If SSE dies mid-stream, detected within sseKeepaliveTimeout
58
+ * G4: Consecutive SSE failures trigger exponential backoff
59
+ * G5: All reconnections carry a classified SessionReconnectReason
60
+ */
61
+ export type SessionState = "disconnected" | "connecting" | "synchronizing" | "streaming" | "reconnecting";
62
+ /** Classifies why a session re-entered `reconnecting`. */
63
+ export type SessionReconnectReason = "heartbeat_failed" | "sse_watchdog" | "sse_never_opened" | "session_invalid";
64
+ /**
65
+ * Classified hub event delivered to the shim. Mirrors the existing
66
+ * `HubEvent` shape from `event-router.ts` but re-declared here so the
67
+ * AgentClient surface can be consumed without importing the router.
68
+ * Phase 3+ will unify these back to one source of truth.
69
+ */
70
+ export interface AgentEvent {
71
+ readonly id?: number | string;
72
+ readonly event: string;
73
+ readonly data: Record<string, unknown>;
74
+ readonly timestamp?: string;
75
+ readonly targetRoles?: readonly string[];
76
+ }
77
+ /**
78
+ * What a shim plugs into an AgentClient. All callbacks are optional —
79
+ * a shim that doesn't care about, say, informational events can omit
80
+ * the handler entirely and the AgentClient will drop them silently.
81
+ */
82
+ export interface AgentClientCallbacks {
83
+ onActionableEvent?: (event: AgentEvent) => void;
84
+ onInformationalEvent?: (event: AgentEvent) => void;
85
+ onStateChange?: (state: SessionState, previous: SessionState, reason?: SessionReconnectReason) => void;
86
+ }
87
+ /**
88
+ * Enriched handshake configuration. Optional — an AgentClient can run in
89
+ * "plain" mode with only a role, in which case handshake semantics
90
+ * collapse to the bare register_role call.
91
+ */
92
+ export interface AgentHandshakeConfig {
93
+ globalInstanceId: string;
94
+ proxyName: string;
95
+ proxyVersion: string;
96
+ transport: string;
97
+ sdkVersion: string;
98
+ getClientInfo: () => {
99
+ name: string;
100
+ version: string;
101
+ };
102
+ llmModel?: string;
103
+ /** Fatal-code halt (agent_thrashing_detected / role_mismatch). */
104
+ onFatalHalt?: (err: HandshakeFatalError) => void;
105
+ /** Successful handshake callback — useful for shim state tracking. */
106
+ onHandshakeComplete?: (response: HandshakeResponse) => void;
107
+ /** ADR-017: optional durable-wake HTTP endpoint for this agent. */
108
+ wakeEndpoint?: string;
109
+ /** ADR-017: optional per-agent receipt-SLA override (ms). */
110
+ receiptSla?: number;
111
+ /** Optional hook invoked by state-sync when a pending task is discovered. */
112
+ onPendingTask?: (task: Record<string, unknown>) => void;
113
+ /**
114
+ * ADR-017: optional hook invoked per drained PendingActionItem on every
115
+ * state-sync (wake cycle). Adapters thread the `id` field as
116
+ * `sourceQueueItemId` on their settling tool call to complete-ACK.
117
+ * Omitting this hook means queue items drain without consumer — the
118
+ * Hub's watchdog escalates to Director after 3× receiptSla.
119
+ */
120
+ onPendingActionItem?: (item: DrainedPendingAction) => void;
121
+ }
122
+ export interface AgentClientConfig {
123
+ /** Role the session registers as (e.g. "engineer", "architect"). */
124
+ role: string;
125
+ /**
126
+ * Mission-19 routing labels. Persisted on first-create and immutable
127
+ * thereafter (INV-AG1). Only take effect when an enriched `handshake`
128
+ * config is also supplied — the Hub's bare register_role handler
129
+ * drops labels silently because it doesn't create an Agent entity.
130
+ */
131
+ labels?: Record<string, string>;
132
+ /** Optional full enriched handshake. When omitted, plain register_role. */
133
+ handshake?: AgentHandshakeConfig;
134
+ /** Structured logger. Legacy string logger accepted during migration. */
135
+ logger?: ILogger | LegacyStringLogger;
136
+ }
137
+ /**
138
+ * Pull-style telemetry on an AgentClient. Separate from
139
+ * `TransportMetrics` because the session layer has its own counters
140
+ * (session_invalid retries, handshakes, dedup hits) that the wire
141
+ * below has no visibility into.
142
+ */
143
+ export interface AgentClientMetrics {
144
+ readonly sessionState: SessionState;
145
+ readonly agentId?: string;
146
+ readonly sessionEpoch?: number;
147
+ readonly totalHandshakes: number;
148
+ readonly totalSessionInvalidRetries: number;
149
+ readonly dedupDropCount: number;
150
+ }
151
+ export interface IAgentClient {
152
+ /** Current session FSM state. */
153
+ readonly state: SessionState;
154
+ /** True when `state === "streaming"`. Convenience alias. */
155
+ readonly isConnected: boolean;
156
+ /**
157
+ * Drive the session through connecting → synchronizing → streaming.
158
+ * Idempotent: a second start on an already-started client is a no-op.
159
+ */
160
+ start(): Promise<void>;
161
+ /** Gracefully tear down the session and close the underlying wire. */
162
+ stop(): Promise<void>;
163
+ /**
164
+ * Generic string-keyed call. Phase 2+: this routes through the
165
+ * underlying `ITransport.request` for MCP-style transports, but a
166
+ * future loopback or gRPC transport would translate here.
167
+ *
168
+ * On `session_invalid` classification, the AgentClient reconnects
169
+ * and retries exactly once on the fresh session before surfacing
170
+ * the error.
171
+ */
172
+ call(method: string, params: Record<string, unknown>): Promise<unknown>;
173
+ /**
174
+ * List the method surface currently advertised by the peer. The
175
+ * AgentClient delegates to `ITransport.listMethods()` and may cache
176
+ * the result; callers should treat this as a snapshot.
177
+ */
178
+ listMethods(): Promise<string[]>;
179
+ /** Register shim callbacks. Replaces any previously registered set. */
180
+ setCallbacks(callbacks: AgentClientCallbacks): void;
181
+ /** Current session id, if any. Undefined before first connect. */
182
+ getSessionId(): string | undefined;
183
+ /** Point-in-time session-layer metrics. */
184
+ getMetrics(): AgentClientMetrics;
185
+ /**
186
+ * Escape hatch for test harnesses and advanced shims that need to
187
+ * observe wire-level state or metrics. Implementations MAY return
188
+ * undefined if they're running on an in-memory / loopback transport
189
+ * without a meaningful wire surface.
190
+ */
191
+ getTransport(): ITransport | undefined;
192
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * IAgentClient — Layer-7 session surface for the network-adapter package.
3
+ *
4
+ * Phase 1: types only. Zero runtime change. The implementation that
5
+ * satisfies this interface is the current `UniversalClientAdapter`,
6
+ * which will be refactored in Phase 3 to own the session FSM itself
7
+ * (currently delegated down to McpConnectionManager).
8
+ *
9
+ * ## Responsibility boundary
10
+ *
11
+ * An AgentClient sits between a shim (the application-side adapter for
12
+ * Claude Code, OpenCode, etc.) and an `ITransport`. It owns:
13
+ *
14
+ * 1. The session FSM (disconnected → connecting → synchronizing →
15
+ * streaming → reconnecting). Phase 3 moves this out of
16
+ * `McpConnectionManager` and into the AgentClient implementation.
17
+ * 2. Role registration and the enriched handshake that binds a session
18
+ * to an agentId + sessionEpoch.
19
+ * 3. State sync on entry to `synchronizing` (get_task,
20
+ * get_pending_actions, state reconciliation).
21
+ * 4. Session-level reconnect policy on top of the wire: retry-once on
22
+ * `session_invalid`, drive the FSM across wire bounces, coordinate
23
+ * displacement checks (wasCreated / sessionEpoch monotonicity).
24
+ * 5. Event classification, dedup, and routing into the shim's
25
+ * actionable/informational/state callbacks.
26
+ * 6. Discovery translation — turn `ITransport.listMethods()` into the
27
+ * typed tool surface the shim consumes.
28
+ *
29
+ * It is NOT responsible for:
30
+ *
31
+ * - Wire framing, SSE keepalive watchdogs, HTTP retries. All of that
32
+ * lives in the Transport below.
33
+ * - Shim-specific UX or side effects (toast formatting, prompt
34
+ * injection, persistence). Those live in the shim above.
35
+ *
36
+ * ## Shim-facing surface
37
+ *
38
+ * A shim is handed an `IAgentClient` and sees only `call(method, ...)`,
39
+ * the session-state stream, and the classified-event callbacks. It
40
+ * does not see `ITransport` and must not. Phase 6 renames the existing
41
+ * `IClientShim` consumers onto `IAgentClient` to make this explicit.
42
+ */
43
+ export {};
44
+ //# sourceMappingURL=agent-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-client.js","sourceRoot":"","sources":["../../src/kernel/agent-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared Event Router — Classification, dedup, and parsing for Hub SSE events.
3
+ *
4
+ * Extracts the common event handling logic that was duplicated between
5
+ * the Engineer Plugin and the Architect Agent. Both consumers import
6
+ * from this module and only implement their own dispatch mechanisms
7
+ * (Push-to-LLM for the Plugin, Sandwich pattern for the Architect).
8
+ */
9
+ /** All known Hub event types */
10
+ export type HubEventType = "task_issued" | "directive_acknowledged" | "report_submitted" | "review_completed" | "revision_required" | "proposal_submitted" | "proposal_decided" | "clarification_requested" | "clarification_answered" | "thread_opened" | "thread_message" | "thread_convergence_finalized" | "thread_abandoned" | "idea_submitted" | "mission_created" | "mission_activated" | "turn_created" | "turn_updated" | "tele_defined" | "director_attention_required" | "cascade_failure" | "message_arrived" | "agent_state_changed";
11
+ /** Parsed, typed event envelope */
12
+ export interface HubEvent {
13
+ event: HubEventType | string;
14
+ data: Record<string, unknown>;
15
+ timestamp?: string;
16
+ id?: number | string;
17
+ }
18
+ /** Classification result */
19
+ export type EventDisposition = "actionable" | "informational" | "unhandled";
20
+ /**
21
+ * Classify an event for a given role.
22
+ *
23
+ * Returns "actionable" if the event requires the agent to respond,
24
+ * "informational" if it's FYI only, or "unhandled" if the event
25
+ * is not recognized for this role.
26
+ */
27
+ export declare function classifyEvent(event: string, role: "engineer" | "architect"): EventDisposition;
28
+ /**
29
+ * Parse raw eventData from the ConnectionManager into a typed HubEvent.
30
+ */
31
+ export declare function parseHubEvent(eventData: Record<string, unknown>): HubEvent;
32
+ /**
33
+ * Creates a dedup filter that tracks processed events by content hash.
34
+ * Prevents duplicate processing from notification replay or concurrent streams.
35
+ *
36
+ * The hash is computed from event type + entity ID + timestamp.
37
+ * Cache is LRU-style with a configurable max size.
38
+ */
39
+ export declare function createDedupFilter(maxCache?: number): {
40
+ /**
41
+ * Returns true if this event has already been processed.
42
+ * If not a duplicate, marks it as processed.
43
+ */
44
+ isDuplicate(event: HubEvent): boolean;
45
+ /** Clear the dedup cache */
46
+ clear(): void;
47
+ /** Current cache size */
48
+ readonly size: number;
49
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Shared Event Router — Classification, dedup, and parsing for Hub SSE events.
3
+ *
4
+ * Extracts the common event handling logic that was duplicated between
5
+ * the Engineer Plugin and the Architect Agent. Both consumers import
6
+ * from this module and only implement their own dispatch mechanisms
7
+ * (Push-to-LLM for the Plugin, Sandwich pattern for the Architect).
8
+ */
9
+ // ── Event Classification ─────────────────────────────────────────────
10
+ /** Engineer events that require the LLM to respond */
11
+ const ENGINEER_ACTIONABLE = new Set([
12
+ "thread_message",
13
+ "clarification_answered",
14
+ "task_issued",
15
+ // Mission-24 Phase 2 (M24-T3): thread_converged merged into
16
+ // thread_convergence_finalized.
17
+ "thread_convergence_finalized",
18
+ "revision_required",
19
+ // Mission-56 W2.2: Hub-side push-on-Message-create. Layer-2
20
+ // `@apnex/message-router` does kind/subkind-aware Message routing;
21
+ // Layer-1 dispositioning treats every push as wake-the-LLM since
22
+ // delivery="push-immediate" is itself the actionable signal.
23
+ "message_arrived",
24
+ ]);
25
+ /** Engineer events that are FYI (context injection, no response) */
26
+ const ENGINEER_INFORMATIONAL = new Set([
27
+ "review_completed",
28
+ "proposal_decided",
29
+ "mission_created",
30
+ "mission_activated",
31
+ "idea_submitted",
32
+ "turn_created",
33
+ "turn_updated",
34
+ "tele_defined",
35
+ // Mission-62 W1+W2 Pass 5: agent population cache-coherence updates.
36
+ // Adapter maintains a local cache (W3 wiring); LLM wake suppressed.
37
+ "agent_state_changed",
38
+ ]);
39
+ /** Architect events that require a sandwich handler response */
40
+ const ARCHITECT_ACTIONABLE = new Set([
41
+ "report_submitted",
42
+ "proposal_submitted",
43
+ "clarification_requested",
44
+ "thread_message",
45
+ // Mission-24 Phase 2 (M24-T3): thread_converged merged into
46
+ // thread_convergence_finalized.
47
+ "thread_convergence_finalized",
48
+ // Mission-56 W2.2: same dispositioning as engineer — push delivery
49
+ // is itself the wake-the-LLM signal.
50
+ "message_arrived",
51
+ ]);
52
+ /** Architect events that are FYI */
53
+ const ARCHITECT_INFORMATIONAL = new Set([
54
+ "directive_acknowledged",
55
+ "idea_submitted",
56
+ "turn_created",
57
+ "turn_updated",
58
+ "tele_defined",
59
+ "director_attention_required",
60
+ "cascade_failure",
61
+ // Mission-62 W1+W2 Pass 5: agent population cache-coherence updates.
62
+ "agent_state_changed",
63
+ ]);
64
+ /**
65
+ * Classify an event for a given role.
66
+ *
67
+ * Returns "actionable" if the event requires the agent to respond,
68
+ * "informational" if it's FYI only, or "unhandled" if the event
69
+ * is not recognized for this role.
70
+ */
71
+ export function classifyEvent(event, role) {
72
+ if (role === "engineer") {
73
+ if (ENGINEER_ACTIONABLE.has(event))
74
+ return "actionable";
75
+ if (ENGINEER_INFORMATIONAL.has(event))
76
+ return "informational";
77
+ return "unhandled";
78
+ }
79
+ else {
80
+ if (ARCHITECT_ACTIONABLE.has(event))
81
+ return "actionable";
82
+ if (ARCHITECT_INFORMATIONAL.has(event))
83
+ return "informational";
84
+ return "unhandled";
85
+ }
86
+ }
87
+ // ── Event Parsing ────────────────────────────────────────────────────
88
+ /**
89
+ * Parse raw eventData from the ConnectionManager into a typed HubEvent.
90
+ */
91
+ export function parseHubEvent(eventData) {
92
+ return {
93
+ event: eventData.event || "unknown",
94
+ data: eventData.data || {},
95
+ timestamp: eventData.timestamp,
96
+ id: eventData.id,
97
+ };
98
+ }
99
+ // ── Dedup Filter ─────────────────────────────────────────────────────
100
+ /**
101
+ * Creates a dedup filter that tracks processed events by content hash.
102
+ * Prevents duplicate processing from notification replay or concurrent streams.
103
+ *
104
+ * The hash is computed from event type + entity ID + timestamp.
105
+ * Cache is LRU-style with a configurable max size.
106
+ */
107
+ export function createDedupFilter(maxCache = 100) {
108
+ const processed = new Set();
109
+ function computeHash(event) {
110
+ const entity = event.data.taskId ||
111
+ event.data.proposalId ||
112
+ event.data.threadId ||
113
+ "unknown";
114
+ // Prefer the application-level timestamp from the event data
115
+ // (which represents the logical event identity) over the
116
+ // delivery-level timestamp set by the Hub on each send.
117
+ const ts = event.data.timestamp ||
118
+ event.timestamp ||
119
+ "";
120
+ return `${event.event}:${entity}:${ts}`;
121
+ }
122
+ return {
123
+ /**
124
+ * Returns true if this event has already been processed.
125
+ * If not a duplicate, marks it as processed.
126
+ */
127
+ isDuplicate(event) {
128
+ const hash = computeHash(event);
129
+ if (processed.has(hash))
130
+ return true;
131
+ processed.add(hash);
132
+ // Evict oldest entries if cache exceeds max
133
+ if (processed.size > maxCache) {
134
+ const first = processed.values().next().value;
135
+ if (first)
136
+ processed.delete(first);
137
+ }
138
+ return false;
139
+ },
140
+ /** Clear the dedup cache */
141
+ clear() {
142
+ processed.clear();
143
+ },
144
+ /** Current cache size */
145
+ get size() {
146
+ return processed.size;
147
+ },
148
+ };
149
+ }
150
+ //# sourceMappingURL=event-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-router.js","sourceRoot":"","sources":["../../src/kernel/event-router.ts"],"names":[],"mappings":"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"}