@rkat/mobkit-sdk 0.4.9

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 (82) hide show
  1. package/dist/agent-builder.d.ts +44 -0
  2. package/dist/agent-builder.d.ts.map +1 -0
  3. package/dist/agent-builder.js +111 -0
  4. package/dist/agent-builder.js.map +1 -0
  5. package/dist/builder.d.ts +74 -0
  6. package/dist/builder.d.ts.map +1 -0
  7. package/dist/builder.js +145 -0
  8. package/dist/builder.js.map +1 -0
  9. package/dist/cjs/agent-builder.cjs +114 -0
  10. package/dist/cjs/builder.cjs +182 -0
  11. package/dist/cjs/client.cjs +182 -0
  12. package/dist/cjs/config/auth.cjs +44 -0
  13. package/dist/cjs/config/index.cjs +51 -0
  14. package/dist/cjs/config/memory.cjs +28 -0
  15. package/dist/cjs/config/session-store.cjs +41 -0
  16. package/dist/cjs/errors.cjs +82 -0
  17. package/dist/cjs/events.cjs +176 -0
  18. package/dist/cjs/helpers.cjs +72 -0
  19. package/dist/cjs/index.cjs +130 -0
  20. package/dist/cjs/models.cjs +124 -0
  21. package/dist/cjs/runtime.cjs +476 -0
  22. package/dist/cjs/sse.cjs +79 -0
  23. package/dist/cjs/transport.cjs +282 -0
  24. package/dist/cjs/types.cjs +376 -0
  25. package/dist/client.d.ts +79 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/client.js +178 -0
  28. package/dist/client.js.map +1 -0
  29. package/dist/config/auth.d.ts +28 -0
  30. package/dist/config/auth.d.ts.map +1 -0
  31. package/dist/config/auth.js +39 -0
  32. package/dist/config/auth.js.map +1 -0
  33. package/dist/config/index.d.ts +16 -0
  34. package/dist/config/index.d.ts.map +1 -0
  35. package/dist/config/index.js +16 -0
  36. package/dist/config/index.js.map +1 -0
  37. package/dist/config/memory.d.ts +16 -0
  38. package/dist/config/memory.d.ts.map +1 -0
  39. package/dist/config/memory.js +25 -0
  40. package/dist/config/memory.js.map +1 -0
  41. package/dist/config/session-store.d.ts +23 -0
  42. package/dist/config/session-store.d.ts.map +1 -0
  43. package/dist/config/session-store.js +36 -0
  44. package/dist/config/session-store.js.map +1 -0
  45. package/dist/errors.d.ts +48 -0
  46. package/dist/errors.d.ts.map +1 -0
  47. package/dist/errors.js +74 -0
  48. package/dist/errors.js.map +1 -0
  49. package/dist/events.d.ts +126 -0
  50. package/dist/events.d.ts.map +1 -0
  51. package/dist/events.js +164 -0
  52. package/dist/events.js.map +1 -0
  53. package/dist/helpers.d.ts +58 -0
  54. package/dist/helpers.d.ts.map +1 -0
  55. package/dist/helpers.js +62 -0
  56. package/dist/helpers.js.map +1 -0
  57. package/dist/index.cjs +2 -0
  58. package/dist/index.d.ts +48 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +49 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/models.d.ts +61 -0
  63. package/dist/models.d.ts.map +1 -0
  64. package/dist/models.js +118 -0
  65. package/dist/models.js.map +1 -0
  66. package/dist/runtime.d.ts +131 -0
  67. package/dist/runtime.d.ts.map +1 -0
  68. package/dist/runtime.js +470 -0
  69. package/dist/runtime.js.map +1 -0
  70. package/dist/sse.d.ts +21 -0
  71. package/dist/sse.d.ts.map +1 -0
  72. package/dist/sse.js +76 -0
  73. package/dist/sse.js.map +1 -0
  74. package/dist/transport.d.ts +84 -0
  75. package/dist/transport.d.ts.map +1 -0
  76. package/dist/transport.js +275 -0
  77. package/dist/transport.js.map +1 -0
  78. package/dist/types.d.ts +209 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +348 -0
  81. package/dist/types.js.map +1 -0
  82. package/package.json +34 -0
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ /**
3
+ * Persistent subprocess transport for MobKit JSON-RPC.
4
+ *
5
+ * Keeps a long-lived gateway binary alive, communicating over stdin/stdout
6
+ * newline-delimited JSON. Supports bidirectional callbacks from Rust.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PersistentTransport = void 0;
10
+ exports.buildJsonRpcRequest = buildJsonRpcRequest;
11
+ exports.createGatewaySyncTransport = createGatewaySyncTransport;
12
+ exports.createGatewayAsyncTransport = createGatewayAsyncTransport;
13
+ exports.createJsonRpcHttpTransport = createJsonRpcHttpTransport;
14
+ const node_child_process_1 = require("node:child_process");
15
+ const node_readline_1 = require("node:readline");
16
+ // -- Helpers --------------------------------------------------------------
17
+ function buildJsonRpcRequest(id, method, params) {
18
+ return { jsonrpc: "2.0", id, method, params };
19
+ }
20
+ function sanitizeForJson(obj) {
21
+ if (obj === null || obj === undefined)
22
+ return obj;
23
+ if (typeof obj === "boolean" || typeof obj === "number" || typeof obj === "string")
24
+ return obj;
25
+ if (Array.isArray(obj))
26
+ return obj.map(sanitizeForJson);
27
+ if (typeof obj === "object") {
28
+ const result = {};
29
+ for (const [k, v] of Object.entries(obj)) {
30
+ result[k] = sanitizeForJson(v);
31
+ }
32
+ return result;
33
+ }
34
+ return String(obj);
35
+ }
36
+ // -- PersistentTransport --------------------------------------------------
37
+ /**
38
+ * Long-lived gateway subprocess communicating over stdin/stdout JSON-RPC.
39
+ *
40
+ * Uses a readline reader to multiplex responses and callbacks. Unlike
41
+ * per-call subprocess transports, this keeps the process alive so mob
42
+ * state persists across calls.
43
+ */
44
+ class PersistentTransport {
45
+ gatewayBin;
46
+ _process = null;
47
+ _env;
48
+ _timeout;
49
+ _callbackHandler = null;
50
+ _pending = new Map();
51
+ constructor(gatewayBin, options) {
52
+ this.gatewayBin = gatewayBin;
53
+ this._env = { ...process.env, ...(options?.env ?? {}) };
54
+ this._timeout = options?.timeout ?? 60_000;
55
+ }
56
+ setCallbackHandler(handler) {
57
+ this._callbackHandler = handler;
58
+ }
59
+ start() {
60
+ if (this._process !== null && this._process.exitCode === null) {
61
+ return;
62
+ }
63
+ this._process = (0, node_child_process_1.spawn)(this.gatewayBin, ["--persistent"], {
64
+ env: this._env,
65
+ stdio: ["pipe", "pipe", "ignore"],
66
+ });
67
+ const child = this._process;
68
+ // Background reader on stdout
69
+ if (child.stdout) {
70
+ const rl = (0, node_readline_1.createInterface)({ input: child.stdout });
71
+ rl.on("line", (line) => {
72
+ let msg;
73
+ try {
74
+ msg = JSON.parse(line);
75
+ }
76
+ catch {
77
+ return;
78
+ }
79
+ if ("method" in msg) {
80
+ this._handleCallback(msg);
81
+ }
82
+ else if ("id" in msg) {
83
+ const msgId = String(msg.id);
84
+ const pending = this._pending.get(msgId);
85
+ if (pending) {
86
+ this._pending.delete(msgId);
87
+ pending.resolve(msg);
88
+ }
89
+ }
90
+ });
91
+ rl.on("close", () => {
92
+ // Process closed stdout — fail all pending requests
93
+ for (const [id, pending] of this._pending) {
94
+ this._pending.delete(id);
95
+ pending.resolve({
96
+ jsonrpc: "2.0",
97
+ id,
98
+ error: { code: -32099, message: "subprocess died" },
99
+ });
100
+ }
101
+ });
102
+ }
103
+ child.on("error", () => {
104
+ // Process spawn error — fail all pending
105
+ for (const [id, pending] of this._pending) {
106
+ this._pending.delete(id);
107
+ pending.reject(new Error("gateway process failed to start"));
108
+ }
109
+ });
110
+ }
111
+ _handleCallback(msg) {
112
+ if (!this._callbackHandler)
113
+ return;
114
+ const method = String(msg.method ?? "");
115
+ const params = (typeof msg.params === "object" && msg.params !== null
116
+ ? msg.params
117
+ : {});
118
+ const callbackId = msg.id !== undefined ? String(msg.id) : null;
119
+ this._callbackHandler(method, params)
120
+ .then((result) => {
121
+ if (callbackId === null)
122
+ return; // Notification — no response
123
+ this._writeLine({
124
+ jsonrpc: "2.0",
125
+ id: callbackId,
126
+ result: sanitizeForJson(result),
127
+ });
128
+ })
129
+ .catch((err) => {
130
+ if (callbackId === null)
131
+ return;
132
+ this._writeLine({
133
+ jsonrpc: "2.0",
134
+ id: callbackId,
135
+ error: { code: -32000, message: String(err instanceof Error ? err.message : err) },
136
+ });
137
+ });
138
+ }
139
+ _writeLine(obj) {
140
+ if (this._process?.stdin?.writable) {
141
+ this._process.stdin.write(JSON.stringify(obj) + "\n");
142
+ }
143
+ }
144
+ async sendAsync(request) {
145
+ this._ensureRunning();
146
+ const msgId = String(request.id ?? "");
147
+ return new Promise((resolve, reject) => {
148
+ const timer = setTimeout(() => {
149
+ this._pending.delete(msgId);
150
+ reject(new Error(`persistent transport: timeout after ${this._timeout}ms`));
151
+ }, this._timeout);
152
+ this._pending.set(msgId, {
153
+ resolve: (value) => {
154
+ clearTimeout(timer);
155
+ resolve(value);
156
+ },
157
+ reject: (error) => {
158
+ clearTimeout(timer);
159
+ reject(error);
160
+ },
161
+ });
162
+ this._writeLine(request);
163
+ });
164
+ }
165
+ stop() {
166
+ if (this._process === null)
167
+ return;
168
+ try {
169
+ if (this._process.stdin) {
170
+ this._process.stdin.end();
171
+ }
172
+ this._process.kill();
173
+ }
174
+ catch {
175
+ // Ignore cleanup errors
176
+ }
177
+ finally {
178
+ this._process = null;
179
+ }
180
+ }
181
+ isRunning() {
182
+ return this._process !== null && this._process.exitCode === null;
183
+ }
184
+ _ensureRunning() {
185
+ if (!this.isRunning()) {
186
+ this.start();
187
+ }
188
+ }
189
+ }
190
+ exports.PersistentTransport = PersistentTransport;
191
+ // -- Per-call transport factories -----------------------------------------
192
+ /**
193
+ * Create a synchronous transport that spawns the gateway binary per call.
194
+ */
195
+ function createGatewaySyncTransport(gatewayBin) {
196
+ return (request) => {
197
+ const requestJson = JSON.stringify(request);
198
+ const out = (0, node_child_process_1.spawnSync)(gatewayBin, [], {
199
+ env: { ...process.env, MOBKIT_RPC_REQUEST: requestJson },
200
+ encoding: "utf8",
201
+ });
202
+ if (out.status !== 0) {
203
+ throw new Error(`gateway failed (status=${out.status}): ${String(out.stderr ?? "")}`);
204
+ }
205
+ try {
206
+ return JSON.parse(String(out.stdout ?? ""));
207
+ }
208
+ catch {
209
+ throw new Error("gateway returned non-JSON response");
210
+ }
211
+ };
212
+ }
213
+ /**
214
+ * Create an async transport that spawns the gateway binary per call.
215
+ */
216
+ function createGatewayAsyncTransport(gatewayBin) {
217
+ return async (request) => new Promise((resolve, reject) => {
218
+ const requestJson = JSON.stringify(request);
219
+ const child = (0, node_child_process_1.spawn)(gatewayBin, [], {
220
+ env: { ...process.env, MOBKIT_RPC_REQUEST: requestJson },
221
+ stdio: ["ignore", "pipe", "pipe"],
222
+ });
223
+ let stdout = "";
224
+ let stderr = "";
225
+ if (child.stdout) {
226
+ child.stdout.setEncoding("utf8");
227
+ child.stdout.on("data", (chunk) => {
228
+ stdout += chunk;
229
+ });
230
+ }
231
+ if (child.stderr) {
232
+ child.stderr.setEncoding("utf8");
233
+ child.stderr.on("data", (chunk) => {
234
+ stderr += chunk;
235
+ });
236
+ }
237
+ child.on("error", (error) => reject(error));
238
+ child.on("close", (code) => {
239
+ if (code !== 0) {
240
+ reject(new Error(`gateway failed (status=${code}): ${stderr}`));
241
+ return;
242
+ }
243
+ try {
244
+ resolve(JSON.parse(stdout));
245
+ }
246
+ catch {
247
+ reject(new Error("gateway returned non-JSON response"));
248
+ }
249
+ });
250
+ });
251
+ }
252
+ /**
253
+ * Create an async HTTP POST transport.
254
+ */
255
+ function createJsonRpcHttpTransport(endpoint, options = {}) {
256
+ const globalFetch = globalThis.fetch;
257
+ const fetchImpl = options.fetchImpl ?? globalFetch;
258
+ if (!fetchImpl) {
259
+ throw new Error("fetch implementation not available");
260
+ }
261
+ return async (request) => {
262
+ const response = await fetchImpl(endpoint, {
263
+ method: "POST",
264
+ headers: {
265
+ "content-type": "application/json",
266
+ accept: "application/json",
267
+ ...(options.headers ?? {}),
268
+ },
269
+ body: JSON.stringify(request),
270
+ });
271
+ const body = await response.text();
272
+ if (!response.ok) {
273
+ throw new Error(`http transport failed (status=${response.status}): ${body}`);
274
+ }
275
+ try {
276
+ return JSON.parse(body);
277
+ }
278
+ catch {
279
+ throw new Error("http transport returned non-JSON response");
280
+ }
281
+ };
282
+ }
@@ -0,0 +1,376 @@
1
+ "use strict";
2
+ /**
3
+ * Typed return models for MobKit SDK RPC methods.
4
+ *
5
+ * All interfaces use `readonly` fields with camelCase naming. Parse functions
6
+ * convert from the wire protocol's snake_case representation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ErrorCategory = exports.MEMBER_STATE_RETIRING = exports.MEMBER_STATE_ACTIVE = void 0;
10
+ exports.parseStatusResult = parseStatusResult;
11
+ exports.parseCapabilitiesResult = parseCapabilitiesResult;
12
+ exports.parseReconcileResult = parseReconcileResult;
13
+ exports.parseSpawnResult = parseSpawnResult;
14
+ exports.parseKeepAliveConfig = parseKeepAliveConfig;
15
+ exports.parseEventEnvelope = parseEventEnvelope;
16
+ exports.parseSubscribeResult = parseSubscribeResult;
17
+ exports.parseSendMessageResult = parseSendMessageResult;
18
+ exports.parseRoutingResolution = parseRoutingResolution;
19
+ exports.parseDeliveryResult = parseDeliveryResult;
20
+ exports.parseDeliveryHistoryResult = parseDeliveryHistoryResult;
21
+ exports.parseMemoryQueryResult = parseMemoryQueryResult;
22
+ exports.parseMemoryStoreInfo = parseMemoryStoreInfo;
23
+ exports.parseMemoryIndexResult = parseMemoryIndexResult;
24
+ exports.parseCallToolResult = parseCallToolResult;
25
+ exports.parseMemberSnapshot = parseMemberSnapshot;
26
+ exports.parseRuntimeRouteResult = parseRuntimeRouteResult;
27
+ exports.parseGatingEvaluateResult = parseGatingEvaluateResult;
28
+ exports.parseGatingDecisionResult = parseGatingDecisionResult;
29
+ exports.parseGatingAuditEntry = parseGatingAuditEntry;
30
+ exports.parseGatingPendingEntry = parseGatingPendingEntry;
31
+ exports.parseReconcileEdgesReport = parseReconcileEdgesReport;
32
+ exports.parseRediscoverReport = parseRediscoverReport;
33
+ exports.parsePersistedEvent = parsePersistedEvent;
34
+ exports.eventQueryToDict = eventQueryToDict;
35
+ exports.parseErrorEvent = parseErrorEvent;
36
+ // -- Helpers (internal) ---------------------------------------------------
37
+ function asRecord(value) {
38
+ if (typeof value === "object" && value !== null) {
39
+ return value;
40
+ }
41
+ return {};
42
+ }
43
+ function asStringArray(value) {
44
+ if (Array.isArray(value)) {
45
+ return value.filter((v) => typeof v === "string");
46
+ }
47
+ return [];
48
+ }
49
+ function asRecordArray(value) {
50
+ if (Array.isArray(value)) {
51
+ return value.filter((v) => typeof v === "object" && v !== null);
52
+ }
53
+ return [];
54
+ }
55
+ function asStringRecord(value) {
56
+ const raw = asRecord(value);
57
+ const result = {};
58
+ for (const [k, v] of Object.entries(raw)) {
59
+ if (typeof v === "string") {
60
+ result[k] = v;
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ // -- Constants ------------------------------------------------------------
66
+ exports.MEMBER_STATE_ACTIVE = "active";
67
+ exports.MEMBER_STATE_RETIRING = "retiring";
68
+ function parseStatusResult(raw) {
69
+ const d = asRecord(raw);
70
+ return {
71
+ contractVersion: String(d.contract_version ?? ""),
72
+ running: Boolean(d.running),
73
+ loadedModules: asStringArray(d.loaded_modules),
74
+ };
75
+ }
76
+ function parseCapabilitiesResult(raw) {
77
+ const d = asRecord(raw);
78
+ return {
79
+ contractVersion: String(d.contract_version ?? ""),
80
+ methods: asStringArray(d.methods),
81
+ loadedModules: asStringArray(d.loaded_modules),
82
+ };
83
+ }
84
+ function parseReconcileResult(raw) {
85
+ const d = asRecord(raw);
86
+ return {
87
+ accepted: Boolean(d.accepted),
88
+ reconciledModules: asStringArray(d.reconciled_modules),
89
+ added: Number(d.added ?? 0),
90
+ };
91
+ }
92
+ function parseSpawnResult(raw) {
93
+ const d = asRecord(raw);
94
+ return {
95
+ accepted: Boolean(d.accepted),
96
+ moduleId: String(d.module_id ?? ""),
97
+ meerkatId: typeof d.meerkat_id === "string" ? d.meerkat_id : null,
98
+ profile: typeof d.profile === "string" ? d.profile : null,
99
+ };
100
+ }
101
+ function parseKeepAliveConfig(raw) {
102
+ const d = asRecord(raw);
103
+ return {
104
+ intervalMs: Number(d.interval_ms ?? 0),
105
+ event: String(d.event ?? ""),
106
+ };
107
+ }
108
+ function parseEventEnvelope(raw) {
109
+ const d = asRecord(raw);
110
+ return {
111
+ eventId: String(d.event_id ?? ""),
112
+ source: String(d.source ?? ""),
113
+ timestampMs: Number(d.timestamp_ms ?? 0),
114
+ event: d.event,
115
+ };
116
+ }
117
+ function parseSubscribeResult(raw) {
118
+ const d = asRecord(raw);
119
+ const eventsRaw = Array.isArray(d.events) ? d.events : [];
120
+ return {
121
+ scope: String(d.scope ?? ""),
122
+ replayFromEventId: typeof d.replay_from_event_id === "string"
123
+ ? d.replay_from_event_id
124
+ : null,
125
+ keepAlive: parseKeepAliveConfig(d.keep_alive),
126
+ keepAliveComment: String(d.keep_alive_comment ?? ""),
127
+ eventFrames: asStringArray(d.event_frames),
128
+ events: eventsRaw.map(parseEventEnvelope),
129
+ };
130
+ }
131
+ function parseSendMessageResult(raw) {
132
+ const d = asRecord(raw);
133
+ return {
134
+ accepted: Boolean(d.accepted),
135
+ memberId: String(d.member_id ?? ""),
136
+ sessionId: String(d.session_id ?? ""),
137
+ };
138
+ }
139
+ function parseRoutingResolution(raw) {
140
+ const d = asRecord(raw);
141
+ return {
142
+ recipient: String(d.recipient ?? ""),
143
+ route: asRecord(d.route ?? d),
144
+ };
145
+ }
146
+ function parseDeliveryResult(raw) {
147
+ const d = asRecord(raw);
148
+ return {
149
+ delivered: Boolean(d.delivered),
150
+ deliveryId: String(d.delivery_id ?? ""),
151
+ };
152
+ }
153
+ function parseDeliveryHistoryResult(raw) {
154
+ const d = asRecord(raw);
155
+ return {
156
+ deliveries: asRecordArray(d.deliveries),
157
+ };
158
+ }
159
+ function parseMemoryQueryResult(raw) {
160
+ const d = asRecord(raw);
161
+ return {
162
+ results: asRecordArray(d.results),
163
+ };
164
+ }
165
+ function parseMemoryStoreInfo(raw) {
166
+ const d = asRecord(raw);
167
+ return {
168
+ store: String(d.store ?? ""),
169
+ recordCount: Number(d.record_count ?? 0),
170
+ };
171
+ }
172
+ function parseMemoryIndexResult(raw) {
173
+ const d = asRecord(raw);
174
+ return {
175
+ entity: String(d.entity ?? ""),
176
+ topic: String(d.topic ?? ""),
177
+ store: String(d.store ?? ""),
178
+ assertionId: typeof d.assertion_id === "string" ? d.assertion_id : null,
179
+ };
180
+ }
181
+ function parseCallToolResult(raw) {
182
+ const d = asRecord(raw);
183
+ return {
184
+ moduleId: String(d.module_id ?? ""),
185
+ tool: String(d.tool ?? ""),
186
+ result: d.result,
187
+ };
188
+ }
189
+ function parseMemberSnapshot(raw) {
190
+ const d = asRecord(raw);
191
+ return {
192
+ meerkatId: String(d.meerkat_id ?? ""),
193
+ profile: String(d.profile ?? ""),
194
+ state: String(d.state ?? ""),
195
+ wiredTo: asStringArray(d.wired_to),
196
+ labels: asStringRecord(d.labels),
197
+ };
198
+ }
199
+ function parseRuntimeRouteResult(raw) {
200
+ const d = asRecord(raw);
201
+ return {
202
+ routeKey: String(d.route_key ?? ""),
203
+ recipient: String(d.recipient ?? ""),
204
+ channel: typeof d.channel === "string" ? d.channel : null,
205
+ sink: String(d.sink ?? ""),
206
+ targetModule: String(d.target_module ?? ""),
207
+ };
208
+ }
209
+ function parseGatingEvaluateResult(raw) {
210
+ const d = asRecord(raw);
211
+ return {
212
+ actionId: String(d.action_id ?? ""),
213
+ action: String(d.action ?? ""),
214
+ actorId: String(d.actor_id ?? ""),
215
+ riskTier: typeof d.risk_tier === "string" ? d.risk_tier : null,
216
+ outcome: String(d.outcome ?? ""),
217
+ pendingId: typeof d.pending_id === "string" ? d.pending_id : null,
218
+ };
219
+ }
220
+ function parseGatingDecisionResult(raw) {
221
+ const d = asRecord(raw);
222
+ return {
223
+ pendingId: String(d.pending_id ?? ""),
224
+ actionId: String(d.action_id ?? ""),
225
+ decision: String(d.decision ?? ""),
226
+ };
227
+ }
228
+ function parseGatingAuditEntry(raw) {
229
+ const d = asRecord(raw);
230
+ return {
231
+ auditId: String(d.audit_id ?? ""),
232
+ timestampMs: Number(d.timestamp_ms ?? 0),
233
+ eventType: String(d.event_type ?? ""),
234
+ actionId: String(d.action_id ?? ""),
235
+ actorId: String(d.actor_id ?? ""),
236
+ riskTier: typeof d.risk_tier === "string" ? d.risk_tier : null,
237
+ outcome: String(d.outcome ?? ""),
238
+ };
239
+ }
240
+ function parseGatingPendingEntry(raw) {
241
+ const d = asRecord(raw);
242
+ return {
243
+ pendingId: String(d.pending_id ?? ""),
244
+ actionId: String(d.action_id ?? ""),
245
+ action: String(d.action ?? ""),
246
+ actorId: String(d.actor_id ?? ""),
247
+ riskTier: typeof d.risk_tier === "string" ? d.risk_tier : null,
248
+ createdAtMs: Number(d.created_at_ms ?? 0),
249
+ };
250
+ }
251
+ function parseReconcileEdgesReport(raw) {
252
+ const d = asRecord(raw);
253
+ const failures = asRecordArray(d.failures);
254
+ const skipped = asRecordArray(d.skipped_missing_members);
255
+ return {
256
+ desiredEdges: asRecordArray(d.desired_edges),
257
+ wiredEdges: asRecordArray(d.wired_edges),
258
+ unwiredEdges: asRecordArray(d.unwired_edges),
259
+ retainedEdges: asRecordArray(d.retained_edges),
260
+ preexistingEdges: asRecordArray(d.preexisting_edges),
261
+ skippedMissingMembers: skipped,
262
+ prunedStaleManagedEdges: asRecordArray(d.pruned_stale_managed_edges),
263
+ failures,
264
+ isComplete: failures.length === 0 && skipped.length === 0,
265
+ };
266
+ }
267
+ function parseRediscoverReport(raw) {
268
+ const d = asRecord(raw);
269
+ return {
270
+ spawned: asStringArray(d.spawned),
271
+ edges: parseReconcileEdgesReport(d.edges),
272
+ };
273
+ }
274
+ function parseUnifiedEvent(raw) {
275
+ const d = asRecord(raw);
276
+ if ("Agent" in d) {
277
+ const agent = asRecord(d.Agent);
278
+ return {
279
+ kind: "agent",
280
+ agentId: String(agent.agent_id ?? ""),
281
+ eventType: String(agent.event_type ?? ""),
282
+ };
283
+ }
284
+ if ("Module" in d) {
285
+ const mod = asRecord(d.Module);
286
+ return {
287
+ kind: "module",
288
+ module: String(mod.module ?? ""),
289
+ eventType: String(mod.event_type ?? ""),
290
+ payload: asRecord(mod.payload),
291
+ };
292
+ }
293
+ return {
294
+ kind: "module",
295
+ module: "unknown",
296
+ eventType: "unknown",
297
+ payload: asRecord(raw),
298
+ };
299
+ }
300
+ function parsePersistedEvent(raw) {
301
+ const d = asRecord(raw);
302
+ const rawEvent = d.event;
303
+ const event = typeof rawEvent === "object" && rawEvent !== null
304
+ ? parseUnifiedEvent(rawEvent)
305
+ : { kind: "module", module: "unknown", eventType: "unknown", payload: {} };
306
+ return {
307
+ id: String(d.id ?? ""),
308
+ seq: Number(d.seq ?? 0),
309
+ timestampMs: Number(d.timestamp_ms ?? 0),
310
+ memberId: typeof d.member_id === "string" ? d.member_id : null,
311
+ event,
312
+ };
313
+ }
314
+ function eventQueryToDict(query) {
315
+ const d = {};
316
+ if (query.sinceMs !== undefined)
317
+ d.since_ms = query.sinceMs;
318
+ if (query.untilMs !== undefined)
319
+ d.until_ms = query.untilMs;
320
+ if (query.memberId !== undefined)
321
+ d.member_id = query.memberId;
322
+ if (query.eventTypes !== undefined && query.eventTypes.length > 0) {
323
+ d.event_types = [...query.eventTypes];
324
+ }
325
+ if (query.limit !== undefined)
326
+ d.limit = query.limit;
327
+ if (query.afterSeq !== undefined)
328
+ d.after_seq = query.afterSeq;
329
+ return d;
330
+ }
331
+ // -- ErrorCategory / ErrorEvent -------------------------------------------
332
+ exports.ErrorCategory = {
333
+ SPAWN_FAILURE: "spawn_failure",
334
+ RECONCILE_INCOMPLETE: "reconcile_incomplete",
335
+ CHECKPOINT_FAILURE: "checkpoint_failure",
336
+ HOST_LOOP_CRASH: "host_loop_crash",
337
+ REDISCOVER_FAILURE: "rediscover_failure",
338
+ };
339
+ function parseErrorEvent(raw) {
340
+ const d = asRecord(raw);
341
+ const category = String(d.category ?? "unknown");
342
+ const context = {};
343
+ for (const [k, v] of Object.entries(d)) {
344
+ if (k !== "category")
345
+ context[k] = v;
346
+ }
347
+ const error = String(context.error ?? "");
348
+ const memberId = String(context.member_id ?? "");
349
+ let message;
350
+ switch (category) {
351
+ case exports.ErrorCategory.SPAWN_FAILURE:
352
+ message = memberId ? `${memberId}: ${error}` : error;
353
+ break;
354
+ case exports.ErrorCategory.RECONCILE_INCOMPLETE: {
355
+ const failures = Number(context.failures ?? 0);
356
+ const skipped = Number(context.skipped ?? 0);
357
+ message = `${failures} failures, ${skipped} skipped`;
358
+ break;
359
+ }
360
+ case exports.ErrorCategory.CHECKPOINT_FAILURE: {
361
+ const sessionId = String(context.session_id ?? "");
362
+ message = sessionId ? `${sessionId}: ${error}` : error;
363
+ break;
364
+ }
365
+ case exports.ErrorCategory.HOST_LOOP_CRASH:
366
+ message = memberId ? `${memberId}: ${error}` : error;
367
+ break;
368
+ case exports.ErrorCategory.REDISCOVER_FAILURE:
369
+ message = error;
370
+ break;
371
+ default:
372
+ message = JSON.stringify(d);
373
+ break;
374
+ }
375
+ return { category, message, context };
376
+ }