@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.
- package/dist/hub-error.d.ts +42 -0
- package/dist/hub-error.js +74 -0
- package/dist/hub-error.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/agent-client.d.ts +192 -0
- package/dist/kernel/agent-client.js +44 -0
- package/dist/kernel/agent-client.js.map +1 -0
- package/dist/kernel/event-router.d.ts +49 -0
- package/dist/kernel/event-router.js +150 -0
- package/dist/kernel/event-router.js.map +1 -0
- package/dist/kernel/handshake.d.ts +161 -0
- package/dist/kernel/handshake.js +257 -0
- package/dist/kernel/handshake.js.map +1 -0
- package/dist/kernel/instance.d.ts +40 -0
- package/dist/kernel/instance.js +79 -0
- package/dist/kernel/instance.js.map +1 -0
- package/dist/kernel/mcp-agent-client.d.ts +139 -0
- package/dist/kernel/mcp-agent-client.js +505 -0
- package/dist/kernel/mcp-agent-client.js.map +1 -0
- package/dist/kernel/poll-backstop.d.ts +108 -0
- package/dist/kernel/poll-backstop.js +243 -0
- package/dist/kernel/poll-backstop.js.map +1 -0
- package/dist/kernel/session-claim.d.ts +67 -0
- package/dist/kernel/session-claim.js +106 -0
- package/dist/kernel/session-claim.js.map +1 -0
- package/dist/kernel/state-sync.d.ts +43 -0
- package/dist/kernel/state-sync.js +85 -0
- package/dist/kernel/state-sync.js.map +1 -0
- package/dist/logger.d.ts +82 -0
- package/dist/logger.js +114 -0
- package/dist/logger.js.map +1 -0
- package/dist/notification-log.d.ts +29 -0
- package/dist/notification-log.js +66 -0
- package/dist/notification-log.js.map +1 -0
- package/dist/prompt-format.d.ts +38 -0
- package/dist/prompt-format.js +220 -0
- package/dist/prompt-format.js.map +1 -0
- package/dist/tool-manager/dispatcher.d.ts +180 -0
- package/dist/tool-manager/dispatcher.js +379 -0
- package/dist/tool-manager/dispatcher.js.map +1 -0
- package/dist/tool-manager/tool-catalog-cache.d.ts +85 -0
- package/dist/tool-manager/tool-catalog-cache.js +137 -0
- package/dist/tool-manager/tool-catalog-cache.js.map +1 -0
- package/dist/wire/mcp-transport.d.ts +120 -0
- package/dist/wire/mcp-transport.js +447 -0
- package/dist/wire/mcp-transport.js.map +1 -0
- package/dist/wire/transport.d.ts +174 -0
- package/dist/wire/transport.js +43 -0
- package/dist/wire/transport.js.map +1 -0
- 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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|