@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.
- package/dist/build-info.json +6 -0
- package/dist/file-logger.d.ts +42 -0
- package/dist/file-logger.js +122 -0
- package/dist/file-logger.js.map +1 -0
- package/dist/index.d.ts +37 -13
- package/dist/index.js +42 -15
- package/dist/index.js.map +1 -1
- package/dist/kernel/adapter-config.d.ts +47 -0
- package/dist/kernel/adapter-config.js +90 -0
- package/dist/kernel/adapter-config.js.map +1 -0
- package/dist/kernel/agent-client.d.ts +25 -20
- package/dist/kernel/build-identity.d.ts +24 -0
- package/dist/kernel/build-identity.js +45 -0
- package/dist/kernel/build-identity.js.map +1 -0
- package/dist/kernel/event-router.d.ts +22 -1
- package/dist/kernel/event-router.js +45 -7
- package/dist/kernel/event-router.js.map +1 -1
- package/dist/kernel/handshake.d.ts +40 -4
- package/dist/kernel/handshake.js +46 -7
- package/dist/kernel/handshake.js.map +1 -1
- package/dist/kernel/harness-manifest.d.ts +51 -0
- package/dist/kernel/harness-manifest.js +96 -0
- package/dist/kernel/harness-manifest.js.map +1 -0
- package/dist/kernel/liveness-signal.d.ts +35 -0
- package/dist/kernel/liveness-signal.js +61 -0
- package/dist/kernel/liveness-signal.js.map +1 -0
- package/dist/kernel/liveness-watchdog.d.ts +78 -0
- package/dist/kernel/liveness-watchdog.js +115 -0
- package/dist/kernel/liveness-watchdog.js.map +1 -0
- package/dist/kernel/mcp-agent-client.d.ts +2 -2
- package/dist/kernel/mcp-agent-client.js +49 -3
- package/dist/kernel/mcp-agent-client.js.map +1 -1
- package/dist/kernel/poll-backstop.d.ts +134 -4
- package/dist/kernel/poll-backstop.js +254 -24
- package/dist/kernel/poll-backstop.js.map +1 -1
- package/dist/kernel/state-sync.d.ts +2 -13
- package/dist/kernel/state-sync.js.map +1 -1
- package/dist/notification-log.d.ts +8 -0
- package/dist/notification-log.js +10 -0
- package/dist/notification-log.js.map +1 -1
- package/dist/observability.d.ts +24 -0
- package/dist/observability.js +52 -0
- package/dist/observability.js.map +1 -0
- package/dist/prompt-format.js +82 -9
- package/dist/prompt-format.js.map +1 -1
- package/dist/tool-manager/catalog/health-revision.d.ts +43 -0
- package/dist/tool-manager/catalog/health-revision.js +61 -0
- package/dist/tool-manager/catalog/health-revision.js.map +1 -0
- package/dist/tool-manager/{tool-catalog-cache.d.ts → catalog/tool-catalog-cache.d.ts} +39 -19
- package/dist/tool-manager/{tool-catalog-cache.js → catalog/tool-catalog-cache.js} +53 -26
- package/dist/tool-manager/catalog/tool-catalog-cache.js.map +1 -0
- package/dist/tool-manager/catalog/tool-surface-reconciler.d.ts +93 -0
- package/dist/tool-manager/catalog/tool-surface-reconciler.js +101 -0
- package/dist/tool-manager/catalog/tool-surface-reconciler.js.map +1 -0
- package/dist/tool-manager/contracts.d.ts +91 -0
- package/dist/tool-manager/contracts.js +20 -0
- package/dist/tool-manager/contracts.js.map +1 -0
- package/dist/tool-manager/dispatch/dispatch.d.ts +77 -0
- package/dist/tool-manager/dispatch/dispatch.js +170 -0
- package/dist/tool-manager/dispatch/dispatch.js.map +1 -0
- package/dist/tool-manager/dispatch/tool-call-policy.d.ts +32 -0
- package/dist/tool-manager/dispatch/tool-call-policy.js +56 -0
- package/dist/tool-manager/dispatch/tool-call-policy.js.map +1 -0
- package/dist/tool-manager/{dispatcher.d.ts → orchestrator/dispatcher.d.ts} +110 -31
- package/dist/tool-manager/orchestrator/dispatcher.js +559 -0
- package/dist/tool-manager/orchestrator/dispatcher.js.map +1 -0
- package/dist/tool-manager/work-protocol/claimable-digest-tracker.d.ts +60 -0
- package/dist/tool-manager/work-protocol/claimable-digest-tracker.js +66 -0
- package/dist/tool-manager/work-protocol/claimable-digest-tracker.js.map +1 -0
- package/dist/tool-manager/work-protocol/work-lease-tracker.d.ts +70 -0
- package/dist/tool-manager/work-protocol/work-lease-tracker.js +146 -0
- package/dist/tool-manager/work-protocol/work-lease-tracker.js.map +1 -0
- package/dist/wire/mcp-transport.d.ts +1 -1
- package/dist/wire/mcp-transport.js +31 -7
- package/dist/wire/mcp-transport.js.map +1 -1
- package/dist/wire/slot-gate.d.ts +40 -0
- package/dist/wire/slot-gate.js +61 -0
- package/dist/wire/slot-gate.js.map +1 -0
- package/dist/wire/transport.d.ts +18 -0
- package/package.json +6 -3
- package/dist/kernel/instance.d.ts +0 -40
- package/dist/kernel/instance.js +0 -79
- package/dist/kernel/instance.js.map +0 -1
- package/dist/tool-manager/dispatcher.js +0 -379
- package/dist/tool-manager/dispatcher.js.map +0 -1
- package/dist/tool-manager/tool-catalog-cache.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-identity.js","sourceRoot":"","sources":["../../src/kernel/build-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,QAAgB;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAUD,MAAM,CAAC,MAAM,kBAAkB,GAAc;IAC3C,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,IAAI;IACf,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,aAAqB;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,OAAO;YACL,SAAS,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;YACxE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK;YAClB,SAAS,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YACnE,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAChE,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,kBAAkB,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* from this module and only implement their own dispatch mechanisms
|
|
7
7
|
* (Push-to-LLM for the Plugin, Sandwich pattern for the Architect).
|
|
8
8
|
*/
|
|
9
|
+
import type { DrainedPendingAction } from "./state-sync.js";
|
|
9
10
|
/** All known Hub event types */
|
|
10
11
|
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
12
|
/** Parsed, typed event envelope */
|
|
@@ -24,7 +25,27 @@ export type EventDisposition = "actionable" | "informational" | "unhandled";
|
|
|
24
25
|
* "informational" if it's FYI only, or "unhandled" if the event
|
|
25
26
|
* is not recognized for this role.
|
|
26
27
|
*/
|
|
27
|
-
export declare function classifyEvent(event: string, role: "engineer" | "architect"): EventDisposition;
|
|
28
|
+
export declare function classifyEvent(event: string, role: "engineer" | "architect" | "verifier"): EventDisposition;
|
|
29
|
+
/** Pulse-kind discriminators that route to informational level (Mission-57 W3). */
|
|
30
|
+
export declare const PULSE_KINDS: ReadonlySet<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Detect whether an event is a pulse Message (status_check or
|
|
33
|
+
* missed_threshold_escalation): a `message_arrived` event whose
|
|
34
|
+
* `data.message.payload.pulseKind` ∈ PULSE_KINDS.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isPulseEvent(eventType: string, eventData?: Record<string, unknown>): boolean;
|
|
37
|
+
export interface DrainedActionReconstruction {
|
|
38
|
+
/** {event,data} — structurally an AgentEvent for the host wake to consume. */
|
|
39
|
+
agentEvent: {
|
|
40
|
+
event: string;
|
|
41
|
+
data: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
/** Diagnostic-log action hint (identical across both shims). */
|
|
44
|
+
actionHint: string;
|
|
45
|
+
/** Notification level — pulse Messages downgrade to informational. */
|
|
46
|
+
level: "actionable" | "informational";
|
|
47
|
+
}
|
|
48
|
+
export declare function reconstructDrainedAction(item: DrainedPendingAction): DrainedActionReconstruction;
|
|
28
49
|
/**
|
|
29
50
|
* Parse raw eventData from the ConnectionManager into a typed HubEvent.
|
|
30
51
|
*/
|
|
@@ -76,13 +76,51 @@ export function classifyEvent(event, role) {
|
|
|
76
76
|
return "informational";
|
|
77
77
|
return "unhandled";
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return "
|
|
85
|
-
|
|
79
|
+
// architect + verifier (mission-93): the verifier's directed-wake surface
|
|
80
|
+
// ≈ the architect's (thread_message / review_requested / pulse / directed
|
|
81
|
+
// notifications per verifier-role.md §2.1), so it shares the architect
|
|
82
|
+
// classification set rather than mis-falling through an engineer path.
|
|
83
|
+
if (ARCHITECT_ACTIONABLE.has(event))
|
|
84
|
+
return "actionable";
|
|
85
|
+
if (ARCHITECT_INFORMATIONAL.has(event))
|
|
86
|
+
return "informational";
|
|
87
|
+
return "unhandled";
|
|
88
|
+
}
|
|
89
|
+
// ── Pulse detection (M-OpenCode-Shim-Sovereign-Dedup, idea-331) ──────
|
|
90
|
+
//
|
|
91
|
+
// Hoisted to core from the two shims (claude-plugin/src/source-attribute.ts
|
|
92
|
+
// + the opencode shim's inlined mirror — its own comment admitted the dup).
|
|
93
|
+
// Sibling to classifyEvent: both shims call isPulseEvent to downgrade a
|
|
94
|
+
// pulse Message's notification level from "actionable" to "informational"
|
|
95
|
+
// (Mission-57 W3 / Design v1.0 §4 — S3 noise reduction during high-activity
|
|
96
|
+
// sub-PR cascades). `eventData` is OPTIONAL (Claude's signature) so the one
|
|
97
|
+
// core impl serves both shims behavior-preservingly.
|
|
98
|
+
/** Pulse-kind discriminators that route to informational level (Mission-57 W3). */
|
|
99
|
+
export const PULSE_KINDS = new Set([
|
|
100
|
+
"status_check",
|
|
101
|
+
"missed_threshold_escalation",
|
|
102
|
+
]);
|
|
103
|
+
/**
|
|
104
|
+
* Detect whether an event is a pulse Message (status_check or
|
|
105
|
+
* missed_threshold_escalation): a `message_arrived` event whose
|
|
106
|
+
* `data.message.payload.pulseKind` ∈ PULSE_KINDS.
|
|
107
|
+
*/
|
|
108
|
+
export function isPulseEvent(eventType, eventData) {
|
|
109
|
+
if (eventType !== "message_arrived" || !eventData)
|
|
110
|
+
return false;
|
|
111
|
+
const message = eventData.message;
|
|
112
|
+
const payload = message?.payload;
|
|
113
|
+
return typeof payload?.pulseKind === "string" && PULSE_KINDS.has(payload.pulseKind);
|
|
114
|
+
}
|
|
115
|
+
export function reconstructDrainedAction(item) {
|
|
116
|
+
const agentEvent = { event: item.dispatchType, data: item.payload };
|
|
117
|
+
const actionHint = item.dispatchType === "thread_message"
|
|
118
|
+
? `Reply with create_thread_reply to thread ${item.entityRef}`
|
|
119
|
+
: `Owed: ${item.dispatchType} on ${item.entityRef}`;
|
|
120
|
+
const level = isPulseEvent(agentEvent.event, agentEvent.data)
|
|
121
|
+
? "informational"
|
|
122
|
+
: "actionable";
|
|
123
|
+
return { agentEvent, actionHint, level };
|
|
86
124
|
}
|
|
87
125
|
// ── Event Parsing ────────────────────────────────────────────────────
|
|
88
126
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-router.js","sourceRoot":"","sources":["../../src/kernel/event-router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"event-router.js","sourceRoot":"","sources":["../../src/kernel/event-router.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* (which proves the wire is alive and the session is bound), the agent
|
|
6
6
|
* performs this enriched handshake on entry to the `synchronizing` state. It:
|
|
7
7
|
*
|
|
8
|
-
* 1. Re-calls `register_role` with full payload (
|
|
9
|
-
*
|
|
8
|
+
* 1. Re-calls `register_role` with full payload (name, clientMetadata,
|
|
9
|
+
* advisoryTags). idea-251 D-prime Phase 2: name IS identity (was
|
|
10
|
+
* globalInstanceId pre-D-prime; that field RETIRED).
|
|
10
11
|
* 2. Parses the response for `agentId`/`sessionEpoch`/`wasCreated`.
|
|
11
12
|
* 3. Tracks epoch displacement across reconnects.
|
|
12
13
|
* 4. Halts on fatal codes (`agent_thrashing_detected`, `role_mismatch`).
|
|
@@ -27,6 +28,10 @@ export interface HandshakeClientMetadata {
|
|
|
27
28
|
hostname?: string;
|
|
28
29
|
platform?: string;
|
|
29
30
|
pid?: number;
|
|
31
|
+
proxyCommitSha?: string;
|
|
32
|
+
proxyDirty?: boolean;
|
|
33
|
+
sdkCommitSha?: string;
|
|
34
|
+
sdkDirty?: boolean;
|
|
30
35
|
}
|
|
31
36
|
export interface HandshakeAdvisoryTags {
|
|
32
37
|
/** Best-effort, drift-prone. Hub MUST NOT route on this. */
|
|
@@ -35,10 +40,16 @@ export interface HandshakeAdvisoryTags {
|
|
|
35
40
|
}
|
|
36
41
|
export interface HandshakePayload {
|
|
37
42
|
role: string;
|
|
38
|
-
globalInstanceId: string;
|
|
39
43
|
clientMetadata: HandshakeClientMetadata;
|
|
40
44
|
advisoryTags: HandshakeAdvisoryTags;
|
|
41
45
|
labels?: Record<string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* idea-251 D-prime Phase 2: name IS identity. REQUIRED. Sourced from the
|
|
48
|
+
* `OIS_AGENT_NAME` env var by the host process; passed through here. Drives
|
|
49
|
+
* agentId derivation `agent-{8-hex-of-sha256(name)}` on the Hub side.
|
|
50
|
+
* (`globalInstanceId` field RETIRED in D-prime — name replaces it.)
|
|
51
|
+
*/
|
|
52
|
+
name: string;
|
|
42
53
|
}
|
|
43
54
|
export interface HandshakeResponse {
|
|
44
55
|
agentId: string;
|
|
@@ -68,7 +79,6 @@ export declare function parseHandshakeError(result: unknown): HandshakeFatalErro
|
|
|
68
79
|
export declare function parseHandshakeResponse(result: unknown): HandshakeResponse | null;
|
|
69
80
|
export interface HandshakeConfig {
|
|
70
81
|
role: string;
|
|
71
|
-
globalInstanceId: string;
|
|
72
82
|
clientInfo: {
|
|
73
83
|
name: string;
|
|
74
84
|
version: string;
|
|
@@ -77,6 +87,10 @@ export interface HandshakeConfig {
|
|
|
77
87
|
proxyVersion: string;
|
|
78
88
|
transport: string;
|
|
79
89
|
sdkVersion: string;
|
|
90
|
+
proxyCommitSha?: string;
|
|
91
|
+
proxyDirty?: boolean;
|
|
92
|
+
sdkCommitSha?: string;
|
|
93
|
+
sdkDirty?: boolean;
|
|
80
94
|
llmModel?: string;
|
|
81
95
|
/**
|
|
82
96
|
* Mission-19 routing labels. Forwarded as the `labels` arg on the
|
|
@@ -99,6 +113,12 @@ export interface HandshakeConfig {
|
|
|
99
113
|
* When omitted, Hub uses DEFAULT_AGENT_RECEIPT_SLA_MS (30000).
|
|
100
114
|
*/
|
|
101
115
|
receiptSla?: number;
|
|
116
|
+
/**
|
|
117
|
+
* idea-251 D-prime Phase 2: name IS identity. REQUIRED. Operator sets via
|
|
118
|
+
* `OIS_AGENT_NAME` env var; host shim passes through. Hub derives
|
|
119
|
+
* `agent-{8-hex-of-sha256(name)}` agentId from this.
|
|
120
|
+
*/
|
|
121
|
+
name: string;
|
|
102
122
|
}
|
|
103
123
|
export interface HandshakeContext {
|
|
104
124
|
/** Tool executor — typically `transport.request.bind(transport)`. */
|
|
@@ -120,6 +140,13 @@ export interface HandshakeResult {
|
|
|
120
140
|
response: HandshakeResponse | null;
|
|
121
141
|
/** New epoch to persist for the next reconnect's displacement check. */
|
|
122
142
|
epoch: number;
|
|
143
|
+
/**
|
|
144
|
+
* True when register_role was REJECTED by a structured fatal error
|
|
145
|
+
* (FATAL_CODES — e.g. role_mismatch). The caller MUST HALT, not silently
|
|
146
|
+
* degrade to streaming with a fallback role. mission-93 cutover: that
|
|
147
|
+
* silent degrade masked a role-change rejection for an hour.
|
|
148
|
+
*/
|
|
149
|
+
fatal?: boolean;
|
|
123
150
|
}
|
|
124
151
|
/**
|
|
125
152
|
* bug-17 fix: resolve clientName / clientVersion with proxy-fallback.
|
|
@@ -140,6 +167,15 @@ export interface HandshakeResult {
|
|
|
140
167
|
*/
|
|
141
168
|
export declare function resolveClientName(raw: string | undefined, proxyName: string): string;
|
|
142
169
|
export declare function resolveClientVersion(raw: string | undefined, proxyVersion: string): string;
|
|
170
|
+
/**
|
|
171
|
+
* idea-251 D-prime Phase 2 + idea-355 SLICE-1 dedup: read the REQUIRED
|
|
172
|
+
* OIS_AGENT_NAME identity (name IS identity per idea-251). Reads + trims +
|
|
173
|
+
* loud-errors if absent (returns null) so misconfiguration surfaces at startup
|
|
174
|
+
* rather than a silent Hub-side identity collision. The caller keeps the
|
|
175
|
+
* one-line host-specific abort (claude `process.exit(2)`; opencode `return` —
|
|
176
|
+
* it can't kill the TUI).
|
|
177
|
+
*/
|
|
178
|
+
export declare function readRequiredAgentName(log: (m: string) => void): string | null;
|
|
143
179
|
/**
|
|
144
180
|
* Build the enriched register_role payload.
|
|
145
181
|
*/
|
package/dist/kernel/handshake.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* (which proves the wire is alive and the session is bound), the agent
|
|
6
6
|
* performs this enriched handshake on entry to the `synchronizing` state. It:
|
|
7
7
|
*
|
|
8
|
-
* 1. Re-calls `register_role` with full payload (
|
|
9
|
-
*
|
|
8
|
+
* 1. Re-calls `register_role` with full payload (name, clientMetadata,
|
|
9
|
+
* advisoryTags). idea-251 D-prime Phase 2: name IS identity (was
|
|
10
|
+
* globalInstanceId pre-D-prime; that field RETIRED).
|
|
10
11
|
* 2. Parses the response for `agentId`/`sessionEpoch`/`wasCreated`.
|
|
11
12
|
* 3. Tracks epoch displacement across reconnects.
|
|
12
13
|
* 4. Halts on fatal codes (`agent_thrashing_detected`, `role_mismatch`).
|
|
@@ -29,11 +30,25 @@ export const FATAL_CODES = new Set([
|
|
|
29
30
|
export function parseHandshakeError(result) {
|
|
30
31
|
try {
|
|
31
32
|
const r = result;
|
|
32
|
-
if (!r || r
|
|
33
|
+
if (!r || typeof r !== "object")
|
|
33
34
|
return null;
|
|
35
|
+
let body;
|
|
36
|
+
let envelopeError = false;
|
|
37
|
+
if (Array.isArray(r.content) && r.content[0]?.text) {
|
|
38
|
+
// {content:[{text:<json>}], isError} envelope shape.
|
|
39
|
+
body = JSON.parse(r.content[0].text);
|
|
40
|
+
envelopeError = r.isError === true;
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
else {
|
|
43
|
+
// Parsed-body-directly shape: executeTool unwraps the envelope on some
|
|
44
|
+
// transports (e.g. bun-serve-proxy / opencode), dropping the isError
|
|
45
|
+
// flag — so the body-level `ok:false` is the failure signal. mission-93:
|
|
46
|
+
// this unwrapped path is exactly why a role_mismatch rejection fell
|
|
47
|
+
// through to parse_failed + silent engineer/offline degrade for Steve.
|
|
48
|
+
body = r;
|
|
49
|
+
}
|
|
50
|
+
const isFailure = envelopeError || body.ok === false;
|
|
51
|
+
if (isFailure && typeof body.code === "string" && FATAL_CODES.has(body.code)) {
|
|
37
52
|
return { code: body.code, message: String(body.message ?? "") };
|
|
38
53
|
}
|
|
39
54
|
}
|
|
@@ -108,13 +123,30 @@ export function resolveClientVersion(raw, proxyVersion) {
|
|
|
108
123
|
return proxyVersion;
|
|
109
124
|
return raw;
|
|
110
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* idea-251 D-prime Phase 2 + idea-355 SLICE-1 dedup: read the REQUIRED
|
|
128
|
+
* OIS_AGENT_NAME identity (name IS identity per idea-251). Reads + trims +
|
|
129
|
+
* loud-errors if absent (returns null) so misconfiguration surfaces at startup
|
|
130
|
+
* rather than a silent Hub-side identity collision. The caller keeps the
|
|
131
|
+
* one-line host-specific abort (claude `process.exit(2)`; opencode `return` —
|
|
132
|
+
* it can't kill the TUI).
|
|
133
|
+
*/
|
|
134
|
+
export function readRequiredAgentName(log) {
|
|
135
|
+
const agentName = process.env.OIS_AGENT_NAME?.trim();
|
|
136
|
+
if (!agentName) {
|
|
137
|
+
log("[Handshake] FATAL: OIS_AGENT_NAME env var required (idea-251 D-prime). Set in ~/.config/apnex-agents/{name}.env.");
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
log(`[Handshake] OIS_AGENT_NAME=${agentName}`);
|
|
141
|
+
return agentName;
|
|
142
|
+
}
|
|
111
143
|
/**
|
|
112
144
|
* Build the enriched register_role payload.
|
|
113
145
|
*/
|
|
114
146
|
export function buildHandshakePayload(config) {
|
|
115
147
|
const payload = {
|
|
116
148
|
role: config.role,
|
|
117
|
-
|
|
149
|
+
name: config.name,
|
|
118
150
|
clientMetadata: {
|
|
119
151
|
clientName: resolveClientName(config.clientInfo.name, config.proxyName),
|
|
120
152
|
clientVersion: resolveClientVersion(config.clientInfo.version, config.proxyVersion),
|
|
@@ -125,6 +157,13 @@ export function buildHandshakePayload(config) {
|
|
|
125
157
|
hostname: hostname(),
|
|
126
158
|
platform: osPlatform(),
|
|
127
159
|
pid: process.pid,
|
|
160
|
+
// M-Build-Identity-AdvisoryTag (idea-256): forward build-identity
|
|
161
|
+
// when host provided it; omit otherwise (Hub gracefully treats
|
|
162
|
+
// absent as legacy / unknown).
|
|
163
|
+
...(config.proxyCommitSha !== undefined && { proxyCommitSha: config.proxyCommitSha }),
|
|
164
|
+
...(config.proxyDirty !== undefined && { proxyDirty: config.proxyDirty }),
|
|
165
|
+
...(config.sdkCommitSha !== undefined && { sdkCommitSha: config.sdkCommitSha }),
|
|
166
|
+
...(config.sdkDirty !== undefined && { sdkDirty: config.sdkDirty }),
|
|
128
167
|
},
|
|
129
168
|
advisoryTags: {
|
|
130
169
|
llmModel: config.llmModel ?? "unknown",
|
|
@@ -161,7 +200,7 @@ export async function performHandshake(ctx) {
|
|
|
161
200
|
log.log("agent.handshake.fatal", { code: fatal.code, message: fatal.message }, `[Handshake] FATAL ${fatal.code}: ${fatal.message}`);
|
|
162
201
|
if (ctx.onFatalHalt)
|
|
163
202
|
ctx.onFatalHalt(fatal);
|
|
164
|
-
return { response: null, epoch: ctx.previousEpoch };
|
|
203
|
+
return { response: null, epoch: ctx.previousEpoch, fatal: true };
|
|
165
204
|
}
|
|
166
205
|
const response = parseHandshakeResponse(result);
|
|
167
206
|
if (!response) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/kernel/handshake.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/kernel/handshake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** A 3-valued capability cell (scion-steelman: yes/partial/no + REASON + per-capability unevenness). */
|
|
2
|
+
export interface HarnessCapability {
|
|
3
|
+
value: "yes" | "partial" | "no";
|
|
4
|
+
reason: string;
|
|
5
|
+
}
|
|
6
|
+
export interface HarnessManifest {
|
|
7
|
+
/** Schema version (forward-compat). */
|
|
8
|
+
manifestVersion: 1;
|
|
9
|
+
/** Harness id, e.g. "claude" | "opencode". */
|
|
10
|
+
harness: string;
|
|
11
|
+
/** npm package name of the shim, e.g. "@apnex/claude-plugin". */
|
|
12
|
+
proxyName: string;
|
|
13
|
+
/** Transport id reported on handshake, e.g. "stdio-mcp-proxy". */
|
|
14
|
+
transport: string;
|
|
15
|
+
/** MCP server name, e.g. "proxy". */
|
|
16
|
+
serverName: string;
|
|
17
|
+
/** Tool-prefix / dialect the host sees, e.g. "mcp__plugin_agent-adapter_proxy__". */
|
|
18
|
+
toolPrefix: string;
|
|
19
|
+
/**
|
|
20
|
+
* Last-hop injection mechanism (capability-matrix seed). For claude this is the
|
|
21
|
+
* MCP server-notification method exposed as an experimental server capability
|
|
22
|
+
* (e.g. "claude/channel"); the shim builds serverCapabilities from it.
|
|
23
|
+
*/
|
|
24
|
+
injectionChannel: string;
|
|
25
|
+
/** Free-form mechanism descriptor, e.g. "mcp-server-notification". */
|
|
26
|
+
injectionMechanism: string;
|
|
27
|
+
/** Optional protocol dialect descriptor (e.g. "mcp"). (P1e A8 — folds the manifest's dialect field.) */
|
|
28
|
+
dialect?: string;
|
|
29
|
+
/** The 3-valued per-capability matrix (capability-name -> cell). */
|
|
30
|
+
capabilityMatrix: Record<string, HarnessCapability>;
|
|
31
|
+
/** Credential resolution order, e.g. ["defaults","file","env"]. */
|
|
32
|
+
authOrder: string[];
|
|
33
|
+
/** Per-agent ENV var NAMES (instance config); values live in the environment, never here. */
|
|
34
|
+
envTemplate: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate + parse a raw object into a HarnessManifest. Throws a field-named
|
|
38
|
+
* Error on any violation (fail-closed). Returns the typed manifest on success.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseHarnessManifest(raw: unknown): HarnessManifest;
|
|
41
|
+
/** Read + validate a manifest JSON file. Throws on read or validation failure (fail-closed). */
|
|
42
|
+
export declare function loadHarnessManifest(path: string): HarnessManifest;
|
|
43
|
+
/**
|
|
44
|
+
* Build the MCP serverCapabilities object from a manifest's injectionChannel —
|
|
45
|
+
* the experimental server-notification capability the host negotiates (e.g.
|
|
46
|
+
* { tools: {}, experimental: { "claude/channel": {} } }).
|
|
47
|
+
*/
|
|
48
|
+
export declare function serverCapabilitiesFromManifest(m: HarnessManifest): {
|
|
49
|
+
tools: Record<string, never>;
|
|
50
|
+
experimental: Record<string, Record<string, never>>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness manifest — the per-harness STANDARD config as schema-validated DATA
|
|
3
|
+
* (M-Adapter-Modernization Design §3, P1b — the #1 shim-audit structural gap).
|
|
4
|
+
*
|
|
5
|
+
* The per-harness config (proxyName, transport, serverName, tool-prefix/dialect,
|
|
6
|
+
* injection-mechanism, the 3-valued capability-matrix, auth-order, env-template)
|
|
7
|
+
* was hardcoded inline in each shim. This single-homes it as a schema-validated
|
|
8
|
+
* JSON manifest the shim LOADS, so per-harness variation is DATA (references, not
|
|
9
|
+
* topology) and a new harness is a manifest + thin glue. Per-AGENT INSTANCE values
|
|
10
|
+
* (hubUrl/token/name/labels) stay in ENV — the manifest lists only the var NAMES
|
|
11
|
+
* (envTemplate), never the values.
|
|
12
|
+
*
|
|
13
|
+
* Validation is HAND-ROLLED (no zod — it is a dev-dep only; the kernel's existing
|
|
14
|
+
* validators — parseLabels, parseHandshakeResponse — are hand-rolled too) so the
|
|
15
|
+
* published package carries no extra runtime dep. parseHarnessManifest throws a
|
|
16
|
+
* field-named error on any violation (fail-closed; a malformed manifest must not
|
|
17
|
+
* boot a mis-shaped adapter).
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
function fail(msg) {
|
|
21
|
+
throw new Error(`harness-manifest: ${msg}`);
|
|
22
|
+
}
|
|
23
|
+
function str(obj, key) {
|
|
24
|
+
const v = obj[key];
|
|
25
|
+
if (typeof v !== "string" || v.length === 0)
|
|
26
|
+
fail(`field '${key}' must be a non-empty string`);
|
|
27
|
+
return v;
|
|
28
|
+
}
|
|
29
|
+
function strArray(obj, key) {
|
|
30
|
+
const v = obj[key];
|
|
31
|
+
if (!Array.isArray(v) || !v.every((x) => typeof x === "string"))
|
|
32
|
+
fail(`field '${key}' must be a string[]`);
|
|
33
|
+
return v;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validate + parse a raw object into a HarnessManifest. Throws a field-named
|
|
37
|
+
* Error on any violation (fail-closed). Returns the typed manifest on success.
|
|
38
|
+
*/
|
|
39
|
+
export function parseHarnessManifest(raw) {
|
|
40
|
+
if (typeof raw !== "object" || raw === null)
|
|
41
|
+
fail("manifest must be a JSON object");
|
|
42
|
+
const o = raw;
|
|
43
|
+
if (o.manifestVersion !== 1)
|
|
44
|
+
fail("manifestVersion must be 1");
|
|
45
|
+
const cmRaw = o.capabilityMatrix;
|
|
46
|
+
if (typeof cmRaw !== "object" || cmRaw === null || Array.isArray(cmRaw)) {
|
|
47
|
+
fail("field 'capabilityMatrix' must be an object map");
|
|
48
|
+
}
|
|
49
|
+
const capabilityMatrix = {};
|
|
50
|
+
for (const [name, cell] of Object.entries(cmRaw)) {
|
|
51
|
+
if (typeof cell !== "object" || cell === null)
|
|
52
|
+
fail(`capability '${name}' must be an object`);
|
|
53
|
+
const c = cell;
|
|
54
|
+
if (c.value !== "yes" && c.value !== "partial" && c.value !== "no") {
|
|
55
|
+
fail(`capability '${name}'.value must be one of yes|partial|no (got ${JSON.stringify(c.value)})`);
|
|
56
|
+
}
|
|
57
|
+
if (typeof c.reason !== "string" || c.reason.length === 0) {
|
|
58
|
+
fail(`capability '${name}'.reason must be a non-empty string (the per-capability unevenness rationale)`);
|
|
59
|
+
}
|
|
60
|
+
capabilityMatrix[name] = { value: c.value, reason: c.reason };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
manifestVersion: 1,
|
|
64
|
+
harness: str(o, "harness"),
|
|
65
|
+
proxyName: str(o, "proxyName"),
|
|
66
|
+
transport: str(o, "transport"),
|
|
67
|
+
serverName: str(o, "serverName"),
|
|
68
|
+
toolPrefix: str(o, "toolPrefix"),
|
|
69
|
+
injectionChannel: str(o, "injectionChannel"),
|
|
70
|
+
injectionMechanism: str(o, "injectionMechanism"),
|
|
71
|
+
...(typeof o.dialect === "string" ? { dialect: o.dialect } : {}),
|
|
72
|
+
capabilityMatrix,
|
|
73
|
+
authOrder: strArray(o, "authOrder"),
|
|
74
|
+
envTemplate: strArray(o, "envTemplate"),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Read + validate a manifest JSON file. Throws on read or validation failure (fail-closed). */
|
|
78
|
+
export function loadHarnessManifest(path) {
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
fail(`cannot read/parse manifest at ${path}: ${err?.message ?? String(err)}`);
|
|
85
|
+
}
|
|
86
|
+
return parseHarnessManifest(raw);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build the MCP serverCapabilities object from a manifest's injectionChannel —
|
|
90
|
+
* the experimental server-notification capability the host negotiates (e.g.
|
|
91
|
+
* { tools: {}, experimental: { "claude/channel": {} } }).
|
|
92
|
+
*/
|
|
93
|
+
export function serverCapabilitiesFromManifest(m) {
|
|
94
|
+
return { tools: {}, experimental: { [m.injectionChannel]: {} } };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=harness-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-manifest.js","sourceRoot":"","sources":["../../src/kernel/harness-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** EX_TEMPFAIL — the wedged-restart container-exit code the P1e supervisor uses. */
|
|
2
|
+
export declare const WEDGED_RESTART_EXIT_CODE = 75;
|
|
3
|
+
/** Default sentinel path PID-1 watches. Override via OIS_LIVENESS_SENTINEL (P1e mounts /run). */
|
|
4
|
+
export declare const DEFAULT_LIVENESS_SENTINEL = "/run/adapter-wedged";
|
|
5
|
+
export interface LivenessLostSignalPayload {
|
|
6
|
+
reason: "session-wedged";
|
|
7
|
+
consecutiveFailures: number;
|
|
8
|
+
/** The code PID-1 should exit the CONTAINER with (NOT the shim's exit code). */
|
|
9
|
+
exitCode: number;
|
|
10
|
+
emittedAt: string;
|
|
11
|
+
pid: number;
|
|
12
|
+
lastError?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface EmitLivenessLostOptions {
|
|
15
|
+
sentinelPath?: string;
|
|
16
|
+
consecutiveFailures: number;
|
|
17
|
+
lastError?: unknown;
|
|
18
|
+
now?: () => string;
|
|
19
|
+
/** Injectable writer (tests). Default writes the file, mkdir-p the parent dir. */
|
|
20
|
+
writeFile?: (path: string, data: string) => void;
|
|
21
|
+
log?: (msg: string) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the sentinel path: explicit opt > OIS_LIVENESS_SENTINEL env > default.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveSentinelPath(explicit?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Emit the liveness-lost sentinel (the kernel->PID-1 signal). Writes the JSON
|
|
29
|
+
* payload durably and returns it. Does NOT itself exit the process — the caller
|
|
30
|
+
* chains the in-process self-exit AFTER this returns, so the sentinel is on disk
|
|
31
|
+
* before the shim dies and the supervisor can read it. Never throws out: a write
|
|
32
|
+
* failure is logged (the self-exit still proceeds; L3 lease-reclaim is the final
|
|
33
|
+
* backstop) but does not mask the wedge.
|
|
34
|
+
*/
|
|
35
|
+
export declare function emitLivenessLostSignal(opts: EmitLivenessLostOptions): LivenessLostSignalPayload;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Liveness-lost signal-contract — the kernel->supervisor seam (Design §4 P1c,
|
|
3
|
+
* the EMBEDDED exit-propagation seam).
|
|
4
|
+
*
|
|
5
|
+
* In the EMBEDDED topology (PID-1 supervisor -> CLI -> kernel-shim GRANDCHILD)
|
|
6
|
+
* the kernel CANNOT exit the container by itself: `process.exit` kills only the
|
|
7
|
+
* shim, the CLI swallows its exit code, and PID-1 (the supervisor) never sees it.
|
|
8
|
+
* So liveness-loss is signalled to PID-1 DIRECTLY + OUT-OF-BAND via a SENTINEL
|
|
9
|
+
* file. P1c (here) EMITS the sentinel; P1e's PID-1 supervisor CONSUMES it -> exits
|
|
10
|
+
* the container with WEDGED_RESTART_EXIT_CODE -> docker-L2 restart-on-exit fires
|
|
11
|
+
* -> a fresh container re-handshakes + re-claims.
|
|
12
|
+
*
|
|
13
|
+
* The distinct code (75 / EX_TEMPFAIL) carries "wedged — restart me" semantics
|
|
14
|
+
* (vs a clean exit 0). It is the SIGNAL payload's code + the code PID-1 exits the
|
|
15
|
+
* CONTAINER with — NOT the grandchild shim's exit code (which the CLI discards).
|
|
16
|
+
*/
|
|
17
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
18
|
+
import { dirname } from "node:path";
|
|
19
|
+
/** EX_TEMPFAIL — the wedged-restart container-exit code the P1e supervisor uses. */
|
|
20
|
+
export const WEDGED_RESTART_EXIT_CODE = 75;
|
|
21
|
+
/** Default sentinel path PID-1 watches. Override via OIS_LIVENESS_SENTINEL (P1e mounts /run). */
|
|
22
|
+
export const DEFAULT_LIVENESS_SENTINEL = "/run/adapter-wedged";
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the sentinel path: explicit opt > OIS_LIVENESS_SENTINEL env > default.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveSentinelPath(explicit) {
|
|
27
|
+
return explicit ?? process.env.OIS_LIVENESS_SENTINEL ?? DEFAULT_LIVENESS_SENTINEL;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Emit the liveness-lost sentinel (the kernel->PID-1 signal). Writes the JSON
|
|
31
|
+
* payload durably and returns it. Does NOT itself exit the process — the caller
|
|
32
|
+
* chains the in-process self-exit AFTER this returns, so the sentinel is on disk
|
|
33
|
+
* before the shim dies and the supervisor can read it. Never throws out: a write
|
|
34
|
+
* failure is logged (the self-exit still proceeds; L3 lease-reclaim is the final
|
|
35
|
+
* backstop) but does not mask the wedge.
|
|
36
|
+
*/
|
|
37
|
+
export function emitLivenessLostSignal(opts) {
|
|
38
|
+
const path = resolveSentinelPath(opts.sentinelPath);
|
|
39
|
+
const payload = {
|
|
40
|
+
reason: "session-wedged",
|
|
41
|
+
consecutiveFailures: opts.consecutiveFailures,
|
|
42
|
+
exitCode: WEDGED_RESTART_EXIT_CODE,
|
|
43
|
+
emittedAt: (opts.now ?? (() => new Date().toISOString()))(),
|
|
44
|
+
pid: process.pid,
|
|
45
|
+
...(opts.lastError !== undefined ? { lastError: String(opts.lastError) } : {}),
|
|
46
|
+
};
|
|
47
|
+
const write = opts.writeFile ??
|
|
48
|
+
((p, d) => {
|
|
49
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
50
|
+
writeFileSync(p, d);
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
write(path, JSON.stringify(payload, null, 2) + "\n");
|
|
54
|
+
opts.log?.(`[LivenessSignal] wrote wedged-restart sentinel ${path} (exitCode ${WEDGED_RESTART_EXIT_CODE}, ${opts.consecutiveFailures} failures); PID-1 supervisor consumes -> container-exit -> docker-L2 restart`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
opts.log?.(`[LivenessSignal] FAILED to write sentinel ${path}: ${err} — self-exit proceeds; L3 lease-reclaim is the backstop`);
|
|
58
|
+
}
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=liveness-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness-signal.js","sourceRoot":"","sources":["../../src/kernel/liveness-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,oFAAoF;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,iGAAiG;AACjG,MAAM,CAAC,MAAM,yBAAyB,GAAG,qBAAqB,CAAC;AAsB/D;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAiB;IACnD,OAAO,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,yBAAyB,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA6B;IAClE,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,OAAO,GAA8B;QACzC,MAAM,EAAE,gBAAgB;QACxB,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;QAC7C,QAAQ,EAAE,wBAAwB;QAClC,SAAS,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE;QAC3D,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/E,CAAC;IACF,MAAM,KAAK,GACT,IAAI,CAAC,SAAS;QACd,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACR,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,IAAI,CAAC;QACH,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,GAAG,EAAE,CACR,kDAAkD,IAAI,cAAc,wBAAwB,KAAK,IAAI,CAAC,mBAAmB,8EAA8E,CACxM,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,EAAE,CAAC,6CAA6C,IAAI,KAAK,GAAG,yDAAyD,CAAC,CAAC;IACjI,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|