@rkat/sdk 0.6.34 → 0.7.1
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 +92 -46
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +487 -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 +349 -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/mob.d.ts +6 -1
- package/dist/mob.d.ts.map +1 -1
- package/dist/mob.js +6 -0
- package/dist/mob.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 +37 -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");
|
|
@@ -1408,10 +1507,24 @@ export class MeerkatClient {
|
|
|
1408
1507
|
const result = await this.request("mob/flow_run", { mob_id: mobId, flow_id: flowId, params });
|
|
1409
1508
|
return String(result.run_id ?? "");
|
|
1410
1509
|
}
|
|
1510
|
+
async runMob(mobId, params = {}, options = {}) {
|
|
1511
|
+
const payload = { mob_id: mobId, params };
|
|
1512
|
+
if (options.prompt !== undefined)
|
|
1513
|
+
payload.prompt = options.prompt;
|
|
1514
|
+
if (options.flowId !== undefined)
|
|
1515
|
+
payload.flow_id = options.flowId;
|
|
1516
|
+
const result = (await this.request("mob/run", payload));
|
|
1517
|
+
return String(result.run_id ?? "");
|
|
1518
|
+
}
|
|
1411
1519
|
async getMobFlowStatus(mobId, runId) {
|
|
1412
1520
|
const result = await this.request("mob/flow_status", { mob_id: mobId, run_id: runId });
|
|
1413
1521
|
return result.run == null ? null : { run: result.run };
|
|
1414
1522
|
}
|
|
1523
|
+
async getMobRunResult(mobId, runId) {
|
|
1524
|
+
const params = { mob_id: mobId, run_id: runId };
|
|
1525
|
+
const result = (await this.request("mob/run_result", params));
|
|
1526
|
+
return result.run == null ? null : { run: result.run };
|
|
1527
|
+
}
|
|
1415
1528
|
async cancelMobFlow(mobId, runId) {
|
|
1416
1529
|
await this.request("mob/flow_cancel", { mob_id: mobId, run_id: runId });
|
|
1417
1530
|
}
|
|
@@ -1450,8 +1563,10 @@ export class MeerkatClient {
|
|
|
1450
1563
|
streamId,
|
|
1451
1564
|
queue,
|
|
1452
1565
|
closeRemote: async (id) => {
|
|
1453
|
-
|
|
1566
|
+
// Await stream-close authority before tearing down local dispatch:
|
|
1567
|
+
// a rejected close must leave the subscription delivering events.
|
|
1454
1568
|
await this.request(closeMethod, { stream_id: id });
|
|
1569
|
+
this.streamQueues.delete(id);
|
|
1455
1570
|
},
|
|
1456
1571
|
parseEvent: parse,
|
|
1457
1572
|
getTerminalOutcome: () => this.streamTerminalOutcomes.get(streamId),
|
|
@@ -1460,7 +1575,6 @@ export class MeerkatClient {
|
|
|
1460
1575
|
static parseAgentEventEnvelope(raw) {
|
|
1461
1576
|
const eventId = MeerkatClient.parseOptionalString(raw.event_id ?? raw.eventId);
|
|
1462
1577
|
const source = MeerkatClient.parseEventSourceIdentity(raw.source);
|
|
1463
|
-
const sourceId = MeerkatClient.parseOptionalString(raw.source_id ?? raw.sourceId);
|
|
1464
1578
|
const seq = MeerkatClient.parseOptionalNumber(raw.seq);
|
|
1465
1579
|
const timestampMs = MeerkatClient.parseOptionalNumber(raw.timestamp_ms ?? raw.timestampMs);
|
|
1466
1580
|
const payloadRaw = raw.payload;
|
|
@@ -1470,7 +1584,6 @@ export class MeerkatClient {
|
|
|
1470
1584
|
return {
|
|
1471
1585
|
...(eventId != null ? { eventId } : {}),
|
|
1472
1586
|
...(source != null ? { source } : {}),
|
|
1473
|
-
...(sourceId != null ? { sourceId } : {}),
|
|
1474
1587
|
...(seq != null ? { seq } : {}),
|
|
1475
1588
|
...(timestampMs != null ? { timestampMs } : {}),
|
|
1476
1589
|
...(payload ? { payload } : {}),
|
|
@@ -1512,10 +1625,28 @@ export class MeerkatClient {
|
|
|
1512
1625
|
return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
|
|
1513
1626
|
}
|
|
1514
1627
|
static parseAttributedMobEvent(raw) {
|
|
1628
|
+
const context = "Invalid attributed mob event";
|
|
1629
|
+
// The runtime wire shape (meerkat-mob AttributedEvent) carries a typed
|
|
1630
|
+
// source `{ identity, generation }` (AgentRuntimeId) and a `role`
|
|
1631
|
+
// (ProfileName) string — NOT free-form `source`/`profile` strings. Mirror
|
|
1632
|
+
// the web SDK `parseAttributedEventItem`/`parseAttributedSource`: validate
|
|
1633
|
+
// the source record (require non-empty `identity` + non-negative integer
|
|
1634
|
+
// `generation`) and the `role` field, throwing on absence/malformation.
|
|
1635
|
+
// The public `AttributedMobEvent` TS type exposes `source: string` /
|
|
1636
|
+
// `profile: string`, so the validated `identity` is projected into
|
|
1637
|
+
// `source` and the validated `role` into `profile`. Nothing is coalesced
|
|
1638
|
+
// to "" — absent/unknown fields fail closed.
|
|
1639
|
+
const source = MeerkatClient.requireRecord(raw.source, "source", context);
|
|
1640
|
+
const identity = MeerkatClient.requireStringField(source, "identity", context);
|
|
1641
|
+
const generation = MeerkatClient.requireNumberField(source, "generation", context);
|
|
1642
|
+
if (!Number.isInteger(generation) || generation < 0) {
|
|
1643
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: source generation must be a non-negative integer`);
|
|
1644
|
+
}
|
|
1645
|
+
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
1515
1646
|
return {
|
|
1516
|
-
source:
|
|
1517
|
-
profile:
|
|
1518
|
-
envelope: MeerkatClient.parseAgentEventEnvelope((raw.envelope
|
|
1647
|
+
source: identity,
|
|
1648
|
+
profile: role,
|
|
1649
|
+
envelope: MeerkatClient.parseAgentEventEnvelope(MeerkatClient.requireRecord(raw.envelope, "envelope", context)),
|
|
1519
1650
|
};
|
|
1520
1651
|
}
|
|
1521
1652
|
// -- Internal methods used by Session -----------------------------------
|
|
@@ -1608,7 +1739,9 @@ export class MeerkatClient {
|
|
|
1608
1739
|
}
|
|
1609
1740
|
/** @internal */
|
|
1610
1741
|
async _interrupt(sessionId) {
|
|
1611
|
-
await this.request("turn/interrupt", {
|
|
1742
|
+
return (await this.request("turn/interrupt", {
|
|
1743
|
+
session_id: sessionId,
|
|
1744
|
+
}));
|
|
1612
1745
|
}
|
|
1613
1746
|
/** @internal */
|
|
1614
1747
|
async _archive(sessionId) {
|
|
@@ -1718,7 +1851,8 @@ export class MeerkatClient {
|
|
|
1718
1851
|
return result;
|
|
1719
1852
|
}
|
|
1720
1853
|
async liveClose(params) {
|
|
1721
|
-
await this.request("live/close", params);
|
|
1854
|
+
const result = await this.request("live/close", params);
|
|
1855
|
+
return MeerkatClient.parseLiveCloseResult(result);
|
|
1722
1856
|
}
|
|
1723
1857
|
async liveSendInput(params) {
|
|
1724
1858
|
await this.request("live/send_input", params);
|
|
@@ -1782,19 +1916,18 @@ export class MeerkatClient {
|
|
|
1782
1916
|
* `model_id` and `provider_id` match the channel's currently-open
|
|
1783
1917
|
* identity and rejects swaps with a typed adapter-level error.
|
|
1784
1918
|
*
|
|
1785
|
-
* R4-5 (P3): the result is a typed `LiveRefreshResult` carrying
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
1789
|
-
* The host
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
*
|
|
1793
|
-
* `WireLiveAdapterObservation::Error`).
|
|
1919
|
+
* R4-5 (P3): the result is a typed `LiveRefreshResult` carrying the
|
|
1920
|
+
* generated-authority `status` discriminator (today: `"queued"`). This SDK
|
|
1921
|
+
* build accepts the generated status set it was built with and rejects
|
|
1922
|
+
* missing or unknown status values until regenerated for a newer contract.
|
|
1923
|
+
* The host queue acceptance happens before this result is projected; the
|
|
1924
|
+
* adapter pump applies the `session.update` asynchronously, and the
|
|
1925
|
+
* realtime stream is the source of truth for the actual outcome (failures
|
|
1926
|
+
* surface as `WireLiveAdapterObservation::Error`).
|
|
1794
1927
|
*/
|
|
1795
1928
|
async liveRefresh(params) {
|
|
1796
1929
|
const result = await this.request("live/refresh", params);
|
|
1797
|
-
return result;
|
|
1930
|
+
return MeerkatClient.parseLiveRefreshResult(result);
|
|
1798
1931
|
}
|
|
1799
1932
|
/**
|
|
1800
1933
|
* Type-narrow an inbound live-adapter observation against
|
|
@@ -1912,6 +2045,11 @@ export class MeerkatClient {
|
|
|
1912
2045
|
data = JSON.parse(line);
|
|
1913
2046
|
}
|
|
1914
2047
|
catch {
|
|
2048
|
+
// A corrupted (non-JSON) frame means the JSONL stream framing can no
|
|
2049
|
+
// longer be trusted. Surface a typed protocol fault to every pending
|
|
2050
|
+
// caller and stream consumer rather than silently dropping the line,
|
|
2051
|
+
// which would launder a corrupted stream into a hang / missing response.
|
|
2052
|
+
this.failTransport(new MeerkatError("PROTOCOL_ERROR", "Corrupted JSON-RPC frame: stream framing is no longer trustworthy"));
|
|
1915
2053
|
return;
|
|
1916
2054
|
}
|
|
1917
2055
|
// Server→client callback request (has both id and method).
|
|
@@ -1977,7 +2115,10 @@ export class MeerkatClient {
|
|
|
1977
2115
|
if (queue) {
|
|
1978
2116
|
queue.put(event);
|
|
1979
2117
|
}
|
|
1980
|
-
else if (this.
|
|
2118
|
+
else if (this.pendingStreamQueues.size > 0) {
|
|
2119
|
+
// A stream is pending but its session_id is not yet bound. Buffer by
|
|
2120
|
+
// session_id; the create response that binds this session_id drains
|
|
2121
|
+
// exactly this buffer into the matching request's queue.
|
|
1981
2122
|
const buffered = this.unmatchedStreamBuffer.get(sessionId) ?? [];
|
|
1982
2123
|
buffered.push(event);
|
|
1983
2124
|
this.unmatchedStreamBuffer.set(sessionId, buffered);
|
|
@@ -1995,29 +2136,20 @@ export class MeerkatClient {
|
|
|
1995
2136
|
details: parsed.details ?? parsed.reason ?? rawData,
|
|
1996
2137
|
};
|
|
1997
2138
|
}
|
|
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
|
-
}
|
|
2139
|
+
// The server's typed error projection is `error.data` ({code, message,
|
|
2140
|
+
// details}). `error.message` is presentation text only — it is never
|
|
2141
|
+
// parsed as JSON to recover typed fields, so SDK error semantics cannot
|
|
2142
|
+
// depend on message-string folklore.
|
|
2014
2143
|
return {
|
|
2015
2144
|
code: String(error.code ?? "UNKNOWN"),
|
|
2016
|
-
message: String(
|
|
2145
|
+
message: String(error.message ?? "Unknown error"),
|
|
2017
2146
|
details: error.data,
|
|
2018
2147
|
};
|
|
2019
2148
|
}
|
|
2020
2149
|
request(method, params) {
|
|
2150
|
+
if (this.transportFault) {
|
|
2151
|
+
throw this.transportFault;
|
|
2152
|
+
}
|
|
2021
2153
|
if (!this.process?.stdin) {
|
|
2022
2154
|
throw new MeerkatError("NOT_CONNECTED", "Client not connected");
|
|
2023
2155
|
}
|
|
@@ -2037,16 +2169,6 @@ export class MeerkatClient {
|
|
|
2037
2169
|
});
|
|
2038
2170
|
}
|
|
2039
2171
|
// -- 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
2172
|
static requireRecord(raw, field, context) {
|
|
2051
2173
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2052
2174
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${field}`);
|
|
@@ -2083,6 +2205,81 @@ export class MeerkatClient {
|
|
|
2083
2205
|
}
|
|
2084
2206
|
return value;
|
|
2085
2207
|
}
|
|
2208
|
+
static parseWireMobMemberStatus(raw, message) {
|
|
2209
|
+
if (typeof raw === "string" &&
|
|
2210
|
+
["active", "retiring", "broken", "completed", "unknown"].includes(raw)) {
|
|
2211
|
+
return raw;
|
|
2212
|
+
}
|
|
2213
|
+
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Parse a runtime-emitted handling mode against the closed WireHandlingMode
|
|
2217
|
+
* union. Fails closed on an absent or unrecognized variant — the SDK never
|
|
2218
|
+
* substitutes the client-requested mode for the runtime receipt. Mirrors the
|
|
2219
|
+
* web SDK `parseWireHandlingMode` and Python `_parse_wire_handling_mode`.
|
|
2220
|
+
*/
|
|
2221
|
+
static parseWireHandlingMode(raw, message) {
|
|
2222
|
+
if (raw === "queue" || raw === "steer") {
|
|
2223
|
+
return raw;
|
|
2224
|
+
}
|
|
2225
|
+
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2226
|
+
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Parse a capability status against the externally-tagged Rust
|
|
2229
|
+
* `CapabilityStatus` enum: either the bare string variant (e.g. "Available")
|
|
2230
|
+
* or a single-key object like `{ DisabledByPolicy: {...} }`. The capability
|
|
2231
|
+
* vocabulary evolves, so we do not whitelist variants, but we fail closed on
|
|
2232
|
+
* an absent or otherwise unparseable status rather than fabricating a
|
|
2233
|
+
* permissive default. Mirrors Python `_parse_wire_capability_status`.
|
|
2234
|
+
*/
|
|
2235
|
+
static parseWireCapabilityStatus(raw, context) {
|
|
2236
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
2237
|
+
return raw;
|
|
2238
|
+
}
|
|
2239
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
2240
|
+
const firstKey = Object.keys(raw)[0];
|
|
2241
|
+
if (typeof firstKey === "string" && firstKey.length > 0) {
|
|
2242
|
+
return firstKey;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing or invalid capability status`);
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Parse a single capabilities/get entry, failing closed on absent/malformed
|
|
2249
|
+
* required fields rather than coercing to empty strings or a permissive
|
|
2250
|
+
* status default. Mirrors the Python capability parser.
|
|
2251
|
+
*/
|
|
2252
|
+
static parseWireCapabilityEntry(raw) {
|
|
2253
|
+
const context = "Invalid capabilities/get response";
|
|
2254
|
+
const record = MeerkatClient.requireRecord(raw, "capability", context);
|
|
2255
|
+
return {
|
|
2256
|
+
id: MeerkatClient.requireStringField(record, "id", context),
|
|
2257
|
+
description: MeerkatClient.requireStringField(record, "description", context),
|
|
2258
|
+
status: MeerkatClient.parseWireCapabilityStatus(record.status, context),
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
static parseLiveRefreshResult(raw) {
|
|
2262
|
+
const context = "Invalid live/refresh response";
|
|
2263
|
+
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
2264
|
+
const status = MeerkatClient.requireStringField(record, "status", context);
|
|
2265
|
+
if (status !== "queued") {
|
|
2266
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported status ${JSON.stringify(status)}`);
|
|
2267
|
+
}
|
|
2268
|
+
return {
|
|
2269
|
+
status,
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
static parseLiveCloseResult(raw) {
|
|
2273
|
+
const context = "Invalid live/close response";
|
|
2274
|
+
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
2275
|
+
const status = MeerkatClient.requireStringField(record, "status", context);
|
|
2276
|
+
if (status !== "closed") {
|
|
2277
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported status ${JSON.stringify(status)}`);
|
|
2278
|
+
}
|
|
2279
|
+
return {
|
|
2280
|
+
status,
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2086
2283
|
static parseSkillDiagnostics(raw) {
|
|
2087
2284
|
if (!raw || typeof raw !== "object")
|
|
2088
2285
|
return undefined;
|
|
@@ -2116,16 +2313,10 @@ export class MeerkatClient {
|
|
|
2116
2313
|
};
|
|
2117
2314
|
}
|
|
2118
2315
|
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
|
-
}
|
|
2316
|
+
// Drive contract-version compatibility off the generated helper
|
|
2317
|
+
// (mirrors `ContractVersion::is_compatible_with`) instead of a hand-rolled
|
|
2318
|
+
// copy of the rule (dogma row #193).
|
|
2319
|
+
return isCompatibleWith(server, client);
|
|
2129
2320
|
}
|
|
2130
2321
|
static parseRunResult(data) {
|
|
2131
2322
|
const context = "Invalid run result";
|
|
@@ -2195,33 +2386,30 @@ export class MeerkatClient {
|
|
|
2195
2386
|
return undefined;
|
|
2196
2387
|
}
|
|
2197
2388
|
const raw = data;
|
|
2389
|
+
const vision = raw.vision;
|
|
2390
|
+
const imageInput = raw.image_input;
|
|
2391
|
+
const imageToolResults = raw.image_tool_results;
|
|
2392
|
+
const inlineVideo = raw.inline_video;
|
|
2393
|
+
const realtime = raw.realtime;
|
|
2394
|
+
const webSearch = raw.web_search;
|
|
2395
|
+
const imageGeneration = raw.image_generation;
|
|
2396
|
+
if (typeof vision !== "boolean" ||
|
|
2397
|
+
typeof imageInput !== "boolean" ||
|
|
2398
|
+
typeof imageToolResults !== "boolean" ||
|
|
2399
|
+
typeof inlineVideo !== "boolean" ||
|
|
2400
|
+
typeof realtime !== "boolean" ||
|
|
2401
|
+
typeof webSearch !== "boolean" ||
|
|
2402
|
+
typeof imageGeneration !== "boolean") {
|
|
2403
|
+
return undefined;
|
|
2404
|
+
}
|
|
2198
2405
|
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,
|
|
2406
|
+
vision,
|
|
2407
|
+
imageInput,
|
|
2408
|
+
imageToolResults,
|
|
2409
|
+
inlineVideo,
|
|
2410
|
+
realtime,
|
|
2411
|
+
webSearch,
|
|
2412
|
+
imageGeneration,
|
|
2225
2413
|
};
|
|
2226
2414
|
}
|
|
2227
2415
|
static parseCommsSendReceipt(data) {
|
|
@@ -2236,25 +2424,7 @@ export class MeerkatClient {
|
|
|
2236
2424
|
const providersRaw = Array.isArray(data.providers)
|
|
2237
2425
|
? data.providers
|
|
2238
2426
|
: [];
|
|
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
|
-
}
|
|
2427
|
+
const contractVersion = MeerkatClient.parseContractVersion(data.contract_version, "Invalid models/catalog response");
|
|
2258
2428
|
return {
|
|
2259
2429
|
contractVersion,
|
|
2260
2430
|
providers: providersRaw.map((provider) => ({
|
|
@@ -2291,6 +2461,36 @@ export class MeerkatClient {
|
|
|
2291
2461
|
})),
|
|
2292
2462
|
};
|
|
2293
2463
|
}
|
|
2464
|
+
static parseContractVersion(raw, context) {
|
|
2465
|
+
const parseComponent = (value, field) => {
|
|
2466
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
|
|
2467
|
+
return value;
|
|
2468
|
+
}
|
|
2469
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
2470
|
+
return Number(value);
|
|
2471
|
+
}
|
|
2472
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: contract_version.${field} must be non-negative integer`);
|
|
2473
|
+
};
|
|
2474
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
2475
|
+
const record = raw;
|
|
2476
|
+
return {
|
|
2477
|
+
major: parseComponent(record.major, "major"),
|
|
2478
|
+
minor: parseComponent(record.minor, "minor"),
|
|
2479
|
+
patch: parseComponent(record.patch, "patch"),
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
if (typeof raw === "string") {
|
|
2483
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(raw);
|
|
2484
|
+
if (match) {
|
|
2485
|
+
return {
|
|
2486
|
+
major: parseComponent(match[1], "major"),
|
|
2487
|
+
minor: parseComponent(match[2], "minor"),
|
|
2488
|
+
patch: parseComponent(match[3], "patch"),
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing contract_version`);
|
|
2493
|
+
}
|
|
2294
2494
|
static parseSchedule(data) {
|
|
2295
2495
|
const labelsRaw = data.labels && typeof data.labels === "object"
|
|
2296
2496
|
? data.labels
|
|
@@ -2373,53 +2573,6 @@ export class MeerkatClient {
|
|
|
2373
2573
|
: undefined,
|
|
2374
2574
|
};
|
|
2375
2575
|
}
|
|
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
2576
|
static parseStringArray(value, context) {
|
|
2424
2577
|
if (value == null) {
|
|
2425
2578
|
return [];
|
|
@@ -2449,35 +2602,6 @@ export class MeerkatClient {
|
|
|
2449
2602
|
}
|
|
2450
2603
|
return value.map((item, index) => MeerkatClient.requireRecord(item, `entry ${index}`, context));
|
|
2451
2604
|
}
|
|
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
2605
|
static parseMobWireMembersBatchEdge(raw, context) {
|
|
2482
2606
|
const data = MeerkatClient.requireRecord(raw, "edge", context);
|
|
2483
2607
|
return {
|
|
@@ -2496,214 +2620,6 @@ export class MeerkatClient {
|
|
|
2496
2620
|
alreadyWired: MeerkatClient.parseMobWireMembersBatchEdges(data.already_wired, `${context} already_wired`),
|
|
2497
2621
|
};
|
|
2498
2622
|
}
|
|
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
2623
|
static parseMobProfileLookup(data) {
|
|
2708
2624
|
if (Boolean(data.not_found)) {
|
|
2709
2625
|
return {
|
|
@@ -2752,50 +2668,60 @@ export class MeerkatClient {
|
|
|
2752
2668
|
};
|
|
2753
2669
|
}
|
|
2754
2670
|
static parseSessionHistory(data) {
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
:
|
|
2671
|
+
const context = "Invalid session history response";
|
|
2672
|
+
if (!Array.isArray(data.messages)) {
|
|
2673
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: messages must be a list`);
|
|
2674
|
+
}
|
|
2675
|
+
const rawMessages = data.messages;
|
|
2758
2676
|
return {
|
|
2759
|
-
sessionId:
|
|
2677
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2760
2678
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2761
|
-
messageCount:
|
|
2762
|
-
offset:
|
|
2679
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2680
|
+
offset: MeerkatClient.requireNumberField(data, "offset", context),
|
|
2763
2681
|
limit: data.limit != null ? Number(data.limit) : undefined,
|
|
2764
|
-
hasMore:
|
|
2682
|
+
hasMore: MeerkatClient.requireBooleanField(data, "has_more", context),
|
|
2765
2683
|
messages: rawMessages.map((message) => MeerkatClient.parseSessionMessage(message)),
|
|
2766
2684
|
};
|
|
2767
2685
|
}
|
|
2768
2686
|
static parseSessionTranscriptRevision(data) {
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
:
|
|
2687
|
+
const context = "Invalid session transcript revision response";
|
|
2688
|
+
if (!Array.isArray(data.messages)) {
|
|
2689
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: messages must be a list`);
|
|
2690
|
+
}
|
|
2691
|
+
const rawMessages = data.messages;
|
|
2772
2692
|
return {
|
|
2773
|
-
sessionId:
|
|
2693
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2774
2694
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2775
|
-
revision:
|
|
2776
|
-
headRevision:
|
|
2777
|
-
messageCount:
|
|
2778
|
-
offset:
|
|
2695
|
+
revision: MeerkatClient.requireStringField(data, "revision", context),
|
|
2696
|
+
headRevision: MeerkatClient.requireStringField(data, "head_revision", context),
|
|
2697
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2698
|
+
offset: MeerkatClient.requireNumberField(data, "offset", context),
|
|
2779
2699
|
limit: data.limit != null ? Number(data.limit) : undefined,
|
|
2780
|
-
hasMore:
|
|
2700
|
+
hasMore: MeerkatClient.requireBooleanField(data, "has_more", context),
|
|
2781
2701
|
messages: rawMessages.map((message) => MeerkatClient.parseSessionMessage(message)),
|
|
2782
2702
|
};
|
|
2783
2703
|
}
|
|
2784
2704
|
static parseSessionForkResult(data) {
|
|
2705
|
+
const context = "Invalid session fork response";
|
|
2785
2706
|
return {
|
|
2786
|
-
sourceSessionId:
|
|
2787
|
-
sessionId:
|
|
2707
|
+
sourceSessionId: MeerkatClient.requireStringField(data, "source_session_id", context),
|
|
2708
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2788
2709
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
2789
|
-
messageCount:
|
|
2710
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2790
2711
|
};
|
|
2791
2712
|
}
|
|
2792
2713
|
static parseSessionTranscriptRewriteResult(data) {
|
|
2714
|
+
const context = "Invalid session transcript rewrite response";
|
|
2715
|
+
const commit = data.commit;
|
|
2716
|
+
if (typeof commit !== "object" || commit === null || Array.isArray(commit)) {
|
|
2717
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: commit must be an object`);
|
|
2718
|
+
}
|
|
2793
2719
|
return {
|
|
2794
|
-
sessionId:
|
|
2795
|
-
parentRevision:
|
|
2796
|
-
revision:
|
|
2797
|
-
messageCount:
|
|
2798
|
-
commit:
|
|
2720
|
+
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
2721
|
+
parentRevision: MeerkatClient.requireStringField(data, "parent_revision", context),
|
|
2722
|
+
revision: MeerkatClient.requireStringField(data, "revision", context),
|
|
2723
|
+
messageCount: MeerkatClient.requireNumberField(data, "message_count", context),
|
|
2724
|
+
commit: commit,
|
|
2799
2725
|
};
|
|
2800
2726
|
}
|
|
2801
2727
|
static serializeTranscriptReplacement(replacement) {
|
|
@@ -2828,37 +2754,50 @@ export class MeerkatClient {
|
|
|
2828
2754
|
throw new Error(`Unsupported transcript replacement type: ${replacement.type}`);
|
|
2829
2755
|
}
|
|
2830
2756
|
static parseSessionMessage(data) {
|
|
2831
|
-
|
|
2757
|
+
// Transcript truth fails closed: a message without its identity facts
|
|
2758
|
+
// (role, created_at) or with malformed collections is a wire-contract
|
|
2759
|
+
// violation, never coerced to ""/[] placeholder truth.
|
|
2760
|
+
const context = "Invalid session message";
|
|
2761
|
+
const role = MeerkatClient.requireStringField(data, "role", context);
|
|
2762
|
+
const createdAt = MeerkatClient.requireStringField(data, "created_at", context);
|
|
2832
2763
|
const contentValue = role === "system_notice" && data.content == null && data.body != null
|
|
2833
2764
|
? String(data.body)
|
|
2834
2765
|
: data.content;
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
const
|
|
2842
|
-
|
|
2843
|
-
: [];
|
|
2766
|
+
if (data.blocks != null && !Array.isArray(data.blocks)) {
|
|
2767
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: blocks must be a list`);
|
|
2768
|
+
}
|
|
2769
|
+
if (data.results != null && !Array.isArray(data.results)) {
|
|
2770
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: results must be a list`);
|
|
2771
|
+
}
|
|
2772
|
+
const rawBlocks = data.blocks ?? [];
|
|
2773
|
+
const rawResults = data.results ?? [];
|
|
2844
2774
|
return {
|
|
2845
2775
|
role,
|
|
2846
|
-
createdAt
|
|
2776
|
+
createdAt,
|
|
2847
2777
|
kind: data.kind != null ? String(data.kind) : undefined,
|
|
2848
2778
|
body: data.body != null ? String(data.body) : undefined,
|
|
2849
2779
|
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
2780
|
stopReason: data.stop_reason != null ? String(data.stop_reason) : undefined,
|
|
2856
2781
|
blocks: rawBlocks.map((block) => MeerkatClient.parseSessionAssistantBlock(block)),
|
|
2857
|
-
results: rawResults.map((result) =>
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2782
|
+
results: rawResults.map((result) => {
|
|
2783
|
+
if (typeof result !== "object" || result === null) {
|
|
2784
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result must be an object`);
|
|
2785
|
+
}
|
|
2786
|
+
const isError = result.is_error ?? false;
|
|
2787
|
+
if (typeof isError !== "boolean") {
|
|
2788
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: is_error must be a boolean`);
|
|
2789
|
+
}
|
|
2790
|
+
// `WireToolResult.content` is mandatory on the wire; a frame without
|
|
2791
|
+
// it is malformed and must not be coalesced into an empty transcript.
|
|
2792
|
+
if (result.content === undefined || result.content === null) {
|
|
2793
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result missing content`);
|
|
2794
|
+
}
|
|
2795
|
+
return {
|
|
2796
|
+
toolUseId: MeerkatClient.requireStringField(result, "tool_use_id", context),
|
|
2797
|
+
content: MeerkatClient.parseContentInput(result.content),
|
|
2798
|
+
isError,
|
|
2799
|
+
};
|
|
2800
|
+
}),
|
|
2862
2801
|
raw: { ...data },
|
|
2863
2802
|
};
|
|
2864
2803
|
}
|
|
@@ -2891,13 +2830,6 @@ export class MeerkatClient {
|
|
|
2891
2830
|
payload.content = camel.content;
|
|
2892
2831
|
}
|
|
2893
2832
|
}
|
|
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
2833
|
if (camel.stopReason !== undefined) {
|
|
2902
2834
|
payload.stop_reason = camel.stopReason;
|
|
2903
2835
|
}
|
|
@@ -2981,13 +2913,17 @@ export class MeerkatClient {
|
|
|
2981
2913
|
return { type: "text", text: "" };
|
|
2982
2914
|
}
|
|
2983
2915
|
static parseSessionAssistantBlock(data) {
|
|
2916
|
+
const context = "Invalid session assistant block";
|
|
2917
|
+
if (data.data != null && (typeof data.data !== "object" || Array.isArray(data.data))) {
|
|
2918
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: data must be an object`);
|
|
2919
|
+
}
|
|
2984
2920
|
const blockData = data.data ?? {};
|
|
2985
2921
|
const blobRef = blockData.blob_ref;
|
|
2986
2922
|
const revisedPrompt = blockData.revised_prompt != null && typeof blockData.revised_prompt === "object"
|
|
2987
2923
|
? blockData.revised_prompt
|
|
2988
2924
|
: undefined;
|
|
2989
2925
|
return {
|
|
2990
|
-
blockType:
|
|
2926
|
+
blockType: MeerkatClient.requireStringField(data, "block_type", context),
|
|
2991
2927
|
text: blockData.text != null ? String(blockData.text) : undefined,
|
|
2992
2928
|
id: blockData.id != null ? String(blockData.id) : undefined,
|
|
2993
2929
|
name: blockData.name != null ? String(blockData.name) : undefined,
|