@rkat/sdk 0.6.33 → 0.7.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/README.md +8 -8
- package/dist/client.d.ts +87 -46
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +473 -551
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +7 -10
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +35 -17
- package/dist/events.js.map +1 -1
- package/dist/generated/events.d.ts +5 -0
- package/dist/generated/events.d.ts.map +1 -0
- package/dist/generated/events.js +48 -0
- package/dist/generated/events.js.map +1 -0
- package/dist/generated/index.d.ts +2 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +2 -0
- package/dist/generated/index.js.map +1 -1
- package/dist/generated/types.d.ts +328 -55
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/generated/types.js +354 -2
- package/dist/generated/types.js.map +1 -1
- package/dist/generated/version_compat.d.ts +6 -0
- package/dist/generated/version_compat.d.ts.map +1 -0
- package/dist/generated/version_compat.js +24 -0
- package/dist/generated/version_compat.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/live.d.ts +3 -3
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +2 -4
- package/dist/live.js.map +1 -1
- package/dist/subscription.d.ts +1 -0
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +19 -3
- package/dist/subscription.js.map +1 -1
- package/dist/types.d.ts +34 -171
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -33,12 +33,14 @@ import { setTimeout as delay } from "node:timers/promises";
|
|
|
33
33
|
import { createInterface } from "node:readline";
|
|
34
34
|
import { Buffer } from "node:buffer";
|
|
35
35
|
import { MeerkatError, CapabilityUnavailableError } from "./generated/errors.js";
|
|
36
|
+
import { isCompatibleWith } from "./generated/version_compat.js";
|
|
36
37
|
import { CONTRACT_VERSION, } from "./generated/types.js";
|
|
37
38
|
import { DeferredSession, Session } from "./session.js";
|
|
38
39
|
import { Mob, } from "./mob.js";
|
|
39
40
|
import { parseCoreEvent } from "./events.js";
|
|
40
41
|
import { EventStream, AsyncQueue } from "./streaming.js";
|
|
41
42
|
import { EventSubscription } from "./subscription.js";
|
|
43
|
+
import { parseAttentionListResult, parseGoalStatusResult, parseWorkGraphEvent, parseWorkGraphSnapshot, parseWorkItem, } from "./generated/types.js";
|
|
42
44
|
const MEERKAT_REPO = "lukacf/meerkat";
|
|
43
45
|
const MEERKAT_RELEASE_BINARY = "rkat-rpc";
|
|
44
46
|
const MEERKAT_BINARY_CACHE_ROOT = path.join(os.homedir(), ".cache", "meerkat", "bin", MEERKAT_RELEASE_BINARY);
|
|
@@ -78,26 +80,6 @@ const MOB_SPAWN_MANY_FAILURE_CAUSES = new Set([
|
|
|
78
80
|
"work_not_found",
|
|
79
81
|
"internal",
|
|
80
82
|
]);
|
|
81
|
-
const WORK_ATTENTION_DELEGATED_AUTHORITIES = new Set([
|
|
82
|
-
"add_evidence",
|
|
83
|
-
"close_own_review_item",
|
|
84
|
-
"request_closure",
|
|
85
|
-
"close_if_policy_allows",
|
|
86
|
-
]);
|
|
87
|
-
const WORK_ATTENTION_MODES = new Set([
|
|
88
|
-
"pursue",
|
|
89
|
-
"coordinate",
|
|
90
|
-
"review",
|
|
91
|
-
"falsify",
|
|
92
|
-
"judge",
|
|
93
|
-
"observe",
|
|
94
|
-
]);
|
|
95
|
-
const WORK_ATTENTION_STATES = new Set([
|
|
96
|
-
"active",
|
|
97
|
-
"paused",
|
|
98
|
-
"superseded",
|
|
99
|
-
"stopped",
|
|
100
|
-
]);
|
|
101
83
|
function isMobSpawnManyFailureCause(value) {
|
|
102
84
|
return typeof value === "string" && MOB_SPAWN_MANY_FAILURE_CAUSES.has(value);
|
|
103
85
|
}
|
|
@@ -191,9 +173,7 @@ function mobTurnStartPayload(mobId, agentIdentity, prompt, options) {
|
|
|
191
173
|
setIfDefined(payload, "output_schema", options?.outputSchema);
|
|
192
174
|
setIfDefined(payload, "structured_output_retries", options?.structuredOutputRetries);
|
|
193
175
|
setIfDefined(payload, "provider_params", options?.providerParams);
|
|
194
|
-
setIfDefined(payload, "clear_provider_params", options?.clearProviderParams);
|
|
195
176
|
setIfDefined(payload, "auth_binding", options?.authBinding);
|
|
196
|
-
setIfDefined(payload, "clear_auth_binding", options?.clearAuthBinding);
|
|
197
177
|
return payload;
|
|
198
178
|
}
|
|
199
179
|
function normalizeMobWireMembersBatchEdge(edge) {
|
|
@@ -206,6 +186,11 @@ function normalizeMobWireMembersBatchEdge(edge) {
|
|
|
206
186
|
}
|
|
207
187
|
export class MeerkatClient {
|
|
208
188
|
process = null;
|
|
189
|
+
// Permanent transport-failed state: once the JSONL framing is untrustworthy
|
|
190
|
+
// (corrupted frame), every subsequent call must reject with this recorded
|
|
191
|
+
// typed fault instead of writing into the condemned stream. Cleared only by
|
|
192
|
+
// a fresh connect() (new process, new stream).
|
|
193
|
+
transportFault = null;
|
|
209
194
|
processStderr = "";
|
|
210
195
|
requestId = 0;
|
|
211
196
|
_capabilities = [];
|
|
@@ -214,14 +199,28 @@ export class MeerkatClient {
|
|
|
214
199
|
pendingRequests = new Map();
|
|
215
200
|
eventQueues = new Map();
|
|
216
201
|
streamQueues = new Map();
|
|
217
|
-
|
|
218
|
-
|
|
202
|
+
// Per-request_id stream subscriptions for createSessionStreaming calls whose
|
|
203
|
+
// session_id is not yet bound. Keyed by the JSON-RPC request id so concurrent
|
|
204
|
+
// creates are admitted and correlated independently instead of colliding on a
|
|
205
|
+
// single client-local slot (which previously threw INVALID_STATE before the
|
|
206
|
+
// server had any chance to admit or reject the second create).
|
|
207
|
+
pendingStreamQueues = new Map();
|
|
208
|
+
// Pre-binding events keyed by session_id. session_id is globally unique per
|
|
209
|
+
// session, so buffering by session_id correlates correctly even when several
|
|
210
|
+
// streams are pending: each event lands under the session it names, and the
|
|
211
|
+
// response that binds that session_id drains exactly its own buffer.
|
|
219
212
|
unmatchedStreamBuffer = new Map();
|
|
220
213
|
unmatchedStandaloneStreamBuffer = new Map();
|
|
221
214
|
unmatchedStandaloneStreamEnd = new Map();
|
|
222
215
|
streamTerminalOutcomes = new Map();
|
|
223
216
|
rl = null;
|
|
224
217
|
toolHandlers = new Map();
|
|
218
|
+
// Typed faults from post-connect tool registration. `registerTool` is sync and
|
|
219
|
+
// the server round-trip is detached, so a genuine server rejection cannot be
|
|
220
|
+
// surfaced to the synchronous caller. Recording it here makes the
|
|
221
|
+
// silent-never-registers failure programmatically distinguishable instead of
|
|
222
|
+
// being swallowed by a best-effort `.catch(() => {})`.
|
|
223
|
+
toolRegistrationErrors = new Map();
|
|
225
224
|
constructor(rkatPath = "rkat-rpc") {
|
|
226
225
|
this.rkatPath = rkatPath;
|
|
227
226
|
}
|
|
@@ -234,17 +233,77 @@ export class MeerkatClient {
|
|
|
234
233
|
* return `Results for ${args.q}`;
|
|
235
234
|
* });
|
|
236
235
|
* ```
|
|
236
|
+
*
|
|
237
|
+
* Handlers may return a plain string or a `ContentBlock[]` for multimodal
|
|
238
|
+
* tool results. Block arrays are forwarded faithfully to the runtime as
|
|
239
|
+
* `WireToolResultContent` (no string coercion).
|
|
237
240
|
*/
|
|
238
241
|
registerTool(name, description, inputSchema, handler) {
|
|
239
242
|
this.toolHandlers.set(name, { description, inputSchema, handler });
|
|
243
|
+
// A fresh declaration supersedes any stale fault from a prior failed attempt.
|
|
244
|
+
this.toolRegistrationErrors.delete(name);
|
|
240
245
|
// If already connected, register the new tool with the server immediately.
|
|
241
246
|
if (this.process?.stdin) {
|
|
242
|
-
this.
|
|
247
|
+
void this.registerToolWithServer(name, description, inputSchema);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Send a single post-connect tool registration and resolve its outcome
|
|
252
|
+
* without swallowing genuine faults.
|
|
253
|
+
*
|
|
254
|
+
* Benign disconnect/shutdown faults are tolerated: the local
|
|
255
|
+
* `toolHandlers` registry still holds the definition and the next
|
|
256
|
+
* `connect()` re-sends it, so the tool is not lost. A genuine server
|
|
257
|
+
* rejection or protocol fault is a real failure that cannot reach the
|
|
258
|
+
* synchronous `registerTool` caller, so we record it as an inspectable
|
|
259
|
+
* typed fault (queryable via {@link toolRegistrationError}), marking the
|
|
260
|
+
* tool unconfirmed. This replaces the previous `.catch(() => {})` that
|
|
261
|
+
* laundered every failure into silence. The recorded fault is the
|
|
262
|
+
* propagation channel: re-throwing on this detached promise would surface
|
|
263
|
+
* only as an unhandled rejection (the caller is sync), so the typed record
|
|
264
|
+
* is the observable signal instead.
|
|
265
|
+
*/
|
|
266
|
+
async registerToolWithServer(name, description, inputSchema) {
|
|
267
|
+
try {
|
|
268
|
+
await this.request("tools/register", {
|
|
243
269
|
tools: [{ name, description, input_schema: inputSchema }],
|
|
244
|
-
}).catch(() => {
|
|
245
|
-
// Best-effort: tool registration may fail if connection is closing.
|
|
246
270
|
});
|
|
247
271
|
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
const fault = error instanceof MeerkatError
|
|
274
|
+
? error
|
|
275
|
+
: new MeerkatError("TOOL_REGISTRATION_FAILED", `Tool ${name} registration failed: ${String(error)}`);
|
|
276
|
+
// Clean shutdown / disconnect is genuinely benign — the registry already
|
|
277
|
+
// holds the definition and a future connect() will re-send it.
|
|
278
|
+
const BENIGN_CODES = new Set([
|
|
279
|
+
"CLIENT_CLOSED",
|
|
280
|
+
"CONNECTION_CLOSED",
|
|
281
|
+
"NOT_CONNECTED",
|
|
282
|
+
"PROCESS_EXITED",
|
|
283
|
+
"PROCESS_ERROR",
|
|
284
|
+
]);
|
|
285
|
+
if (BENIGN_CODES.has(fault.code)) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
// Genuine server rejection / protocol fault: record the typed fault as
|
|
289
|
+
// caller-inspectable state so the tool is programmatically known to be
|
|
290
|
+
// unconfirmed. The synchronous `registerTool` caller cannot observe a
|
|
291
|
+
// rejection on this detached promise, so the recorded fault — not a
|
|
292
|
+
// re-throw — is the surfaced signal.
|
|
293
|
+
this.toolRegistrationErrors.set(name, fault);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// Success: a prior failed attempt's fault no longer applies.
|
|
297
|
+
this.toolRegistrationErrors.delete(name);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Return the typed fault recorded for a tool whose post-connect registration
|
|
301
|
+
* was rejected by the server, or `undefined` if the tool's most recent
|
|
302
|
+
* registration attempt succeeded (or has not completed yet). Lets callers
|
|
303
|
+
* distinguish a tool that silently never registered from one that is live.
|
|
304
|
+
*/
|
|
305
|
+
toolRegistrationError(name) {
|
|
306
|
+
return this.toolRegistrationErrors.get(name);
|
|
248
307
|
}
|
|
249
308
|
// -- Connection ---------------------------------------------------------
|
|
250
309
|
async connect(options) {
|
|
@@ -269,6 +328,8 @@ export class MeerkatClient {
|
|
|
269
328
|
this.process = spawn(this.rkatPath, args, {
|
|
270
329
|
stdio: ["pipe", "pipe", "pipe"],
|
|
271
330
|
});
|
|
331
|
+
// Fresh process, fresh stream: clear any recorded transport fault.
|
|
332
|
+
this.transportFault = null;
|
|
272
333
|
const child = this.process;
|
|
273
334
|
this.processStderr = "";
|
|
274
335
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -296,8 +357,9 @@ export class MeerkatClient {
|
|
|
296
357
|
});
|
|
297
358
|
this.rl = createInterface({ input: this.process.stdout });
|
|
298
359
|
this.rl.on("line", (line) => this.handleLine(line));
|
|
299
|
-
// Handshake
|
|
300
|
-
|
|
360
|
+
// Handshake — `initialize` returns the generated `ServerCapabilities`
|
|
361
|
+
// contract; fields are validated below.
|
|
362
|
+
const initResult = (await this.request("initialize", {}));
|
|
301
363
|
const serverVersion = String(initResult.contract_version ?? "");
|
|
302
364
|
if (!MeerkatClient.checkVersionCompatible(serverVersion, CONTRACT_VERSION)) {
|
|
303
365
|
throw new MeerkatError("VERSION_MISMATCH", `Server version ${serverVersion} incompatible with SDK ${CONTRACT_VERSION}`);
|
|
@@ -305,14 +367,12 @@ export class MeerkatClient {
|
|
|
305
367
|
this._methods = new Set(Array.isArray(initResult.methods)
|
|
306
368
|
? initResult.methods.map((method) => String(method))
|
|
307
369
|
: []);
|
|
308
|
-
// Fetch capabilities
|
|
309
|
-
const capsResult = await this.request("capabilities/get", {});
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
status: MeerkatClient.normalizeStatus(cap.status),
|
|
315
|
-
}));
|
|
370
|
+
// Fetch capabilities — generated `CapabilitiesResponse` contract.
|
|
371
|
+
const capsResult = (await this.request("capabilities/get", {}));
|
|
372
|
+
if (!Array.isArray(capsResult.capabilities)) {
|
|
373
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid capabilities/get response: capabilities must be a list");
|
|
374
|
+
}
|
|
375
|
+
this._capabilities = capsResult.capabilities.map((cap) => MeerkatClient.parseWireCapabilityEntry(cap));
|
|
316
376
|
// Register callback tools if any were declared.
|
|
317
377
|
if (this.toolHandlers.size > 0) {
|
|
318
378
|
const tools = Array.from(this.toolHandlers.entries()).map(([name, { description, inputSchema }]) => ({
|
|
@@ -321,6 +381,8 @@ export class MeerkatClient {
|
|
|
321
381
|
input_schema: inputSchema,
|
|
322
382
|
}));
|
|
323
383
|
await this.request("tools/register", { tools });
|
|
384
|
+
// The bulk re-send confirmed every declared tool; drop stale per-tool faults.
|
|
385
|
+
this.toolRegistrationErrors.clear();
|
|
324
386
|
}
|
|
325
387
|
return this;
|
|
326
388
|
}
|
|
@@ -354,6 +416,35 @@ export class MeerkatClient {
|
|
|
354
416
|
}
|
|
355
417
|
this.pendingRequests.clear();
|
|
356
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Surface a typed transport/protocol fault to every pending caller and
|
|
421
|
+
* stream consumer. Used when the read loop encounters a corrupted frame and
|
|
422
|
+
* the stream can no longer be trusted, so callers fail closed instead of
|
|
423
|
+
* hanging on a dropped response.
|
|
424
|
+
*/
|
|
425
|
+
failTransport(reason) {
|
|
426
|
+
if (!this.transportFault) {
|
|
427
|
+
this.transportFault =
|
|
428
|
+
reason instanceof MeerkatError
|
|
429
|
+
? reason
|
|
430
|
+
: new MeerkatError("PROTOCOL_ERROR", String(reason));
|
|
431
|
+
}
|
|
432
|
+
this.rejectPendingRequests(reason);
|
|
433
|
+
this.closeQueues();
|
|
434
|
+
// Fail closed: the stream is condemned, so the process goes with it —
|
|
435
|
+
// leaving it alive would let later calls write into framing we just
|
|
436
|
+
// classified as untrustworthy.
|
|
437
|
+
if (this.rl) {
|
|
438
|
+
this.rl.close();
|
|
439
|
+
this.rl = null;
|
|
440
|
+
}
|
|
441
|
+
const process = this.process;
|
|
442
|
+
this.process = null;
|
|
443
|
+
if (process) {
|
|
444
|
+
process.stdin?.destroy();
|
|
445
|
+
process.kill();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
357
448
|
closeQueues() {
|
|
358
449
|
for (const [, queue] of this.eventQueues) {
|
|
359
450
|
queue.put(null);
|
|
@@ -364,12 +455,11 @@ export class MeerkatClient {
|
|
|
364
455
|
}
|
|
365
456
|
this.streamQueues.clear();
|
|
366
457
|
this.streamTerminalOutcomes.clear();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.pendingStreamQueue = null;
|
|
370
|
-
this.pendingStreamRequestId = null;
|
|
371
|
-
this.unmatchedStreamBuffer.clear();
|
|
458
|
+
for (const [, queue] of this.pendingStreamQueues) {
|
|
459
|
+
queue.put(null);
|
|
372
460
|
}
|
|
461
|
+
this.pendingStreamQueues.clear();
|
|
462
|
+
this.unmatchedStreamBuffer.clear();
|
|
373
463
|
}
|
|
374
464
|
// -- Session lifecycle --------------------------------------------------
|
|
375
465
|
/**
|
|
@@ -401,17 +491,19 @@ export class MeerkatClient {
|
|
|
401
491
|
const params = MeerkatClient.buildCreateParams(prompt, options);
|
|
402
492
|
this.requestId++;
|
|
403
493
|
const requestId = this.requestId;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
494
|
+
// Per-request_id subscription: concurrent createSessionStreaming calls each
|
|
495
|
+
// get their own pending queue keyed by request id, so a second create is no
|
|
496
|
+
// longer rejected by a client-local singleton invariant. Admission/rejection
|
|
497
|
+
// is left to the server.
|
|
407
498
|
const queue = new AsyncQueue();
|
|
408
|
-
this.
|
|
409
|
-
this.pendingStreamRequestId = requestId;
|
|
499
|
+
this.pendingStreamQueues.set(requestId, queue);
|
|
410
500
|
const responsePromise = this.registerRequest(requestId);
|
|
411
|
-
// When response arrives, bind
|
|
501
|
+
// When response arrives, bind this request's queue to its session_id.
|
|
412
502
|
const wrappedPromise = responsePromise.then((result) => {
|
|
413
503
|
const sid = String(result.session_id ?? "");
|
|
414
504
|
if (sid) {
|
|
505
|
+
// Drain only this session's pre-binding buffer — events are buffered by
|
|
506
|
+
// session_id, so concurrent pending streams do not cross-deliver.
|
|
415
507
|
const buffered = this.unmatchedStreamBuffer.get(sid) ?? [];
|
|
416
508
|
for (const evt of buffered) {
|
|
417
509
|
queue.put(evt);
|
|
@@ -419,10 +511,15 @@ export class MeerkatClient {
|
|
|
419
511
|
this.unmatchedStreamBuffer.delete(sid);
|
|
420
512
|
this.eventQueues.set(sid, queue);
|
|
421
513
|
}
|
|
422
|
-
this.
|
|
423
|
-
this.pendingStreamRequestId = null;
|
|
424
|
-
this.unmatchedStreamBuffer.clear();
|
|
514
|
+
this.pendingStreamQueues.delete(requestId);
|
|
425
515
|
return result;
|
|
516
|
+
}, (error) => {
|
|
517
|
+
// The server rejected this create: tear down only this request's pending
|
|
518
|
+
// subscription so its consumer fails closed, and leave any concurrent
|
|
519
|
+
// pending streams untouched.
|
|
520
|
+
this.pendingStreamQueues.delete(requestId);
|
|
521
|
+
queue.put(null);
|
|
522
|
+
throw error;
|
|
426
523
|
});
|
|
427
524
|
const rpcRequest = { jsonrpc: "2.0", id: requestId, method: "session/create", params };
|
|
428
525
|
this.process.stdin.write(JSON.stringify(rpcRequest) + "\n");
|
|
@@ -504,7 +601,10 @@ export class MeerkatClient {
|
|
|
504
601
|
return this.request("session/peer_response_terminal", params);
|
|
505
602
|
}
|
|
506
603
|
async injectContext(sessionId, text, options) {
|
|
507
|
-
const params = {
|
|
604
|
+
const params = {
|
|
605
|
+
session_id: sessionId,
|
|
606
|
+
content: { type: "text", text },
|
|
607
|
+
};
|
|
508
608
|
if (options?.source !== undefined) {
|
|
509
609
|
params.source = options.source;
|
|
510
610
|
}
|
|
@@ -615,24 +715,24 @@ export class MeerkatClient {
|
|
|
615
715
|
}
|
|
616
716
|
// -- Config -------------------------------------------------------------
|
|
617
717
|
async getConfig() {
|
|
618
|
-
|
|
619
|
-
return
|
|
718
|
+
// The wire body IS the generated `ConfigEnvelope` contract.
|
|
719
|
+
return (await this.request("config/get", {}));
|
|
620
720
|
}
|
|
621
721
|
async setConfig(config, options) {
|
|
622
722
|
const params = { config };
|
|
623
723
|
if (options?.expectedGeneration !== undefined) {
|
|
624
724
|
params.expected_generation = options.expectedGeneration;
|
|
625
725
|
}
|
|
626
|
-
|
|
627
|
-
|
|
726
|
+
// The wire body IS the generated `ConfigWriteResult` contract (envelope
|
|
727
|
+
// plus the live-propagation report).
|
|
728
|
+
return (await this.request("config/set", params));
|
|
628
729
|
}
|
|
629
730
|
async patchConfig(patch, options) {
|
|
630
731
|
const params = { patch };
|
|
631
732
|
if (options?.expectedGeneration !== undefined) {
|
|
632
733
|
params.expected_generation = options.expectedGeneration;
|
|
633
734
|
}
|
|
634
|
-
|
|
635
|
-
return MeerkatClient.parseConfigEnvelope(raw);
|
|
735
|
+
return (await this.request("config/patch", params));
|
|
636
736
|
}
|
|
637
737
|
async mcpAdd(params) {
|
|
638
738
|
const raw = await this.request("mcp/add", params);
|
|
@@ -659,7 +759,9 @@ export class MeerkatClient {
|
|
|
659
759
|
// -- Skills ---------------------------------------------------------------
|
|
660
760
|
async listSkills() {
|
|
661
761
|
const result = await this.request("skills/list", {});
|
|
662
|
-
|
|
762
|
+
MeerkatClient.requireRecordArray(result.skills, "Invalid skills/list response");
|
|
763
|
+
const envelope = result;
|
|
764
|
+
return envelope.skills;
|
|
663
765
|
}
|
|
664
766
|
async getBlob(blobId) {
|
|
665
767
|
const result = await this.request("blob/get", { blob_id: blobId });
|
|
@@ -779,43 +881,42 @@ export class MeerkatClient {
|
|
|
779
881
|
});
|
|
780
882
|
}
|
|
781
883
|
async getWorkGraphItem(itemId, options) {
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
|
|
884
|
+
const params = { id: itemId };
|
|
885
|
+
if (options?.realmId !== undefined) {
|
|
886
|
+
params.realm_id = options.realmId;
|
|
887
|
+
}
|
|
888
|
+
if (options?.namespace !== undefined) {
|
|
889
|
+
params.namespace = options.namespace;
|
|
890
|
+
}
|
|
891
|
+
const result = await this.request("workgraph/get", params);
|
|
892
|
+
return parseWorkItem(result);
|
|
787
893
|
}
|
|
788
894
|
async listWorkGraphItems(filter = {}) {
|
|
789
|
-
const result = await this.request("workgraph/list",
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
};
|
|
895
|
+
const result = await this.request("workgraph/list", filter);
|
|
896
|
+
const items = MeerkatClient.requireRecordArray(result.items, "Invalid workgraph item list").map((entry) => parseWorkItem(entry));
|
|
897
|
+
return { items };
|
|
793
898
|
}
|
|
794
899
|
async listReadyWorkGraphItems(filter = {}) {
|
|
795
|
-
const result = await this.request("workgraph/ready",
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
};
|
|
900
|
+
const result = await this.request("workgraph/ready", filter);
|
|
901
|
+
const items = MeerkatClient.requireRecordArray(result.items, "Invalid workgraph item list").map((entry) => parseWorkItem(entry));
|
|
902
|
+
return { items };
|
|
799
903
|
}
|
|
800
904
|
async getWorkGraphSnapshot(filter = {}) {
|
|
801
|
-
const result = await this.request("workgraph/snapshot",
|
|
802
|
-
return
|
|
905
|
+
const result = await this.request("workgraph/snapshot", filter);
|
|
906
|
+
return parseWorkGraphSnapshot(result);
|
|
803
907
|
}
|
|
804
908
|
async listWorkGraphEvents(filter = {}) {
|
|
805
|
-
const result = await this.request("workgraph/events",
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
};
|
|
909
|
+
const result = await this.request("workgraph/events", filter);
|
|
910
|
+
const events = MeerkatClient.requireRecordArray(result.events, "Invalid workgraph event list").map((entry) => parseWorkGraphEvent(entry));
|
|
911
|
+
return { events };
|
|
809
912
|
}
|
|
810
913
|
async getWorkGraphGoalStatus(params) {
|
|
811
|
-
const result = await this.request("workgraph/goal/status",
|
|
812
|
-
return
|
|
914
|
+
const result = await this.request("workgraph/goal/status", params);
|
|
915
|
+
return parseGoalStatusResult(result);
|
|
813
916
|
}
|
|
814
917
|
async listWorkGraphAttention(params = {}) {
|
|
815
|
-
const result = await this.request("workgraph/attention/list",
|
|
816
|
-
return
|
|
817
|
-
attention: MeerkatClient.parseWorkAttentionBindingArray(result.attention),
|
|
818
|
-
};
|
|
918
|
+
const result = await this.request("workgraph/attention/list", params);
|
|
919
|
+
return parseAttentionListResult(result);
|
|
819
920
|
}
|
|
820
921
|
async subscribeSessionEvents(sessionId) {
|
|
821
922
|
return this.openEventSubscription("session/stream_open", { session_id: sessionId }, "session/stream_close", MeerkatClient.parseAgentEventEnvelope);
|
|
@@ -831,28 +932,23 @@ export class MeerkatClient {
|
|
|
831
932
|
async listMobs() {
|
|
832
933
|
this.requireCapability("mob");
|
|
833
934
|
const result = await this.request("mob/list", {});
|
|
834
|
-
const mobs = result.mobs
|
|
935
|
+
const mobs = MeerkatClient.requireRecordArray(result.mobs, "Invalid mob/list response");
|
|
835
936
|
return mobs.map((mob) => ({
|
|
836
937
|
mobId: String(mob.mob_id ?? mob.mobId ?? ""),
|
|
837
|
-
status:
|
|
938
|
+
status: MeerkatClient.requireStringField(mob, "status", "Invalid mob/list response entry: missing status"),
|
|
838
939
|
}));
|
|
839
940
|
}
|
|
840
941
|
async mobStatus(mobId) {
|
|
841
942
|
const result = await this.request("mob/status", { mob_id: mobId });
|
|
842
|
-
const
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
: undefined);
|
|
848
|
-
if (!status) {
|
|
849
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/status response: missing status");
|
|
850
|
-
}
|
|
851
|
-
return { mobId: String(result.mob_id ?? mobId), status };
|
|
943
|
+
const context = "Invalid mob/status response";
|
|
944
|
+
const record = MeerkatClient.requireRecord(result, "result", context);
|
|
945
|
+
const status = MeerkatClient.requireStringField(record, "status", context);
|
|
946
|
+
const responseMobId = MeerkatClient.requireStringField(record, "mob_id", context);
|
|
947
|
+
return { mobId: responseMobId, status };
|
|
852
948
|
}
|
|
853
949
|
async listMobMembers(mobId) {
|
|
854
950
|
const result = await this.request("mob/members", { mob_id: mobId });
|
|
855
|
-
const members = result.members
|
|
951
|
+
const members = MeerkatClient.requireRecordArray(result.members, "Invalid mob/members response");
|
|
856
952
|
return members.map((member) => {
|
|
857
953
|
const agentIdentity = String(member.agent_identity ?? "");
|
|
858
954
|
const memberRef = typeof member.member_ref === "string" && member.member_ref.length > 0
|
|
@@ -864,20 +960,21 @@ export class MeerkatClient {
|
|
|
864
960
|
return {
|
|
865
961
|
agentIdentity,
|
|
866
962
|
memberRef,
|
|
867
|
-
profile:
|
|
963
|
+
profile: MeerkatClient.requireStringField(member, "role", "Invalid mob/members response: missing role"),
|
|
868
964
|
peerId: member.peer_id != null ? String(member.peer_id) : undefined,
|
|
869
965
|
externalPeerSpecs: member.external_peer_specs && typeof member.external_peer_specs === "object"
|
|
870
966
|
? Object.fromEntries(Object.entries(member.external_peer_specs).map(([key, value]) => [key, (value ?? {})]))
|
|
871
967
|
: undefined,
|
|
872
968
|
runtimeMode: member.runtime_mode != null ? String(member.runtime_mode) : undefined,
|
|
873
|
-
state: member.state != null ? String(member.state) : undefined,
|
|
874
969
|
wiredTo: Array.isArray(member.wired_to)
|
|
875
970
|
? member.wired_to.map((peer) => String(peer))
|
|
876
971
|
: undefined,
|
|
877
972
|
labels: member.labels && typeof member.labels === "object"
|
|
878
973
|
? Object.fromEntries(Object.entries(member.labels).map(([key, value]) => [key, String(value)]))
|
|
879
974
|
: undefined,
|
|
880
|
-
status: member.status != null
|
|
975
|
+
status: member.status != null
|
|
976
|
+
? MeerkatClient.parseWireMobMemberStatus(member.status, "Invalid mob/members response: invalid member status")
|
|
977
|
+
: undefined,
|
|
881
978
|
error: member.error != null ? String(member.error) : undefined,
|
|
882
979
|
isFinal: member.is_final != null ? Boolean(member.is_final) : undefined,
|
|
883
980
|
};
|
|
@@ -902,9 +999,7 @@ export class MeerkatClient {
|
|
|
902
999
|
? result.agent_identity
|
|
903
1000
|
: agentIdentity,
|
|
904
1001
|
memberRef,
|
|
905
|
-
handlingMode: result.handling_mode
|
|
906
|
-
? result.handling_mode
|
|
907
|
-
: (options?.handlingMode ?? "queue"),
|
|
1002
|
+
handlingMode: MeerkatClient.parseWireHandlingMode(result.handling_mode, "Invalid mob/member_send response: missing or unknown handling_mode"),
|
|
908
1003
|
};
|
|
909
1004
|
}
|
|
910
1005
|
async spawnMobMember(mobId, options) {
|
|
@@ -1006,7 +1101,12 @@ export class MeerkatClient {
|
|
|
1006
1101
|
agent_identity: agentIdentity,
|
|
1007
1102
|
initial_message: initialMessage,
|
|
1008
1103
|
});
|
|
1009
|
-
const
|
|
1104
|
+
const respawnContext = "Invalid mob/respawn response";
|
|
1105
|
+
const respawnRecord = MeerkatClient.requireRecord(result, "result", respawnContext);
|
|
1106
|
+
const status = MeerkatClient.requireStringField(respawnRecord, "status", respawnContext);
|
|
1107
|
+
if (status !== "completed" && status !== "topology_restore_failed") {
|
|
1108
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/respawn response: invalid status");
|
|
1109
|
+
}
|
|
1010
1110
|
const rawFailed = Array.isArray(result.failed_peer_ids)
|
|
1011
1111
|
? result.failed_peer_ids
|
|
1012
1112
|
: [];
|
|
@@ -1021,7 +1121,7 @@ export class MeerkatClient {
|
|
|
1021
1121
|
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/respawn response: receipt missing member_ref");
|
|
1022
1122
|
}
|
|
1023
1123
|
return {
|
|
1024
|
-
status
|
|
1124
|
+
status,
|
|
1025
1125
|
receipt: {
|
|
1026
1126
|
agentIdentity: receipt.identity != null ? String(receipt.identity) : agentIdentity,
|
|
1027
1127
|
memberRef,
|
|
@@ -1047,7 +1147,7 @@ export class MeerkatClient {
|
|
|
1047
1147
|
? result.peer_connectivity
|
|
1048
1148
|
: undefined;
|
|
1049
1149
|
return {
|
|
1050
|
-
status:
|
|
1150
|
+
status: MeerkatClient.parseWireMobMemberStatus(result.status, "Invalid mob/member_status response: missing or invalid status"),
|
|
1051
1151
|
outputPreview: result.output_preview != null ? String(result.output_preview) : undefined,
|
|
1052
1152
|
error: result.error != null ? String(result.error) : undefined,
|
|
1053
1153
|
tokensUsed: Number(result.tokens_used ?? 0),
|
|
@@ -1079,9 +1179,10 @@ export class MeerkatClient {
|
|
|
1079
1179
|
*/
|
|
1080
1180
|
async mobSnapshot(mobId) {
|
|
1081
1181
|
const result = await this.request("mob/snapshot", { mob_id: mobId });
|
|
1182
|
+
const snapshotRecord = MeerkatClient.requireRecord(result, "result", "Invalid mob/snapshot response");
|
|
1082
1183
|
return {
|
|
1083
1184
|
mobId: String(result.mob_id ?? mobId),
|
|
1084
|
-
status:
|
|
1185
|
+
status: MeerkatClient.requireStringField(snapshotRecord, "status", "Invalid mob/snapshot response: missing status"),
|
|
1085
1186
|
members: Array.isArray(result.members) ? result.members : [],
|
|
1086
1187
|
};
|
|
1087
1188
|
}
|
|
@@ -1165,9 +1266,8 @@ export class MeerkatClient {
|
|
|
1165
1266
|
params.timeout_ms = options.timeoutMs;
|
|
1166
1267
|
}
|
|
1167
1268
|
const result = await this.request("mob/wait_kickoff", params);
|
|
1168
|
-
const members =
|
|
1169
|
-
return members.map((
|
|
1170
|
-
const member = entry && typeof entry === "object" ? entry : {};
|
|
1269
|
+
const members = MeerkatClient.requireRecordArray(result.members, "Invalid mob/wait_kickoff response");
|
|
1270
|
+
return members.map((member) => {
|
|
1171
1271
|
const agentIdentity = String(member.agent_identity ?? "");
|
|
1172
1272
|
if (!agentIdentity) {
|
|
1173
1273
|
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/wait_kickoff response: member missing agent_identity");
|
|
@@ -1212,9 +1312,8 @@ export class MeerkatClient {
|
|
|
1212
1312
|
params.timeout_ms = options.timeoutMs;
|
|
1213
1313
|
}
|
|
1214
1314
|
const result = await this.request("mob/wait_ready", params);
|
|
1215
|
-
const members =
|
|
1216
|
-
return members.map((
|
|
1217
|
-
const member = entry && typeof entry === "object" ? entry : {};
|
|
1315
|
+
const members = MeerkatClient.requireRecordArray(result.members, "Invalid mob/wait_ready response");
|
|
1316
|
+
return members.map((member) => {
|
|
1218
1317
|
const agentIdentity = String(member.agent_identity ?? "");
|
|
1219
1318
|
if (!agentIdentity) {
|
|
1220
1319
|
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/wait_ready response: member missing agent_identity");
|
|
@@ -1450,8 +1549,10 @@ export class MeerkatClient {
|
|
|
1450
1549
|
streamId,
|
|
1451
1550
|
queue,
|
|
1452
1551
|
closeRemote: async (id) => {
|
|
1453
|
-
|
|
1552
|
+
// Await stream-close authority before tearing down local dispatch:
|
|
1553
|
+
// a rejected close must leave the subscription delivering events.
|
|
1454
1554
|
await this.request(closeMethod, { stream_id: id });
|
|
1555
|
+
this.streamQueues.delete(id);
|
|
1455
1556
|
},
|
|
1456
1557
|
parseEvent: parse,
|
|
1457
1558
|
getTerminalOutcome: () => this.streamTerminalOutcomes.get(streamId),
|
|
@@ -1460,7 +1561,6 @@ export class MeerkatClient {
|
|
|
1460
1561
|
static parseAgentEventEnvelope(raw) {
|
|
1461
1562
|
const eventId = MeerkatClient.parseOptionalString(raw.event_id ?? raw.eventId);
|
|
1462
1563
|
const source = MeerkatClient.parseEventSourceIdentity(raw.source);
|
|
1463
|
-
const sourceId = MeerkatClient.parseOptionalString(raw.source_id ?? raw.sourceId);
|
|
1464
1564
|
const seq = MeerkatClient.parseOptionalNumber(raw.seq);
|
|
1465
1565
|
const timestampMs = MeerkatClient.parseOptionalNumber(raw.timestamp_ms ?? raw.timestampMs);
|
|
1466
1566
|
const payloadRaw = raw.payload;
|
|
@@ -1470,7 +1570,6 @@ export class MeerkatClient {
|
|
|
1470
1570
|
return {
|
|
1471
1571
|
...(eventId != null ? { eventId } : {}),
|
|
1472
1572
|
...(source != null ? { source } : {}),
|
|
1473
|
-
...(sourceId != null ? { sourceId } : {}),
|
|
1474
1573
|
...(seq != null ? { seq } : {}),
|
|
1475
1574
|
...(timestampMs != null ? { timestampMs } : {}),
|
|
1476
1575
|
...(payload ? { payload } : {}),
|
|
@@ -1512,10 +1611,28 @@ export class MeerkatClient {
|
|
|
1512
1611
|
return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
|
|
1513
1612
|
}
|
|
1514
1613
|
static parseAttributedMobEvent(raw) {
|
|
1614
|
+
const context = "Invalid attributed mob event";
|
|
1615
|
+
// The runtime wire shape (meerkat-mob AttributedEvent) carries a typed
|
|
1616
|
+
// source `{ identity, generation }` (AgentRuntimeId) and a `role`
|
|
1617
|
+
// (ProfileName) string — NOT free-form `source`/`profile` strings. Mirror
|
|
1618
|
+
// the web SDK `parseAttributedEventItem`/`parseAttributedSource`: validate
|
|
1619
|
+
// the source record (require non-empty `identity` + non-negative integer
|
|
1620
|
+
// `generation`) and the `role` field, throwing on absence/malformation.
|
|
1621
|
+
// The public `AttributedMobEvent` TS type exposes `source: string` /
|
|
1622
|
+
// `profile: string`, so the validated `identity` is projected into
|
|
1623
|
+
// `source` and the validated `role` into `profile`. Nothing is coalesced
|
|
1624
|
+
// to "" — absent/unknown fields fail closed.
|
|
1625
|
+
const source = MeerkatClient.requireRecord(raw.source, "source", context);
|
|
1626
|
+
const identity = MeerkatClient.requireStringField(source, "identity", context);
|
|
1627
|
+
const generation = MeerkatClient.requireNumberField(source, "generation", context);
|
|
1628
|
+
if (!Number.isInteger(generation) || generation < 0) {
|
|
1629
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: source generation must be a non-negative integer`);
|
|
1630
|
+
}
|
|
1631
|
+
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
1515
1632
|
return {
|
|
1516
|
-
source:
|
|
1517
|
-
profile:
|
|
1518
|
-
envelope: MeerkatClient.parseAgentEventEnvelope((raw.envelope
|
|
1633
|
+
source: identity,
|
|
1634
|
+
profile: role,
|
|
1635
|
+
envelope: MeerkatClient.parseAgentEventEnvelope(MeerkatClient.requireRecord(raw.envelope, "envelope", context)),
|
|
1519
1636
|
};
|
|
1520
1637
|
}
|
|
1521
1638
|
// -- Internal methods used by Session -----------------------------------
|
|
@@ -1608,7 +1725,9 @@ export class MeerkatClient {
|
|
|
1608
1725
|
}
|
|
1609
1726
|
/** @internal */
|
|
1610
1727
|
async _interrupt(sessionId) {
|
|
1611
|
-
await this.request("turn/interrupt", {
|
|
1728
|
+
return (await this.request("turn/interrupt", {
|
|
1729
|
+
session_id: sessionId,
|
|
1730
|
+
}));
|
|
1612
1731
|
}
|
|
1613
1732
|
/** @internal */
|
|
1614
1733
|
async _archive(sessionId) {
|
|
@@ -1718,7 +1837,8 @@ export class MeerkatClient {
|
|
|
1718
1837
|
return result;
|
|
1719
1838
|
}
|
|
1720
1839
|
async liveClose(params) {
|
|
1721
|
-
await this.request("live/close", params);
|
|
1840
|
+
const result = await this.request("live/close", params);
|
|
1841
|
+
return MeerkatClient.parseLiveCloseResult(result);
|
|
1722
1842
|
}
|
|
1723
1843
|
async liveSendInput(params) {
|
|
1724
1844
|
await this.request("live/send_input", params);
|
|
@@ -1782,19 +1902,18 @@ export class MeerkatClient {
|
|
|
1782
1902
|
* `model_id` and `provider_id` match the channel's currently-open
|
|
1783
1903
|
* identity and rejects swaps with a typed adapter-level error.
|
|
1784
1904
|
*
|
|
1785
|
-
* R4-5 (P3): the result is a typed `LiveRefreshResult` carrying
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
1789
|
-
* The host
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
*
|
|
1793
|
-
* `WireLiveAdapterObservation::Error`).
|
|
1905
|
+
* R4-5 (P3): the result is a typed `LiveRefreshResult` carrying the
|
|
1906
|
+
* generated-authority `status` discriminator (today: `"queued"`). This SDK
|
|
1907
|
+
* build accepts the generated status set it was built with and rejects
|
|
1908
|
+
* missing or unknown status values until regenerated for a newer contract.
|
|
1909
|
+
* The host queue acceptance happens before this result is projected; the
|
|
1910
|
+
* adapter pump applies the `session.update` asynchronously, and the
|
|
1911
|
+
* realtime stream is the source of truth for the actual outcome (failures
|
|
1912
|
+
* surface as `WireLiveAdapterObservation::Error`).
|
|
1794
1913
|
*/
|
|
1795
1914
|
async liveRefresh(params) {
|
|
1796
1915
|
const result = await this.request("live/refresh", params);
|
|
1797
|
-
return result;
|
|
1916
|
+
return MeerkatClient.parseLiveRefreshResult(result);
|
|
1798
1917
|
}
|
|
1799
1918
|
/**
|
|
1800
1919
|
* Type-narrow an inbound live-adapter observation against
|
|
@@ -1912,6 +2031,11 @@ export class MeerkatClient {
|
|
|
1912
2031
|
data = JSON.parse(line);
|
|
1913
2032
|
}
|
|
1914
2033
|
catch {
|
|
2034
|
+
// A corrupted (non-JSON) frame means the JSONL stream framing can no
|
|
2035
|
+
// longer be trusted. Surface a typed protocol fault to every pending
|
|
2036
|
+
// caller and stream consumer rather than silently dropping the line,
|
|
2037
|
+
// which would launder a corrupted stream into a hang / missing response.
|
|
2038
|
+
this.failTransport(new MeerkatError("PROTOCOL_ERROR", "Corrupted JSON-RPC frame: stream framing is no longer trustworthy"));
|
|
1915
2039
|
return;
|
|
1916
2040
|
}
|
|
1917
2041
|
// Server→client callback request (has both id and method).
|
|
@@ -1977,7 +2101,10 @@ export class MeerkatClient {
|
|
|
1977
2101
|
if (queue) {
|
|
1978
2102
|
queue.put(event);
|
|
1979
2103
|
}
|
|
1980
|
-
else if (this.
|
|
2104
|
+
else if (this.pendingStreamQueues.size > 0) {
|
|
2105
|
+
// A stream is pending but its session_id is not yet bound. Buffer by
|
|
2106
|
+
// session_id; the create response that binds this session_id drains
|
|
2107
|
+
// exactly this buffer into the matching request's queue.
|
|
1981
2108
|
const buffered = this.unmatchedStreamBuffer.get(sessionId) ?? [];
|
|
1982
2109
|
buffered.push(event);
|
|
1983
2110
|
this.unmatchedStreamBuffer.set(sessionId, buffered);
|
|
@@ -1995,29 +2122,20 @@ export class MeerkatClient {
|
|
|
1995
2122
|
details: parsed.details ?? parsed.reason ?? rawData,
|
|
1996
2123
|
};
|
|
1997
2124
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
if (parsed && typeof parsed === "object") {
|
|
2003
|
-
return {
|
|
2004
|
-
code: String(parsed.code ?? error.code ?? "UNKNOWN"),
|
|
2005
|
-
message: String(parsed.message ?? rawMessage),
|
|
2006
|
-
details: parsed.details ?? parsed.reason ?? error.data,
|
|
2007
|
-
};
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
catch {
|
|
2011
|
-
// Fall back to the outer JSON-RPC error payload.
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2125
|
+
// The server's typed error projection is `error.data` ({code, message,
|
|
2126
|
+
// details}). `error.message` is presentation text only — it is never
|
|
2127
|
+
// parsed as JSON to recover typed fields, so SDK error semantics cannot
|
|
2128
|
+
// depend on message-string folklore.
|
|
2014
2129
|
return {
|
|
2015
2130
|
code: String(error.code ?? "UNKNOWN"),
|
|
2016
|
-
message: String(
|
|
2131
|
+
message: String(error.message ?? "Unknown error"),
|
|
2017
2132
|
details: error.data,
|
|
2018
2133
|
};
|
|
2019
2134
|
}
|
|
2020
2135
|
request(method, params) {
|
|
2136
|
+
if (this.transportFault) {
|
|
2137
|
+
throw this.transportFault;
|
|
2138
|
+
}
|
|
2021
2139
|
if (!this.process?.stdin) {
|
|
2022
2140
|
throw new MeerkatError("NOT_CONNECTED", "Client not connected");
|
|
2023
2141
|
}
|
|
@@ -2037,16 +2155,6 @@ export class MeerkatClient {
|
|
|
2037
2155
|
});
|
|
2038
2156
|
}
|
|
2039
2157
|
// -- Static helpers -----------------------------------------------------
|
|
2040
|
-
static normalizeStatus(raw) {
|
|
2041
|
-
if (typeof raw === "string")
|
|
2042
|
-
return raw;
|
|
2043
|
-
if (typeof raw === "object" && raw !== null) {
|
|
2044
|
-
// Rust can emit externally-tagged enums for status:
|
|
2045
|
-
// { DisabledByPolicy: { description: "..." } }
|
|
2046
|
-
return Object.keys(raw)[0] ?? "Unknown";
|
|
2047
|
-
}
|
|
2048
|
-
return String(raw);
|
|
2049
|
-
}
|
|
2050
2158
|
static requireRecord(raw, field, context) {
|
|
2051
2159
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2052
2160
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${field}`);
|
|
@@ -2083,6 +2191,81 @@ export class MeerkatClient {
|
|
|
2083
2191
|
}
|
|
2084
2192
|
return value;
|
|
2085
2193
|
}
|
|
2194
|
+
static parseWireMobMemberStatus(raw, message) {
|
|
2195
|
+
if (typeof raw === "string" &&
|
|
2196
|
+
["active", "retiring", "broken", "completed", "unknown"].includes(raw)) {
|
|
2197
|
+
return raw;
|
|
2198
|
+
}
|
|
2199
|
+
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Parse a runtime-emitted handling mode against the closed WireHandlingMode
|
|
2203
|
+
* union. Fails closed on an absent or unrecognized variant — the SDK never
|
|
2204
|
+
* substitutes the client-requested mode for the runtime receipt. Mirrors the
|
|
2205
|
+
* web SDK `parseWireHandlingMode` and Python `_parse_wire_handling_mode`.
|
|
2206
|
+
*/
|
|
2207
|
+
static parseWireHandlingMode(raw, message) {
|
|
2208
|
+
if (raw === "queue" || raw === "steer") {
|
|
2209
|
+
return raw;
|
|
2210
|
+
}
|
|
2211
|
+
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2212
|
+
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Parse a capability status against the externally-tagged Rust
|
|
2215
|
+
* `CapabilityStatus` enum: either the bare string variant (e.g. "Available")
|
|
2216
|
+
* or a single-key object like `{ DisabledByPolicy: {...} }`. The capability
|
|
2217
|
+
* vocabulary evolves, so we do not whitelist variants, but we fail closed on
|
|
2218
|
+
* an absent or otherwise unparseable status rather than fabricating a
|
|
2219
|
+
* permissive default. Mirrors Python `_parse_wire_capability_status`.
|
|
2220
|
+
*/
|
|
2221
|
+
static parseWireCapabilityStatus(raw, context) {
|
|
2222
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
2223
|
+
return raw;
|
|
2224
|
+
}
|
|
2225
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
2226
|
+
const firstKey = Object.keys(raw)[0];
|
|
2227
|
+
if (typeof firstKey === "string" && firstKey.length > 0) {
|
|
2228
|
+
return firstKey;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing or invalid capability status`);
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Parse a single capabilities/get entry, failing closed on absent/malformed
|
|
2235
|
+
* required fields rather than coercing to empty strings or a permissive
|
|
2236
|
+
* status default. Mirrors the Python capability parser.
|
|
2237
|
+
*/
|
|
2238
|
+
static parseWireCapabilityEntry(raw) {
|
|
2239
|
+
const context = "Invalid capabilities/get response";
|
|
2240
|
+
const record = MeerkatClient.requireRecord(raw, "capability", context);
|
|
2241
|
+
return {
|
|
2242
|
+
id: MeerkatClient.requireStringField(record, "id", context),
|
|
2243
|
+
description: MeerkatClient.requireStringField(record, "description", context),
|
|
2244
|
+
status: MeerkatClient.parseWireCapabilityStatus(record.status, context),
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
static parseLiveRefreshResult(raw) {
|
|
2248
|
+
const context = "Invalid live/refresh response";
|
|
2249
|
+
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
2250
|
+
const status = MeerkatClient.requireStringField(record, "status", context);
|
|
2251
|
+
if (status !== "queued") {
|
|
2252
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported status ${JSON.stringify(status)}`);
|
|
2253
|
+
}
|
|
2254
|
+
return {
|
|
2255
|
+
status,
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
static parseLiveCloseResult(raw) {
|
|
2259
|
+
const context = "Invalid live/close response";
|
|
2260
|
+
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
2261
|
+
const status = MeerkatClient.requireStringField(record, "status", context);
|
|
2262
|
+
if (status !== "closed") {
|
|
2263
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported status ${JSON.stringify(status)}`);
|
|
2264
|
+
}
|
|
2265
|
+
return {
|
|
2266
|
+
status,
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2086
2269
|
static parseSkillDiagnostics(raw) {
|
|
2087
2270
|
if (!raw || typeof raw !== "object")
|
|
2088
2271
|
return undefined;
|
|
@@ -2116,16 +2299,10 @@ export class MeerkatClient {
|
|
|
2116
2299
|
};
|
|
2117
2300
|
}
|
|
2118
2301
|
static checkVersionCompatible(server, client) {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
return s[1] === c[1];
|
|
2124
|
-
return s[0] === c[0];
|
|
2125
|
-
}
|
|
2126
|
-
catch {
|
|
2127
|
-
return false;
|
|
2128
|
-
}
|
|
2302
|
+
// Drive contract-version compatibility off the generated helper
|
|
2303
|
+
// (mirrors `ContractVersion::is_compatible_with`) instead of a hand-rolled
|
|
2304
|
+
// copy of the rule (dogma row #193).
|
|
2305
|
+
return isCompatibleWith(server, client);
|
|
2129
2306
|
}
|
|
2130
2307
|
static parseRunResult(data) {
|
|
2131
2308
|
const context = "Invalid run result";
|
|
@@ -2195,33 +2372,30 @@ export class MeerkatClient {
|
|
|
2195
2372
|
return undefined;
|
|
2196
2373
|
}
|
|
2197
2374
|
const raw = data;
|
|
2375
|
+
const vision = raw.vision;
|
|
2376
|
+
const imageInput = raw.image_input;
|
|
2377
|
+
const imageToolResults = raw.image_tool_results;
|
|
2378
|
+
const inlineVideo = raw.inline_video;
|
|
2379
|
+
const realtime = raw.realtime;
|
|
2380
|
+
const webSearch = raw.web_search;
|
|
2381
|
+
const imageGeneration = raw.image_generation;
|
|
2382
|
+
if (typeof vision !== "boolean" ||
|
|
2383
|
+
typeof imageInput !== "boolean" ||
|
|
2384
|
+
typeof imageToolResults !== "boolean" ||
|
|
2385
|
+
typeof inlineVideo !== "boolean" ||
|
|
2386
|
+
typeof realtime !== "boolean" ||
|
|
2387
|
+
typeof webSearch !== "boolean" ||
|
|
2388
|
+
typeof imageGeneration !== "boolean") {
|
|
2389
|
+
return undefined;
|
|
2390
|
+
}
|
|
2198
2391
|
return {
|
|
2199
|
-
vision
|
|
2200
|
-
imageInput
|
|
2201
|
-
imageToolResults
|
|
2202
|
-
inlineVideo
|
|
2203
|
-
realtime
|
|
2204
|
-
webSearch
|
|
2205
|
-
imageGeneration
|
|
2206
|
-
};
|
|
2207
|
-
}
|
|
2208
|
-
static parseConfigEnvelope(data) {
|
|
2209
|
-
const rawConfig = data.config && typeof data.config === "object"
|
|
2210
|
-
? data.config
|
|
2211
|
-
: {};
|
|
2212
|
-
const rawResolvedPaths = data.resolved_paths && typeof data.resolved_paths === "object"
|
|
2213
|
-
? data.resolved_paths
|
|
2214
|
-
: undefined;
|
|
2215
|
-
const resolvedPaths = rawResolvedPaths
|
|
2216
|
-
? Object.fromEntries(Object.entries(rawResolvedPaths).map(([key, value]) => [key, String(value)]))
|
|
2217
|
-
: undefined;
|
|
2218
|
-
return {
|
|
2219
|
-
config: rawConfig,
|
|
2220
|
-
generation: Number(data.generation ?? 0),
|
|
2221
|
-
realmId: data.realm_id != null ? String(data.realm_id) : undefined,
|
|
2222
|
-
instanceId: data.instance_id != null ? String(data.instance_id) : undefined,
|
|
2223
|
-
backend: data.backend != null ? String(data.backend) : undefined,
|
|
2224
|
-
resolvedPaths,
|
|
2392
|
+
vision,
|
|
2393
|
+
imageInput,
|
|
2394
|
+
imageToolResults,
|
|
2395
|
+
inlineVideo,
|
|
2396
|
+
realtime,
|
|
2397
|
+
webSearch,
|
|
2398
|
+
imageGeneration,
|
|
2225
2399
|
};
|
|
2226
2400
|
}
|
|
2227
2401
|
static parseCommsSendReceipt(data) {
|
|
@@ -2236,25 +2410,7 @@ export class MeerkatClient {
|
|
|
2236
2410
|
const providersRaw = Array.isArray(data.providers)
|
|
2237
2411
|
? data.providers
|
|
2238
2412
|
: [];
|
|
2239
|
-
|
|
2240
|
-
if (data.contract_version && typeof data.contract_version === "object") {
|
|
2241
|
-
const contractVersionRaw = data.contract_version;
|
|
2242
|
-
contractVersion = {
|
|
2243
|
-
major: Number(contractVersionRaw.major ?? 0),
|
|
2244
|
-
minor: Number(contractVersionRaw.minor ?? 0),
|
|
2245
|
-
patch: Number(contractVersionRaw.patch ?? 0),
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
else if (typeof data.contract_version === "string") {
|
|
2249
|
-
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(data.contract_version);
|
|
2250
|
-
if (match) {
|
|
2251
|
-
contractVersion = {
|
|
2252
|
-
major: Number(match[1]),
|
|
2253
|
-
minor: Number(match[2]),
|
|
2254
|
-
patch: Number(match[3]),
|
|
2255
|
-
};
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2413
|
+
const contractVersion = MeerkatClient.parseContractVersion(data.contract_version, "Invalid models/catalog response");
|
|
2258
2414
|
return {
|
|
2259
2415
|
contractVersion,
|
|
2260
2416
|
providers: providersRaw.map((provider) => ({
|
|
@@ -2291,6 +2447,36 @@ export class MeerkatClient {
|
|
|
2291
2447
|
})),
|
|
2292
2448
|
};
|
|
2293
2449
|
}
|
|
2450
|
+
static parseContractVersion(raw, context) {
|
|
2451
|
+
const parseComponent = (value, field) => {
|
|
2452
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
|
|
2453
|
+
return value;
|
|
2454
|
+
}
|
|
2455
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
2456
|
+
return Number(value);
|
|
2457
|
+
}
|
|
2458
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: contract_version.${field} must be non-negative integer`);
|
|
2459
|
+
};
|
|
2460
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
2461
|
+
const record = raw;
|
|
2462
|
+
return {
|
|
2463
|
+
major: parseComponent(record.major, "major"),
|
|
2464
|
+
minor: parseComponent(record.minor, "minor"),
|
|
2465
|
+
patch: parseComponent(record.patch, "patch"),
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
if (typeof raw === "string") {
|
|
2469
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(raw);
|
|
2470
|
+
if (match) {
|
|
2471
|
+
return {
|
|
2472
|
+
major: parseComponent(match[1], "major"),
|
|
2473
|
+
minor: parseComponent(match[2], "minor"),
|
|
2474
|
+
patch: parseComponent(match[3], "patch"),
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing contract_version`);
|
|
2479
|
+
}
|
|
2294
2480
|
static parseSchedule(data) {
|
|
2295
2481
|
const labelsRaw = data.labels && typeof data.labels === "object"
|
|
2296
2482
|
? data.labels
|
|
@@ -2373,53 +2559,6 @@ export class MeerkatClient {
|
|
|
2373
2559
|
: undefined,
|
|
2374
2560
|
};
|
|
2375
2561
|
}
|
|
2376
|
-
static toWireWorkGraphScope(options) {
|
|
2377
|
-
const params = {};
|
|
2378
|
-
setIfDefined(params, "realm_id", options?.realmId);
|
|
2379
|
-
setIfDefined(params, "namespace", options?.namespace);
|
|
2380
|
-
return params;
|
|
2381
|
-
}
|
|
2382
|
-
static toWireWorkGraphItemFilter(filter) {
|
|
2383
|
-
const params = MeerkatClient.toWireWorkGraphScope(filter);
|
|
2384
|
-
setIfDefined(params, "all_namespaces", filter.allNamespaces);
|
|
2385
|
-
setIfDefined(params, "statuses", filter.statuses);
|
|
2386
|
-
setIfDefined(params, "labels", filter.labels);
|
|
2387
|
-
setIfDefined(params, "include_terminal", filter.includeTerminal);
|
|
2388
|
-
setIfDefined(params, "limit", filter.limit);
|
|
2389
|
-
return params;
|
|
2390
|
-
}
|
|
2391
|
-
static toWireWorkGraphReadyFilter(filter) {
|
|
2392
|
-
const params = MeerkatClient.toWireWorkGraphScope(filter);
|
|
2393
|
-
setIfDefined(params, "labels", filter.labels);
|
|
2394
|
-
setIfDefined(params, "limit", filter.limit);
|
|
2395
|
-
return params;
|
|
2396
|
-
}
|
|
2397
|
-
static toWireWorkGraphEventFilter(filter) {
|
|
2398
|
-
const params = MeerkatClient.toWireWorkGraphScope(filter);
|
|
2399
|
-
setIfDefined(params, "all_namespaces", filter.allNamespaces);
|
|
2400
|
-
setIfDefined(params, "after_seq", filter.afterSeq);
|
|
2401
|
-
setIfDefined(params, "limit", filter.limit);
|
|
2402
|
-
return params;
|
|
2403
|
-
}
|
|
2404
|
-
static toWireWorkGraphGoalStatusRequest(request) {
|
|
2405
|
-
const params = MeerkatClient.toWireWorkGraphScope(request);
|
|
2406
|
-
params.binding_id = request.bindingId;
|
|
2407
|
-
return params;
|
|
2408
|
-
}
|
|
2409
|
-
static toWireWorkGraphAttentionTarget(target) {
|
|
2410
|
-
if (target.kind === "session") {
|
|
2411
|
-
return { kind: "session", session_id: target.sessionId };
|
|
2412
|
-
}
|
|
2413
|
-
return { kind: "lowered_owner", owner_key: target.ownerKey };
|
|
2414
|
-
}
|
|
2415
|
-
static toWireWorkGraphAttentionListRequest(request) {
|
|
2416
|
-
const params = MeerkatClient.toWireWorkGraphScope(request);
|
|
2417
|
-
setIfDefined(params, "status", request.status);
|
|
2418
|
-
if (request.target !== undefined) {
|
|
2419
|
-
params.target = MeerkatClient.toWireWorkGraphAttentionTarget(request.target);
|
|
2420
|
-
}
|
|
2421
|
-
return params;
|
|
2422
|
-
}
|
|
2423
2562
|
static parseStringArray(value, context) {
|
|
2424
2563
|
if (value == null) {
|
|
2425
2564
|
return [];
|
|
@@ -2449,35 +2588,6 @@ export class MeerkatClient {
|
|
|
2449
2588
|
}
|
|
2450
2589
|
return value.map((item, index) => MeerkatClient.requireRecord(item, `entry ${index}`, context));
|
|
2451
2590
|
}
|
|
2452
|
-
static parseWorkGraphOwner(raw, context) {
|
|
2453
|
-
if (raw == null) {
|
|
2454
|
-
return undefined;
|
|
2455
|
-
}
|
|
2456
|
-
const data = MeerkatClient.requireRecord(raw, "owner", context);
|
|
2457
|
-
const key = MeerkatClient.requireRecord(data.key, "key", context);
|
|
2458
|
-
const kind = MeerkatClient.requireStringField(key, "kind", context);
|
|
2459
|
-
if (!["principal", "agent", "session", "mob", "label"].includes(kind)) {
|
|
2460
|
-
throw new MeerkatError("INVALID_RESPONSE", `${context}: invalid owner key kind`);
|
|
2461
|
-
}
|
|
2462
|
-
return {
|
|
2463
|
-
key: {
|
|
2464
|
-
kind: kind,
|
|
2465
|
-
id: MeerkatClient.requireStringField(key, "id", context),
|
|
2466
|
-
},
|
|
2467
|
-
displayName: MeerkatClient.parseOptionalString(data.display_name),
|
|
2468
|
-
};
|
|
2469
|
-
}
|
|
2470
|
-
static parseWorkOwnerKey(raw, context) {
|
|
2471
|
-
const key = MeerkatClient.requireRecord(raw, "owner_key", context);
|
|
2472
|
-
const kind = MeerkatClient.requireStringField(key, "kind", context);
|
|
2473
|
-
if (!["principal", "agent", "session", "mob", "label"].includes(kind)) {
|
|
2474
|
-
throw new MeerkatError("INVALID_RESPONSE", `${context}: invalid owner key kind`);
|
|
2475
|
-
}
|
|
2476
|
-
return {
|
|
2477
|
-
kind: kind,
|
|
2478
|
-
id: MeerkatClient.requireStringField(key, "id", context),
|
|
2479
|
-
};
|
|
2480
|
-
}
|
|
2481
2591
|
static parseMobWireMembersBatchEdge(raw, context) {
|
|
2482
2592
|
const data = MeerkatClient.requireRecord(raw, "edge", context);
|
|
2483
2593
|
return {
|
|
@@ -2496,214 +2606,6 @@ export class MeerkatClient {
|
|
|
2496
2606
|
alreadyWired: MeerkatClient.parseMobWireMembersBatchEdges(data.already_wired, `${context} already_wired`),
|
|
2497
2607
|
};
|
|
2498
2608
|
}
|
|
2499
|
-
static parseWorkGraphClaim(raw) {
|
|
2500
|
-
if (raw == null) {
|
|
2501
|
-
return undefined;
|
|
2502
|
-
}
|
|
2503
|
-
const data = MeerkatClient.requireRecord(raw, "claim", "Invalid workgraph item");
|
|
2504
|
-
const owner = MeerkatClient.parseWorkGraphOwner(data.owner, "Invalid workgraph item claim");
|
|
2505
|
-
if (!owner) {
|
|
2506
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph item claim: missing owner");
|
|
2507
|
-
}
|
|
2508
|
-
return {
|
|
2509
|
-
owner,
|
|
2510
|
-
claimedAt: MeerkatClient.requireStringField(data, "claimed_at", "Invalid workgraph item claim"),
|
|
2511
|
-
leaseExpiresAt: MeerkatClient.parseOptionalString(data.lease_expires_at),
|
|
2512
|
-
};
|
|
2513
|
-
}
|
|
2514
|
-
static parseWorkCompletionPolicy(raw) {
|
|
2515
|
-
if (raw === undefined || raw === null) {
|
|
2516
|
-
return { kind: "self_attest" };
|
|
2517
|
-
}
|
|
2518
|
-
const policy = MeerkatClient.requireRecord(raw, "completion_policy", "Invalid workgraph item");
|
|
2519
|
-
const kind = MeerkatClient.requireStringField(policy, "kind", "Invalid workgraph completion policy");
|
|
2520
|
-
switch (kind) {
|
|
2521
|
-
case "self_attest":
|
|
2522
|
-
case "host_confirmed":
|
|
2523
|
-
case "principal_confirmed":
|
|
2524
|
-
return { kind };
|
|
2525
|
-
case "supervisor":
|
|
2526
|
-
return {
|
|
2527
|
-
kind,
|
|
2528
|
-
owner_key: MeerkatClient.parseWorkOwnerKey(policy.owner_key, "Invalid workgraph completion policy"),
|
|
2529
|
-
};
|
|
2530
|
-
case "reviewer_quorum":
|
|
2531
|
-
return {
|
|
2532
|
-
kind,
|
|
2533
|
-
threshold: MeerkatClient.requireNumberField(policy, "threshold", "Invalid workgraph completion policy"),
|
|
2534
|
-
};
|
|
2535
|
-
default:
|
|
2536
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph completion policy: invalid kind");
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
static parseWorkItem(data) {
|
|
2540
|
-
const status = MeerkatClient.requireStringField(data, "status", "Invalid workgraph item");
|
|
2541
|
-
if (!["open", "in_progress", "blocked", "completed", "cancelled", "failed"].includes(status)) {
|
|
2542
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph item: invalid status");
|
|
2543
|
-
}
|
|
2544
|
-
const priority = MeerkatClient.requireStringField(data, "priority", "Invalid workgraph item");
|
|
2545
|
-
if (!["low", "medium", "high"].includes(priority)) {
|
|
2546
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph item: invalid priority");
|
|
2547
|
-
}
|
|
2548
|
-
return {
|
|
2549
|
-
id: MeerkatClient.requireStringField(data, "id", "Invalid workgraph item"),
|
|
2550
|
-
realmId: MeerkatClient.requireStringField(data, "realm_id", "Invalid workgraph item"),
|
|
2551
|
-
namespace: MeerkatClient.requireStringField(data, "namespace", "Invalid workgraph item"),
|
|
2552
|
-
title: MeerkatClient.requireStringField(data, "title", "Invalid workgraph item"),
|
|
2553
|
-
description: MeerkatClient.parseOptionalString(data.description),
|
|
2554
|
-
status: status,
|
|
2555
|
-
priority: priority,
|
|
2556
|
-
completionPolicy: MeerkatClient.parseWorkCompletionPolicy(data.completion_policy),
|
|
2557
|
-
labels: MeerkatClient.parseStringArray(data.labels, "Invalid workgraph item labels"),
|
|
2558
|
-
owner: MeerkatClient.parseWorkGraphOwner(data.owner, "Invalid workgraph item"),
|
|
2559
|
-
claim: MeerkatClient.parseWorkGraphClaim(data.claim),
|
|
2560
|
-
machineState: MeerkatClient.requireRecord(data.machine_state, "machine_state", "Invalid workgraph item"),
|
|
2561
|
-
revision: MeerkatClient.requireNumberField(data, "revision", "Invalid workgraph item"),
|
|
2562
|
-
dueAt: MeerkatClient.parseOptionalString(data.due_at),
|
|
2563
|
-
notBefore: MeerkatClient.parseOptionalString(data.not_before),
|
|
2564
|
-
snoozedUntil: MeerkatClient.parseOptionalString(data.snoozed_until),
|
|
2565
|
-
createdAt: MeerkatClient.requireStringField(data, "created_at", "Invalid workgraph item"),
|
|
2566
|
-
updatedAt: MeerkatClient.requireStringField(data, "updated_at", "Invalid workgraph item"),
|
|
2567
|
-
terminalAt: MeerkatClient.parseOptionalString(data.terminal_at),
|
|
2568
|
-
externalRefs: MeerkatClient.parseRecordArray(data.external_refs, "Invalid workgraph external refs").map((ref) => ({
|
|
2569
|
-
kind: MeerkatClient.requireStringField(ref, "kind", "Invalid workgraph external ref"),
|
|
2570
|
-
id: MeerkatClient.requireStringField(ref, "id", "Invalid workgraph external ref"),
|
|
2571
|
-
url: MeerkatClient.parseOptionalString(ref.url),
|
|
2572
|
-
})),
|
|
2573
|
-
evidenceRefs: MeerkatClient.parseRecordArray(data.evidence_refs, "Invalid workgraph evidence refs").map((ref) => ({
|
|
2574
|
-
kind: MeerkatClient.requireStringField(ref, "kind", "Invalid workgraph evidence ref"),
|
|
2575
|
-
id: MeerkatClient.requireStringField(ref, "id", "Invalid workgraph evidence ref"),
|
|
2576
|
-
label: MeerkatClient.parseOptionalString(ref.label),
|
|
2577
|
-
summary: MeerkatClient.parseOptionalString(ref.summary),
|
|
2578
|
-
})),
|
|
2579
|
-
};
|
|
2580
|
-
}
|
|
2581
|
-
static parseWorkItemArray(value, context = "Invalid workgraph item list") {
|
|
2582
|
-
return MeerkatClient.requireRecordArray(value, context).map((item) => MeerkatClient.parseWorkItem(item));
|
|
2583
|
-
}
|
|
2584
|
-
static parseWorkGraphGoalResult(data) {
|
|
2585
|
-
return {
|
|
2586
|
-
item: MeerkatClient.parseWorkItem(MeerkatClient.requireRecord(data.item, "item", "Invalid workgraph goal result")),
|
|
2587
|
-
attention: MeerkatClient.parseWorkAttentionBinding(MeerkatClient.requireRecord(data.attention, "attention", "Invalid workgraph goal result")),
|
|
2588
|
-
};
|
|
2589
|
-
}
|
|
2590
|
-
static parseWorkAttentionBinding(data) {
|
|
2591
|
-
const bindingId = MeerkatClient.requireStringField(data, "binding_id", "Invalid workgraph attention binding");
|
|
2592
|
-
const createdAt = MeerkatClient.requireStringField(data, "created_at", "Invalid workgraph attention binding");
|
|
2593
|
-
const delegatedAuthority = MeerkatClient.requireStringField(data, "delegated_authority", "Invalid workgraph attention binding");
|
|
2594
|
-
if (!WORK_ATTENTION_DELEGATED_AUTHORITIES.has(delegatedAuthority)) {
|
|
2595
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph attention binding: invalid delegated_authority");
|
|
2596
|
-
}
|
|
2597
|
-
const mode = MeerkatClient.requireStringField(data, "mode", "Invalid workgraph attention binding");
|
|
2598
|
-
if (!WORK_ATTENTION_MODES.has(mode)) {
|
|
2599
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph attention binding: invalid mode");
|
|
2600
|
-
}
|
|
2601
|
-
const updatedAt = MeerkatClient.requireStringField(data, "updated_at", "Invalid workgraph attention binding");
|
|
2602
|
-
const status = MeerkatClient.requireRecord(data.status, "status", "Invalid workgraph attention binding");
|
|
2603
|
-
const statusState = MeerkatClient.requireStringField(status, "state", "Invalid workgraph attention status");
|
|
2604
|
-
if (!WORK_ATTENTION_STATES.has(statusState)) {
|
|
2605
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph attention status: invalid state");
|
|
2606
|
-
}
|
|
2607
|
-
const target = MeerkatClient.requireRecord(data.target, "target", "Invalid workgraph attention binding");
|
|
2608
|
-
const targetKind = MeerkatClient.requireStringField(target, "kind", "Invalid workgraph attention target");
|
|
2609
|
-
if (targetKind === "session") {
|
|
2610
|
-
MeerkatClient.requireStringField(target, "session_id", "Invalid workgraph attention target");
|
|
2611
|
-
}
|
|
2612
|
-
else if (targetKind === "lowered_owner") {
|
|
2613
|
-
MeerkatClient.requireRecord(target.owner_key, "owner_key", "Invalid workgraph attention target");
|
|
2614
|
-
}
|
|
2615
|
-
else {
|
|
2616
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph attention target: invalid kind");
|
|
2617
|
-
}
|
|
2618
|
-
const workRef = MeerkatClient.requireRecord(data.work_ref, "work_ref", "Invalid workgraph attention binding");
|
|
2619
|
-
MeerkatClient.requireStringField(workRef, "realm_id", "Invalid workgraph attention work ref");
|
|
2620
|
-
MeerkatClient.requireStringField(workRef, "namespace", "Invalid workgraph attention work ref");
|
|
2621
|
-
MeerkatClient.requireStringField(workRef, "item_id", "Invalid workgraph attention work ref");
|
|
2622
|
-
const attention = {
|
|
2623
|
-
bindingId,
|
|
2624
|
-
createdAt,
|
|
2625
|
-
delegatedAuthority: delegatedAuthority,
|
|
2626
|
-
machineState: MeerkatClient.optionalRecord(data.machine_state),
|
|
2627
|
-
mode: mode,
|
|
2628
|
-
projectionPolicy: MeerkatClient.optionalRecord(data.projection_policy),
|
|
2629
|
-
status: status,
|
|
2630
|
-
target: targetKind === "session"
|
|
2631
|
-
? {
|
|
2632
|
-
kind: "session",
|
|
2633
|
-
sessionId: MeerkatClient.requireStringField(target, "session_id", "Invalid workgraph attention target"),
|
|
2634
|
-
}
|
|
2635
|
-
: {
|
|
2636
|
-
kind: "loweredOwner",
|
|
2637
|
-
ownerKey: MeerkatClient.parseWorkOwnerKey(target.owner_key, "Invalid workgraph attention target"),
|
|
2638
|
-
},
|
|
2639
|
-
updatedAt,
|
|
2640
|
-
workRef: {
|
|
2641
|
-
realmId: MeerkatClient.requireStringField(workRef, "realm_id", "Invalid workgraph attention work ref"),
|
|
2642
|
-
namespace: MeerkatClient.requireStringField(workRef, "namespace", "Invalid workgraph attention work ref"),
|
|
2643
|
-
itemId: MeerkatClient.requireStringField(workRef, "item_id", "Invalid workgraph attention work ref"),
|
|
2644
|
-
},
|
|
2645
|
-
};
|
|
2646
|
-
return attention;
|
|
2647
|
-
}
|
|
2648
|
-
static parseWorkAttentionBindingArray(value) {
|
|
2649
|
-
return MeerkatClient.requireRecordArray(value, "Invalid workgraph attention list").map((attention) => MeerkatClient.parseWorkAttentionBinding(attention));
|
|
2650
|
-
}
|
|
2651
|
-
static parseWorkGraphEdge(data) {
|
|
2652
|
-
const kind = MeerkatClient.requireStringField(data, "kind", "Invalid workgraph edge");
|
|
2653
|
-
if (!["blocks", "parent", "related", "supersedes", "derived_from"].includes(kind)) {
|
|
2654
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph edge: invalid kind");
|
|
2655
|
-
}
|
|
2656
|
-
return {
|
|
2657
|
-
realmId: MeerkatClient.requireStringField(data, "realm_id", "Invalid workgraph edge"),
|
|
2658
|
-
namespace: MeerkatClient.requireStringField(data, "namespace", "Invalid workgraph edge"),
|
|
2659
|
-
kind: kind,
|
|
2660
|
-
fromId: MeerkatClient.requireStringField(data, "from_id", "Invalid workgraph edge"),
|
|
2661
|
-
toId: MeerkatClient.requireStringField(data, "to_id", "Invalid workgraph edge"),
|
|
2662
|
-
createdAt: MeerkatClient.requireStringField(data, "created_at", "Invalid workgraph edge"),
|
|
2663
|
-
};
|
|
2664
|
-
}
|
|
2665
|
-
static parseWorkGraphEvent(data) {
|
|
2666
|
-
const kind = MeerkatClient.requireStringField(data, "kind", "Invalid workgraph event");
|
|
2667
|
-
if (![
|
|
2668
|
-
"created",
|
|
2669
|
-
"updated",
|
|
2670
|
-
"claimed",
|
|
2671
|
-
"released",
|
|
2672
|
-
"blocked",
|
|
2673
|
-
"closed",
|
|
2674
|
-
"linked",
|
|
2675
|
-
"evidence_added",
|
|
2676
|
-
"attention_created",
|
|
2677
|
-
"attention_updated",
|
|
2678
|
-
].includes(kind)) {
|
|
2679
|
-
throw new MeerkatError("INVALID_RESPONSE", "Invalid workgraph event: invalid kind");
|
|
2680
|
-
}
|
|
2681
|
-
return {
|
|
2682
|
-
seq: MeerkatClient.parseOptionalNumber(data.seq),
|
|
2683
|
-
realmId: MeerkatClient.requireStringField(data, "realm_id", "Invalid workgraph event"),
|
|
2684
|
-
namespace: MeerkatClient.requireStringField(data, "namespace", "Invalid workgraph event"),
|
|
2685
|
-
itemId: MeerkatClient.parseOptionalString(data.item_id),
|
|
2686
|
-
kind: kind,
|
|
2687
|
-
at: MeerkatClient.requireStringField(data, "at", "Invalid workgraph event"),
|
|
2688
|
-
payload: data.payload,
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
|
-
static parseWorkGraphEventArray(value) {
|
|
2692
|
-
return MeerkatClient.requireRecordArray(value, "Invalid workgraph event list").map((event) => MeerkatClient.parseWorkGraphEvent(event));
|
|
2693
|
-
}
|
|
2694
|
-
static parseWorkGraphSnapshot(data) {
|
|
2695
|
-
return {
|
|
2696
|
-
realmId: MeerkatClient.requireStringField(data, "realm_id", "Invalid workgraph snapshot"),
|
|
2697
|
-
namespace: MeerkatClient.parseOptionalString(data.namespace),
|
|
2698
|
-
allNamespaces: MeerkatClient.requireBooleanField(data, "all_namespaces", "Invalid workgraph snapshot"),
|
|
2699
|
-
capturedAt: MeerkatClient.requireStringField(data, "captured_at", "Invalid workgraph snapshot"),
|
|
2700
|
-
eventHighWaterMark: MeerkatClient.parseOptionalNumber(data.event_high_water_mark),
|
|
2701
|
-
items: MeerkatClient.parseWorkItemArray(data.items, "Invalid workgraph snapshot items"),
|
|
2702
|
-
edges: MeerkatClient.requireRecordArray(data.edges, "Invalid workgraph snapshot edges").map((edge) => MeerkatClient.parseWorkGraphEdge(edge)),
|
|
2703
|
-
attention: MeerkatClient.parseWorkAttentionBindingArray(data.attention ?? []),
|
|
2704
|
-
readyItemIds: MeerkatClient.requireStringArray(data.ready_item_ids, "Invalid workgraph snapshot ready item ids"),
|
|
2705
|
-
};
|
|
2706
|
-
}
|
|
2707
2609
|
static parseMobProfileLookup(data) {
|
|
2708
2610
|
if (Boolean(data.not_found)) {
|
|
2709
2611
|
return {
|
|
@@ -2752,50 +2654,60 @@ export class MeerkatClient {
|
|
|
2752
2654
|
};
|
|
2753
2655
|
}
|
|
2754
2656
|
static parseSessionHistory(data) {
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
:
|
|
2657
|
+
const context = "Invalid session history response";
|
|
2658
|
+
if (!Array.isArray(data.messages)) {
|
|
2659
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: messages must be a list`);
|
|
2660
|
+
}
|
|
2661
|
+
const rawMessages = data.messages;
|
|
2758
2662
|
return {
|
|
2759
|
-
sessionId:
|
|
2663
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2760
2664
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2761
|
-
messageCount:
|
|
2762
|
-
offset:
|
|
2665
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2666
|
+
offset: MeerkatClient.requireNumberField(data, "offset", context),
|
|
2763
2667
|
limit: data.limit != null ? Number(data.limit) : undefined,
|
|
2764
|
-
hasMore:
|
|
2668
|
+
hasMore: MeerkatClient.requireBooleanField(data, "has_more", context),
|
|
2765
2669
|
messages: rawMessages.map((message) => MeerkatClient.parseSessionMessage(message)),
|
|
2766
2670
|
};
|
|
2767
2671
|
}
|
|
2768
2672
|
static parseSessionTranscriptRevision(data) {
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
:
|
|
2673
|
+
const context = "Invalid session transcript revision response";
|
|
2674
|
+
if (!Array.isArray(data.messages)) {
|
|
2675
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: messages must be a list`);
|
|
2676
|
+
}
|
|
2677
|
+
const rawMessages = data.messages;
|
|
2772
2678
|
return {
|
|
2773
|
-
sessionId:
|
|
2679
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2774
2680
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2775
|
-
revision:
|
|
2776
|
-
headRevision:
|
|
2777
|
-
messageCount:
|
|
2778
|
-
offset:
|
|
2681
|
+
revision: MeerkatClient.requireStringField(data, "revision", context),
|
|
2682
|
+
headRevision: MeerkatClient.requireStringField(data, "head_revision", context),
|
|
2683
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2684
|
+
offset: MeerkatClient.requireNumberField(data, "offset", context),
|
|
2779
2685
|
limit: data.limit != null ? Number(data.limit) : undefined,
|
|
2780
|
-
hasMore:
|
|
2686
|
+
hasMore: MeerkatClient.requireBooleanField(data, "has_more", context),
|
|
2781
2687
|
messages: rawMessages.map((message) => MeerkatClient.parseSessionMessage(message)),
|
|
2782
2688
|
};
|
|
2783
2689
|
}
|
|
2784
2690
|
static parseSessionForkResult(data) {
|
|
2691
|
+
const context = "Invalid session fork response";
|
|
2785
2692
|
return {
|
|
2786
|
-
sourceSessionId:
|
|
2787
|
-
sessionId:
|
|
2693
|
+
sourceSessionId: MeerkatClient.requireStringField(data, "source_session_id", context),
|
|
2694
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2788
2695
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2789
|
-
messageCount:
|
|
2696
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2790
2697
|
};
|
|
2791
2698
|
}
|
|
2792
2699
|
static parseSessionTranscriptRewriteResult(data) {
|
|
2700
|
+
const context = "Invalid session transcript rewrite response";
|
|
2701
|
+
const commit = data.commit;
|
|
2702
|
+
if (typeof commit !== "object" || commit === null || Array.isArray(commit)) {
|
|
2703
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: commit must be an object`);
|
|
2704
|
+
}
|
|
2793
2705
|
return {
|
|
2794
|
-
sessionId:
|
|
2795
|
-
parentRevision:
|
|
2796
|
-
revision:
|
|
2797
|
-
messageCount:
|
|
2798
|
-
commit:
|
|
2706
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2707
|
+
parentRevision: MeerkatClient.requireStringField(data, "parent_revision", context),
|
|
2708
|
+
revision: MeerkatClient.requireStringField(data, "revision", context),
|
|
2709
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2710
|
+
commit: commit,
|
|
2799
2711
|
};
|
|
2800
2712
|
}
|
|
2801
2713
|
static serializeTranscriptReplacement(replacement) {
|
|
@@ -2828,37 +2740,50 @@ export class MeerkatClient {
|
|
|
2828
2740
|
throw new Error(`Unsupported transcript replacement type: ${replacement.type}`);
|
|
2829
2741
|
}
|
|
2830
2742
|
static parseSessionMessage(data) {
|
|
2831
|
-
|
|
2743
|
+
// Transcript truth fails closed: a message without its identity facts
|
|
2744
|
+
// (role, created_at) or with malformed collections is a wire-contract
|
|
2745
|
+
// violation, never coerced to ""/[] placeholder truth.
|
|
2746
|
+
const context = "Invalid session message";
|
|
2747
|
+
const role = MeerkatClient.requireStringField(data, "role", context);
|
|
2748
|
+
const createdAt = MeerkatClient.requireStringField(data, "created_at", context);
|
|
2832
2749
|
const contentValue = role === "system_notice" && data.content == null && data.body != null
|
|
2833
2750
|
? String(data.body)
|
|
2834
2751
|
: data.content;
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
const
|
|
2842
|
-
|
|
2843
|
-
: [];
|
|
2752
|
+
if (data.blocks != null && !Array.isArray(data.blocks)) {
|
|
2753
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: blocks must be a list`);
|
|
2754
|
+
}
|
|
2755
|
+
if (data.results != null && !Array.isArray(data.results)) {
|
|
2756
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: results must be a list`);
|
|
2757
|
+
}
|
|
2758
|
+
const rawBlocks = data.blocks ?? [];
|
|
2759
|
+
const rawResults = data.results ?? [];
|
|
2844
2760
|
return {
|
|
2845
2761
|
role,
|
|
2846
|
-
createdAt
|
|
2762
|
+
createdAt,
|
|
2847
2763
|
kind: data.kind != null ? String(data.kind) : undefined,
|
|
2848
2764
|
body: data.body != null ? String(data.body) : undefined,
|
|
2849
2765
|
content: contentValue != null ? MeerkatClient.parseContentInput(contentValue) : undefined,
|
|
2850
|
-
toolCalls: rawToolCalls.map((toolCall) => ({
|
|
2851
|
-
id: String(toolCall.id ?? ""),
|
|
2852
|
-
name: String(toolCall.name ?? ""),
|
|
2853
|
-
args: toolCall.args,
|
|
2854
|
-
})),
|
|
2855
2766
|
stopReason: data.stop_reason != null ? String(data.stop_reason) : undefined,
|
|
2856
2767
|
blocks: rawBlocks.map((block) => MeerkatClient.parseSessionAssistantBlock(block)),
|
|
2857
|
-
results: rawResults.map((result) =>
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2768
|
+
results: rawResults.map((result) => {
|
|
2769
|
+
if (typeof result !== "object" || result === null) {
|
|
2770
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result must be an object`);
|
|
2771
|
+
}
|
|
2772
|
+
const isError = result.is_error ?? false;
|
|
2773
|
+
if (typeof isError !== "boolean") {
|
|
2774
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: is_error must be a boolean`);
|
|
2775
|
+
}
|
|
2776
|
+
// `WireToolResult.content` is mandatory on the wire; a frame without
|
|
2777
|
+
// it is malformed and must not be coalesced into an empty transcript.
|
|
2778
|
+
if (result.content === undefined || result.content === null) {
|
|
2779
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result missing content`);
|
|
2780
|
+
}
|
|
2781
|
+
return {
|
|
2782
|
+
toolUseId: MeerkatClient.requireStringField(result, "tool_use_id", context),
|
|
2783
|
+
content: MeerkatClient.parseContentInput(result.content),
|
|
2784
|
+
isError,
|
|
2785
|
+
};
|
|
2786
|
+
}),
|
|
2862
2787
|
raw: { ...data },
|
|
2863
2788
|
};
|
|
2864
2789
|
}
|
|
@@ -2891,13 +2816,6 @@ export class MeerkatClient {
|
|
|
2891
2816
|
payload.content = camel.content;
|
|
2892
2817
|
}
|
|
2893
2818
|
}
|
|
2894
|
-
if (camel.toolCalls?.length > 0) {
|
|
2895
|
-
payload.tool_calls = camel.toolCalls.map((toolCall) => ({
|
|
2896
|
-
id: toolCall.id,
|
|
2897
|
-
name: toolCall.name,
|
|
2898
|
-
args: toolCall.args,
|
|
2899
|
-
}));
|
|
2900
|
-
}
|
|
2901
2819
|
if (camel.stopReason !== undefined) {
|
|
2902
2820
|
payload.stop_reason = camel.stopReason;
|
|
2903
2821
|
}
|
|
@@ -2981,13 +2899,17 @@ export class MeerkatClient {
|
|
|
2981
2899
|
return { type: "text", text: "" };
|
|
2982
2900
|
}
|
|
2983
2901
|
static parseSessionAssistantBlock(data) {
|
|
2902
|
+
const context = "Invalid session assistant block";
|
|
2903
|
+
if (data.data != null && (typeof data.data !== "object" || Array.isArray(data.data))) {
|
|
2904
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: data must be an object`);
|
|
2905
|
+
}
|
|
2984
2906
|
const blockData = data.data ?? {};
|
|
2985
2907
|
const blobRef = blockData.blob_ref;
|
|
2986
2908
|
const revisedPrompt = blockData.revised_prompt != null && typeof blockData.revised_prompt === "object"
|
|
2987
2909
|
? blockData.revised_prompt
|
|
2988
2910
|
: undefined;
|
|
2989
2911
|
return {
|
|
2990
|
-
blockType:
|
|
2912
|
+
blockType: MeerkatClient.requireStringField(data, "block_type", context),
|
|
2991
2913
|
text: blockData.text != null ? String(blockData.text) : undefined,
|
|
2992
2914
|
id: blockData.id != null ? String(blockData.id) : undefined,
|
|
2993
2915
|
name: blockData.name != null ? String(blockData.name) : undefined,
|