@rkat/sdk 0.7.31 → 0.8.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/dist/client.d.ts +92 -18
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +863 -149
- package/dist/client.js.map +1 -1
- package/dist/event-envelope.d.ts +5 -0
- package/dist/event-envelope.d.ts.map +1 -0
- package/dist/event-envelope.js +81 -0
- package/dist/event-envelope.js.map +1 -0
- package/dist/events.d.ts +41 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +125 -31
- package/dist/events.js.map +1 -1
- package/dist/generated/errors.d.ts +56 -0
- package/dist/generated/errors.d.ts.map +1 -1
- package/dist/generated/errors.js +111 -0
- package/dist/generated/errors.js.map +1 -1
- package/dist/generated/types.d.ts +1534 -479
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/generated/types.js +4 -4
- package/dist/generated/types.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mob.d.ts +56 -3
- package/dist/mob.d.ts.map +1 -1
- package/dist/mob.js +67 -0
- package/dist/mob.js.map +1 -1
- package/dist/session.d.ts +7 -6
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +1 -1
- package/dist/session.js.map +1 -1
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +4 -3
- package/dist/streaming.js.map +1 -1
- package/dist/types.d.ts +136 -81
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -32,12 +32,12 @@ import path from "node:path";
|
|
|
32
32
|
import { setTimeout as delay } from "node:timers/promises";
|
|
33
33
|
import { createInterface } from "node:readline";
|
|
34
34
|
import { Buffer } from "node:buffer";
|
|
35
|
-
import { MeerkatError, CapabilityUnavailableError } from "./generated/errors.js";
|
|
35
|
+
import { MeerkatError, CapabilityUnavailableError, meerkatErrorFromJsonRpcCode, } from "./generated/errors.js";
|
|
36
36
|
import { isCompatibleWith } from "./generated/version_compat.js";
|
|
37
37
|
import { CONTRACT_VERSION, } from "./generated/types.js";
|
|
38
38
|
import { DeferredSession, Session } from "./session.js";
|
|
39
39
|
import { Mob, } from "./mob.js";
|
|
40
|
-
import {
|
|
40
|
+
import { parseAgentEventEnvelope } from "./event-envelope.js";
|
|
41
41
|
import { EventStream, AsyncQueue } from "./streaming.js";
|
|
42
42
|
import { EventSubscription } from "./subscription.js";
|
|
43
43
|
import { parseAttentionListResult, parseGoalStatusResult, parseWorkGraphEvent, parseWorkGraphSnapshot, parseWorkItem, } from "./generated/types.js";
|
|
@@ -53,6 +53,7 @@ const MOB_SPAWN_MANY_FAILURE_CAUSES = new Set([
|
|
|
53
53
|
"wiring_error",
|
|
54
54
|
"bridge_command_rejected",
|
|
55
55
|
"member_restore_failed",
|
|
56
|
+
"missing_member_capability",
|
|
56
57
|
"kickoff_wait_timed_out",
|
|
57
58
|
"ready_wait_timed_out",
|
|
58
59
|
"definition_error",
|
|
@@ -104,6 +105,18 @@ function skillRefsToWire(refs) {
|
|
|
104
105
|
const keys = skillKeysToWire(refs);
|
|
105
106
|
return keys?.map((key) => ({ kind: "structured", ...key }));
|
|
106
107
|
}
|
|
108
|
+
const MOB_CONTROL_SCOPES = new Set([
|
|
109
|
+
"list",
|
|
110
|
+
"read_history",
|
|
111
|
+
"subscribe_events",
|
|
112
|
+
"send_command",
|
|
113
|
+
"cancel",
|
|
114
|
+
"retire",
|
|
115
|
+
"wire_topology",
|
|
116
|
+
"live",
|
|
117
|
+
"admin_host",
|
|
118
|
+
"admin_grants",
|
|
119
|
+
]);
|
|
107
120
|
function setIfDefined(payload, key, value) {
|
|
108
121
|
if (value !== undefined) {
|
|
109
122
|
payload[key] = value;
|
|
@@ -121,12 +134,12 @@ function mobSpawnPayload(mobId, spec) {
|
|
|
121
134
|
setIfDefined(payload, "labels", spec.labels);
|
|
122
135
|
setIfDefined(payload, "context", spec.context);
|
|
123
136
|
setIfDefined(payload, "additional_instructions", spec.additionalInstructions);
|
|
137
|
+
setIfDefined(payload, "placement", spec.placement);
|
|
124
138
|
setIfDefined(payload, "binding", spec.binding);
|
|
125
139
|
setIfDefined(payload, "shell_env", spec.shellEnv);
|
|
126
140
|
setIfDefined(payload, "auto_wire_parent", spec.autoWireParent);
|
|
127
141
|
setIfDefined(payload, "launch_mode", spec.launchMode);
|
|
128
142
|
setIfDefined(payload, "tool_access_policy", spec.toolAccessPolicy);
|
|
129
|
-
setIfDefined(payload, "budget_split_policy", spec.budgetSplitPolicy);
|
|
130
143
|
setIfDefined(payload, "inherited_tool_filter", spec.inheritedToolFilter);
|
|
131
144
|
setIfDefined(payload, "override_profile", spec.overrideProfile);
|
|
132
145
|
setIfDefined(payload, "model_override", spec.modelOverride);
|
|
@@ -144,6 +157,7 @@ function mobSpawnManySpecPayload(spec) {
|
|
|
144
157
|
setIfDefined(payload, "labels", spec.labels);
|
|
145
158
|
setIfDefined(payload, "context", spec.context);
|
|
146
159
|
setIfDefined(payload, "additional_instructions", spec.additionalInstructions);
|
|
160
|
+
setIfDefined(payload, "placement", spec.placement);
|
|
147
161
|
setIfDefined(payload, "auth_binding", spec.authBinding);
|
|
148
162
|
setIfDefined(payload, "model_override", spec.modelOverride);
|
|
149
163
|
return payload;
|
|
@@ -171,6 +185,7 @@ function mobTurnStartPayload(mobId, agentIdentity, prompt, options) {
|
|
|
171
185
|
setIfDefined(payload, "keep_alive", options?.keepAlive);
|
|
172
186
|
setIfDefined(payload, "model", options?.model);
|
|
173
187
|
setIfDefined(payload, "provider", options?.provider);
|
|
188
|
+
setIfDefined(payload, "self_hosted_server_id", options?.selfHostedServerId);
|
|
174
189
|
setIfDefined(payload, "max_tokens", options?.maxTokens);
|
|
175
190
|
setIfDefined(payload, "system_prompt", options?.systemPrompt);
|
|
176
191
|
setIfDefined(payload, "output_schema", options?.outputSchema);
|
|
@@ -620,7 +635,8 @@ export class MeerkatClient {
|
|
|
620
635
|
params.idempotency_key = options.idempotencyKey;
|
|
621
636
|
}
|
|
622
637
|
const result = await this.request("session/inject_context", params);
|
|
623
|
-
|
|
638
|
+
const status = MeerkatClient.requireClosedStringField(result, "status", ["applied", "staged", "duplicate"], "Invalid session/inject_context response");
|
|
639
|
+
return { status };
|
|
624
640
|
}
|
|
625
641
|
/**
|
|
626
642
|
* Read an input's stored runtime state (terminal outcome, run
|
|
@@ -682,6 +698,9 @@ export class MeerkatClient {
|
|
|
682
698
|
if (options?.runningBehavior !== undefined) {
|
|
683
699
|
params.running_behavior = options.runningBehavior;
|
|
684
700
|
}
|
|
701
|
+
if (options?.toolAccessPolicy !== undefined) {
|
|
702
|
+
params.tool_access_policy = options.toolAccessPolicy;
|
|
703
|
+
}
|
|
685
704
|
const raw = await this.request("session/fork_at", params);
|
|
686
705
|
return MeerkatClient.parseSessionForkResult(raw);
|
|
687
706
|
}
|
|
@@ -694,6 +713,9 @@ export class MeerkatClient {
|
|
|
694
713
|
if (options?.runningBehavior !== undefined) {
|
|
695
714
|
params.running_behavior = options.runningBehavior;
|
|
696
715
|
}
|
|
716
|
+
if (options?.toolAccessPolicy !== undefined) {
|
|
717
|
+
params.tool_access_policy = options.toolAccessPolicy;
|
|
718
|
+
}
|
|
697
719
|
const raw = await this.request("session/fork_replace", params);
|
|
698
720
|
return MeerkatClient.parseSessionForkResult(raw);
|
|
699
721
|
}
|
|
@@ -1133,6 +1155,232 @@ export class MeerkatClient {
|
|
|
1133
1155
|
agent_identity: agentIdentity,
|
|
1134
1156
|
});
|
|
1135
1157
|
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Record (full-replace) a principal's control-scope grant
|
|
1160
|
+
* (`mob/grant_scopes`).
|
|
1161
|
+
*/
|
|
1162
|
+
async grantMobScopes(mobId, principal, scopes, expiresAtMs) {
|
|
1163
|
+
const result = await this.request("mob/grant_scopes", {
|
|
1164
|
+
mob_id: mobId,
|
|
1165
|
+
principal,
|
|
1166
|
+
scopes,
|
|
1167
|
+
...(expiresAtMs !== undefined ? { expires_at_ms: expiresAtMs } : {}),
|
|
1168
|
+
});
|
|
1169
|
+
const context = "Invalid mob/grant_scopes response";
|
|
1170
|
+
const record = MeerkatClient.requireRecord(result.record, "record", context);
|
|
1171
|
+
return MeerkatClient.decodeMobGrantRecord(record, context);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Revoke scopes from a principal's grant (`mob/revoke_scopes`); omit
|
|
1175
|
+
* `scopes` to revoke the entire grant. Returns whether the grant record
|
|
1176
|
+
* was removed entirely — revoking never-granted scopes is an idempotent
|
|
1177
|
+
* no-op (`false`).
|
|
1178
|
+
*/
|
|
1179
|
+
async revokeMobScopes(mobId, principal, scopes) {
|
|
1180
|
+
const result = await this.request("mob/revoke_scopes", {
|
|
1181
|
+
mob_id: mobId,
|
|
1182
|
+
principal,
|
|
1183
|
+
...(scopes !== undefined ? { scopes } : {}),
|
|
1184
|
+
});
|
|
1185
|
+
if (typeof result.removed !== "boolean") {
|
|
1186
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/revoke_scopes response: missing removed");
|
|
1187
|
+
}
|
|
1188
|
+
return result.removed;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* List raw control-scope grant records (`mob/grants`). Expired rows
|
|
1192
|
+
* preserve the generated `expires_at_ms` wire field verbatim.
|
|
1193
|
+
*/
|
|
1194
|
+
async listMobGrants(mobId) {
|
|
1195
|
+
const result = await this.request("mob/grants", { mob_id: mobId });
|
|
1196
|
+
const context = "Invalid mob/grants response";
|
|
1197
|
+
const grants = result.grants;
|
|
1198
|
+
if (!Array.isArray(grants)) {
|
|
1199
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing grants`);
|
|
1200
|
+
}
|
|
1201
|
+
return grants.map((entry) => MeerkatClient.decodeMobGrantRecord(MeerkatClient.requireObject(entry, context), context));
|
|
1202
|
+
}
|
|
1203
|
+
static requireObject(value, context) {
|
|
1204
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1205
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: expected an object entry`);
|
|
1206
|
+
}
|
|
1207
|
+
return value;
|
|
1208
|
+
}
|
|
1209
|
+
static decodeMobGrantRecord(record, context) {
|
|
1210
|
+
const principal = MeerkatClient.requireStringField(record, "principal", context);
|
|
1211
|
+
const scopes = record.scopes;
|
|
1212
|
+
if (!Array.isArray(scopes) ||
|
|
1213
|
+
scopes.some((scope) => typeof scope !== "string" ||
|
|
1214
|
+
!MOB_CONTROL_SCOPES.has(scope))) {
|
|
1215
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: record scopes must use the closed control-scope vocabulary`);
|
|
1216
|
+
}
|
|
1217
|
+
let expiresAtMs;
|
|
1218
|
+
if (Object.prototype.hasOwnProperty.call(record, "expires_at_ms")) {
|
|
1219
|
+
expiresAtMs = MeerkatClient.requireNonNegativeIntegerField(record, "expires_at_ms", context);
|
|
1220
|
+
}
|
|
1221
|
+
return {
|
|
1222
|
+
principal,
|
|
1223
|
+
scopes: scopes,
|
|
1224
|
+
...(expiresAtMs !== undefined ? { expires_at_ms: expiresAtMs } : {}),
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
// ─── Multi-host console verbs (phase 7, DEC-P7A-9) ────────────────────
|
|
1228
|
+
/**
|
|
1229
|
+
* Read a mob member transcript page by identity (`mob/member_history`).
|
|
1230
|
+
* One shape local and remote; the envelope carries typed placement +
|
|
1231
|
+
* provenance facts.
|
|
1232
|
+
*/
|
|
1233
|
+
async mobMemberHistory(mobId, agentIdentity, opts) {
|
|
1234
|
+
const result = await this.request("mob/member_history", {
|
|
1235
|
+
mob_id: mobId,
|
|
1236
|
+
agent_identity: agentIdentity,
|
|
1237
|
+
...(opts?.fromIndex !== undefined ? { from_index: opts.fromIndex } : {}),
|
|
1238
|
+
...(opts?.limit !== undefined ? { limit: opts.limit } : {}),
|
|
1239
|
+
});
|
|
1240
|
+
const context = "Invalid mob/member_history response";
|
|
1241
|
+
const page = MeerkatClient.requireRecord(result.page, "page", context);
|
|
1242
|
+
const messages = MeerkatClient.requireRecordArray(page.messages, `${context}: page.messages`);
|
|
1243
|
+
messages.forEach((message, index) => MeerkatClient.validateWireHistoryRow(message, `${context}: page.messages[${index}]`));
|
|
1244
|
+
MeerkatClient.requireBooleanField(page, "complete", `${context}: page`);
|
|
1245
|
+
const fromIndex = MeerkatClient.requireNonNegativeIntegerField(page, "from_index", `${context}: page`);
|
|
1246
|
+
const messageCount = MeerkatClient.requireNonNegativeIntegerField(page, "message_count", `${context}: page`);
|
|
1247
|
+
const hasNextIndex = Object.prototype.hasOwnProperty.call(page, "next_index");
|
|
1248
|
+
if (hasNextIndex) {
|
|
1249
|
+
MeerkatClient.requireNonNegativeIntegerField(page, "next_index", `${context}: page`);
|
|
1250
|
+
}
|
|
1251
|
+
const pageEnd = fromIndex + messages.length;
|
|
1252
|
+
if (pageEnd > messageCount) {
|
|
1253
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: page ends beyond message_count`);
|
|
1254
|
+
}
|
|
1255
|
+
const complete = page.complete;
|
|
1256
|
+
if (complete !== (pageEnd === messageCount)) {
|
|
1257
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: complete must match whether the page reaches message_count`);
|
|
1258
|
+
}
|
|
1259
|
+
if (complete && hasNextIndex) {
|
|
1260
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: complete page must not carry next_index`);
|
|
1261
|
+
}
|
|
1262
|
+
if (!complete) {
|
|
1263
|
+
if (messages.length === 0) {
|
|
1264
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: incomplete page must make progress`);
|
|
1265
|
+
}
|
|
1266
|
+
if (!hasNextIndex || page.next_index !== pageEnd) {
|
|
1267
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: incomplete page next_index must equal served end`);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
MeerkatClient.requireNonNegativeIntegerField(result, "generation", context);
|
|
1271
|
+
const provenance = MeerkatClient.requireStringField(result, "provenance", context);
|
|
1272
|
+
if (provenance !== "host_claimed" && provenance !== "controlling_host_verified") {
|
|
1273
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported provenance ${JSON.stringify(provenance)}`);
|
|
1274
|
+
}
|
|
1275
|
+
const placement = MeerkatClient.optionalStringField(result, "placement", context);
|
|
1276
|
+
if (placement === "") {
|
|
1277
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: placement must be non-empty`);
|
|
1278
|
+
}
|
|
1279
|
+
return result;
|
|
1280
|
+
}
|
|
1281
|
+
/** List tracked member hosts with bind phase and declared capabilities. */
|
|
1282
|
+
async mobHosts(mobId) {
|
|
1283
|
+
const result = await this.request("mob/hosts", { mob_id: mobId });
|
|
1284
|
+
const hosts = result.hosts;
|
|
1285
|
+
if (!Array.isArray(hosts)) {
|
|
1286
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/hosts response: missing hosts");
|
|
1287
|
+
}
|
|
1288
|
+
return hosts.map((row, index) => MeerkatClient.parseMobHostStatus(row, `Invalid mob/hosts response: hosts[${index}]`));
|
|
1289
|
+
}
|
|
1290
|
+
/** Outstanding cross-host route-install obligations. */
|
|
1291
|
+
async mobRouteInstalls(mobId) {
|
|
1292
|
+
const result = await this.request("mob/route_installs", { mob_id: mobId });
|
|
1293
|
+
const context = "Invalid mob/route_installs response";
|
|
1294
|
+
const outstanding = MeerkatClient.requireRecordArray(result.outstanding, `${context}: outstanding`);
|
|
1295
|
+
outstanding.forEach((row, index) => MeerkatClient.parseRouteInstallObligation(row, `${context}: outstanding[${index}]`));
|
|
1296
|
+
MeerkatClient.requireBooleanField(result, "complete", context);
|
|
1297
|
+
return result;
|
|
1298
|
+
}
|
|
1299
|
+
/** Bind a member-host daemon from its binding descriptor. */
|
|
1300
|
+
async bindMobHost(mobId, descriptor) {
|
|
1301
|
+
const result = await this.request("mob/bind_host", {
|
|
1302
|
+
mob_id: mobId,
|
|
1303
|
+
descriptor,
|
|
1304
|
+
});
|
|
1305
|
+
const context = "Invalid mob/bind_host response";
|
|
1306
|
+
MeerkatClient.requireStringField(result, "host_id", context);
|
|
1307
|
+
MeerkatClient.requireNonNegativeIntegerField(result, "authority_epoch", context);
|
|
1308
|
+
const capabilities = MeerkatClient.parseMobHostCapabilities(result.capabilities, `${context}: capabilities`);
|
|
1309
|
+
return { ...result, capabilities };
|
|
1310
|
+
}
|
|
1311
|
+
/** Revoke a bound (or bind-requested) member host. */
|
|
1312
|
+
async revokeMobHost(mobId, hostId) {
|
|
1313
|
+
const result = await this.request("mob/revoke_host", {
|
|
1314
|
+
mob_id: mobId,
|
|
1315
|
+
host_id: hostId,
|
|
1316
|
+
});
|
|
1317
|
+
const context = "Invalid mob/revoke_host response";
|
|
1318
|
+
MeerkatClient.requireStringField(result, "host_id", context);
|
|
1319
|
+
MeerkatClient.requireStringArray(result.released_members, `${context}: released_members`);
|
|
1320
|
+
return result;
|
|
1321
|
+
}
|
|
1322
|
+
/** Hard-cancel a mob member; `reason` is required. */
|
|
1323
|
+
async hardCancelMobMember(mobId, agentIdentity, reason) {
|
|
1324
|
+
const result = await this.request("mob/hard_cancel_member", {
|
|
1325
|
+
mob_id: mobId,
|
|
1326
|
+
agent_identity: agentIdentity,
|
|
1327
|
+
reason,
|
|
1328
|
+
});
|
|
1329
|
+
if (typeof result.cancelled !== "boolean") {
|
|
1330
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/hard_cancel_member response: missing cancelled");
|
|
1331
|
+
}
|
|
1332
|
+
return result.cancelled;
|
|
1333
|
+
}
|
|
1334
|
+
/** Open a live realtime channel on a mob member. */
|
|
1335
|
+
async openMobMemberLive(mobId, agentIdentity, opts) {
|
|
1336
|
+
const result = await this.request("mob/member_live_open", {
|
|
1337
|
+
mob_id: mobId,
|
|
1338
|
+
agent_identity: agentIdentity,
|
|
1339
|
+
...(opts?.turningMode !== undefined ? { turning_mode: opts.turningMode } : {}),
|
|
1340
|
+
...(opts?.transport !== undefined ? { transport: opts.transport } : {}),
|
|
1341
|
+
});
|
|
1342
|
+
return MeerkatClient.parseLiveOpenResult(result, "Invalid mob/member_live_open response");
|
|
1343
|
+
}
|
|
1344
|
+
/** Close one named live channel on a mob member. */
|
|
1345
|
+
async closeMobMemberLive(mobId, agentIdentity, channelId) {
|
|
1346
|
+
const result = await this.request("mob/member_live_close", {
|
|
1347
|
+
mob_id: mobId,
|
|
1348
|
+
agent_identity: agentIdentity,
|
|
1349
|
+
channel_id: channelId,
|
|
1350
|
+
});
|
|
1351
|
+
return MeerkatClient.parseLiveCloseResult(result);
|
|
1352
|
+
}
|
|
1353
|
+
/** Read live channel status for a mob member. */
|
|
1354
|
+
async mobMemberLiveStatus(mobId, agentIdentity, channelId) {
|
|
1355
|
+
const result = await this.request("mob/member_live_status", {
|
|
1356
|
+
mob_id: mobId,
|
|
1357
|
+
agent_identity: agentIdentity,
|
|
1358
|
+
...(channelId !== undefined ? { channel_id: channelId } : {}),
|
|
1359
|
+
});
|
|
1360
|
+
return MeerkatClient.parseLiveStatusResult(result, "Invalid mob/member_live_status response");
|
|
1361
|
+
}
|
|
1362
|
+
/** Drive one turn-level live control verb on a member channel. */
|
|
1363
|
+
async controlMobMemberLive(mobId, agentIdentity, channelId, verb) {
|
|
1364
|
+
const result = await this.request("mob/member_live_control", {
|
|
1365
|
+
mob_id: mobId,
|
|
1366
|
+
agent_identity: agentIdentity,
|
|
1367
|
+
channel_id: channelId,
|
|
1368
|
+
verb,
|
|
1369
|
+
});
|
|
1370
|
+
const context = "Invalid mob/member_live_control response";
|
|
1371
|
+
const resultVerb = MeerkatClient.requireStringField(result, "verb", context);
|
|
1372
|
+
const status = MeerkatClient.requireStringField(result, "status", context);
|
|
1373
|
+
const expectedStatus = {
|
|
1374
|
+
commit_input: "committed",
|
|
1375
|
+
interrupt: "interrupted",
|
|
1376
|
+
truncate: "truncated",
|
|
1377
|
+
refresh: "queued",
|
|
1378
|
+
};
|
|
1379
|
+
if (expectedStatus[resultVerb] !== status || resultVerb !== verb.verb) {
|
|
1380
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: mismatched verb/status outcome`);
|
|
1381
|
+
}
|
|
1382
|
+
return result;
|
|
1383
|
+
}
|
|
1136
1384
|
async respawnMobMember(mobId, agentIdentity, initialMessage) {
|
|
1137
1385
|
const result = await this.request("mob/respawn", {
|
|
1138
1386
|
mob_id: mobId,
|
|
@@ -1206,6 +1454,15 @@ export class MeerkatClient {
|
|
|
1206
1454
|
if (result.progress !== undefined && result.progress !== null) {
|
|
1207
1455
|
snapshot.progress = MeerkatClient.parseMemberProgressSnapshot(result.progress, "Invalid mob/member_status response");
|
|
1208
1456
|
}
|
|
1457
|
+
snapshot.placement = MeerkatClient.optionalStringField(result, "placement", "Invalid mob/member_status response");
|
|
1458
|
+
snapshot.controlReachability = MeerkatClient.parseWireReachability(result.control_reachability, "control_reachability");
|
|
1459
|
+
snapshot.commsReachability = MeerkatClient.parseWireReachability(result.comms_reachability, "comms_reachability");
|
|
1460
|
+
if (result.last_seen_ms != null) {
|
|
1461
|
+
snapshot.lastSeenMs = MeerkatClient.requireNonNegativeIntegerField(result, "last_seen_ms", "Invalid mob/member_status response");
|
|
1462
|
+
}
|
|
1463
|
+
snapshot.freshnessReason = MeerkatClient.optionalStringField(result, "freshness_reason", "Invalid mob/member_status response");
|
|
1464
|
+
snapshot.lifecycleCapabilities = MeerkatClient.parseMemberLifecycleCapabilities(result.lifecycle_capabilities);
|
|
1465
|
+
snapshot.nonPortableDisabled = MeerkatClient.parseNonPortableDisabled(result.non_portable_disabled);
|
|
1209
1466
|
return snapshot;
|
|
1210
1467
|
}
|
|
1211
1468
|
/**
|
|
@@ -1488,9 +1745,14 @@ export class MeerkatClient {
|
|
|
1488
1745
|
params.limit = options.limit;
|
|
1489
1746
|
}
|
|
1490
1747
|
const result = await this.request("mob/events", params);
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1748
|
+
if (typeof result !== "object" ||
|
|
1749
|
+
result === null ||
|
|
1750
|
+
Array.isArray(result) ||
|
|
1751
|
+
!Object.hasOwn(result, "events") ||
|
|
1752
|
+
!Array.isArray(result.events)) {
|
|
1753
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/events response: events must be an array", result);
|
|
1754
|
+
}
|
|
1755
|
+
const events = result.events;
|
|
1494
1756
|
return { events };
|
|
1495
1757
|
}
|
|
1496
1758
|
async mobIngressInteraction(params) {
|
|
@@ -1616,53 +1878,7 @@ export class MeerkatClient {
|
|
|
1616
1878
|
});
|
|
1617
1879
|
}
|
|
1618
1880
|
static parseAgentEventEnvelope(raw) {
|
|
1619
|
-
|
|
1620
|
-
const source = MeerkatClient.parseEventSourceIdentity(raw.source);
|
|
1621
|
-
const seq = MeerkatClient.parseOptionalNumber(raw.seq);
|
|
1622
|
-
const timestampMs = MeerkatClient.parseOptionalNumber(raw.timestamp_ms ?? raw.timestampMs);
|
|
1623
|
-
const payloadRaw = raw.payload;
|
|
1624
|
-
const payload = payloadRaw && typeof payloadRaw === "object"
|
|
1625
|
-
? parseCoreEvent(payloadRaw)
|
|
1626
|
-
: undefined;
|
|
1627
|
-
return {
|
|
1628
|
-
...(eventId != null ? { eventId } : {}),
|
|
1629
|
-
...(source != null ? { source } : {}),
|
|
1630
|
-
...(seq != null ? { seq } : {}),
|
|
1631
|
-
...(timestampMs != null ? { timestampMs } : {}),
|
|
1632
|
-
...(payload ? { payload } : {}),
|
|
1633
|
-
};
|
|
1634
|
-
}
|
|
1635
|
-
static parseEventSourceIdentity(raw) {
|
|
1636
|
-
if (!raw || typeof raw !== "object") {
|
|
1637
|
-
return undefined;
|
|
1638
|
-
}
|
|
1639
|
-
const record = raw;
|
|
1640
|
-
const type = MeerkatClient.parseOptionalString(record.type);
|
|
1641
|
-
switch (type) {
|
|
1642
|
-
case "session": {
|
|
1643
|
-
const sessionId = MeerkatClient.parseOptionalString(record.session_id ?? record.sessionId);
|
|
1644
|
-
return sessionId != null ? { type: "session", sessionId } : undefined;
|
|
1645
|
-
}
|
|
1646
|
-
case "runtime": {
|
|
1647
|
-
const runtimeId = MeerkatClient.parseOptionalString(record.runtime_id ?? record.runtimeId);
|
|
1648
|
-
return runtimeId != null ? { type: "runtime", runtimeId } : undefined;
|
|
1649
|
-
}
|
|
1650
|
-
case "interaction": {
|
|
1651
|
-
const interactionId = MeerkatClient.parseOptionalString(record.interaction_id ?? record.interactionId);
|
|
1652
|
-
return interactionId != null ? { type: "interaction", interactionId } : undefined;
|
|
1653
|
-
}
|
|
1654
|
-
case "callback":
|
|
1655
|
-
return { type: "callback" };
|
|
1656
|
-
case "external": {
|
|
1657
|
-
const sourceId = MeerkatClient.parseOptionalString(record.source_id ?? record.sourceId);
|
|
1658
|
-
return sourceId != null ? { type: "external", sourceId } : undefined;
|
|
1659
|
-
}
|
|
1660
|
-
default:
|
|
1661
|
-
return undefined;
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
static parseOptionalString(raw) {
|
|
1665
|
-
return typeof raw === "string" ? raw : undefined;
|
|
1881
|
+
return parseAgentEventEnvelope(raw);
|
|
1666
1882
|
}
|
|
1667
1883
|
static parseRequiredString(raw, context) {
|
|
1668
1884
|
if (typeof raw === "string" && raw.length > 0) {
|
|
@@ -1670,9 +1886,6 @@ export class MeerkatClient {
|
|
|
1670
1886
|
}
|
|
1671
1887
|
throw new MeerkatError("INVALID_RESPONSE", context);
|
|
1672
1888
|
}
|
|
1673
|
-
static parseOptionalNumber(raw) {
|
|
1674
|
-
return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
|
|
1675
|
-
}
|
|
1676
1889
|
static parseAttributedMobEvent(raw) {
|
|
1677
1890
|
const context = "Invalid attributed mob event";
|
|
1678
1891
|
// The runtime wire shape (meerkat-mob AttributedEvent) carries a typed
|
|
@@ -1688,7 +1901,7 @@ export class MeerkatClient {
|
|
|
1688
1901
|
const source = MeerkatClient.requireRecord(raw.source, "source", context);
|
|
1689
1902
|
const identity = MeerkatClient.requireStringField(source, "identity", context);
|
|
1690
1903
|
const generation = MeerkatClient.requireNumberField(source, "generation", context);
|
|
1691
|
-
if (!Number.
|
|
1904
|
+
if (!Number.isSafeInteger(generation) || generation < 0) {
|
|
1692
1905
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: source generation must be a non-negative integer`);
|
|
1693
1906
|
}
|
|
1694
1907
|
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
@@ -1724,10 +1937,14 @@ export class MeerkatClient {
|
|
|
1724
1937
|
params.model = options.model;
|
|
1725
1938
|
if (options?.provider)
|
|
1726
1939
|
params.provider = options.provider;
|
|
1940
|
+
if (options?.selfHostedServerId != null) {
|
|
1941
|
+
params.self_hosted_server_id = options.selfHostedServerId;
|
|
1942
|
+
}
|
|
1727
1943
|
if (options?.maxTokens)
|
|
1728
1944
|
params.max_tokens = options.maxTokens;
|
|
1729
|
-
if (options?.systemPrompt)
|
|
1945
|
+
if (options?.systemPrompt !== undefined) {
|
|
1730
1946
|
params.system_prompt = options.systemPrompt;
|
|
1947
|
+
}
|
|
1731
1948
|
if (options?.outputSchema)
|
|
1732
1949
|
params.output_schema = options.outputSchema;
|
|
1733
1950
|
if (options?.structuredOutputRetries != null) {
|
|
@@ -1771,10 +1988,14 @@ export class MeerkatClient {
|
|
|
1771
1988
|
params.model = options.model;
|
|
1772
1989
|
if (options?.provider)
|
|
1773
1990
|
params.provider = options.provider;
|
|
1991
|
+
if (options?.selfHostedServerId != null) {
|
|
1992
|
+
params.self_hosted_server_id = options.selfHostedServerId;
|
|
1993
|
+
}
|
|
1774
1994
|
if (options?.maxTokens)
|
|
1775
1995
|
params.max_tokens = options.maxTokens;
|
|
1776
|
-
if (options?.systemPrompt)
|
|
1996
|
+
if (options?.systemPrompt !== undefined) {
|
|
1777
1997
|
params.system_prompt = options.systemPrompt;
|
|
1998
|
+
}
|
|
1778
1999
|
if (options?.outputSchema)
|
|
1779
2000
|
params.output_schema = options.outputSchema;
|
|
1780
2001
|
if (options?.structuredOutputRetries != null) {
|
|
@@ -1865,7 +2086,11 @@ export class MeerkatClient {
|
|
|
1865
2086
|
return MeerkatClient.parseCommsSendReceipt(result);
|
|
1866
2087
|
}
|
|
1867
2088
|
async peers(sessionId) {
|
|
1868
|
-
|
|
2089
|
+
const result = await this.request("comms/peers", { session_id: sessionId });
|
|
2090
|
+
if (!Array.isArray(result.peers)) {
|
|
2091
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid comms/peers response: missing peers");
|
|
2092
|
+
}
|
|
2093
|
+
return { peers: result.peers };
|
|
1869
2094
|
}
|
|
1870
2095
|
/** Idempotent spawn: spawns or returns the existing member entry. */
|
|
1871
2096
|
async mobEnsureMember(mobId, spec) {
|
|
@@ -1895,7 +2120,7 @@ export class MeerkatClient {
|
|
|
1895
2120
|
// schemas regenerated into `./generated/types.ts`. See I52/I53.
|
|
1896
2121
|
async liveOpen(params) {
|
|
1897
2122
|
const result = await this.request("live/open", params);
|
|
1898
|
-
return result;
|
|
2123
|
+
return MeerkatClient.parseLiveOpenResult(result, "Invalid live/open response");
|
|
1899
2124
|
}
|
|
1900
2125
|
async liveWebrtcAnswer(params) {
|
|
1901
2126
|
const result = await this.request("live/webrtc/answer", params);
|
|
@@ -1903,7 +2128,7 @@ export class MeerkatClient {
|
|
|
1903
2128
|
}
|
|
1904
2129
|
async liveStatus(params) {
|
|
1905
2130
|
const result = await this.request("live/status", params);
|
|
1906
|
-
return result;
|
|
2131
|
+
return MeerkatClient.parseLiveStatusResult(result, "Invalid live/status response");
|
|
1907
2132
|
}
|
|
1908
2133
|
async liveClose(params) {
|
|
1909
2134
|
const result = await this.request("live/close", params);
|
|
@@ -2132,7 +2357,18 @@ export class MeerkatClient {
|
|
|
2132
2357
|
const error = data.error;
|
|
2133
2358
|
if (error) {
|
|
2134
2359
|
const normalized = MeerkatClient.parseRpcErrorPayload(error);
|
|
2135
|
-
|
|
2360
|
+
const errorData = typeof error.data === "object" &&
|
|
2361
|
+
error.data !== null &&
|
|
2362
|
+
!Array.isArray(error.data)
|
|
2363
|
+
? error.data
|
|
2364
|
+
: undefined;
|
|
2365
|
+
const semanticCode = typeof errorData?.code === "string" && errorData.code.length > 0
|
|
2366
|
+
? errorData.code
|
|
2367
|
+
: undefined;
|
|
2368
|
+
const rpcCode = typeof error.code === "number" || typeof error.code === "string"
|
|
2369
|
+
? error.code
|
|
2370
|
+
: "UNKNOWN";
|
|
2371
|
+
pending.reject(meerkatErrorFromJsonRpcCode(rpcCode, semanticCode, normalized.message, normalized.details));
|
|
2136
2372
|
}
|
|
2137
2373
|
else {
|
|
2138
2374
|
pending.resolve((data.result ?? {}));
|
|
@@ -2177,20 +2413,21 @@ export class MeerkatClient {
|
|
|
2177
2413
|
return;
|
|
2178
2414
|
}
|
|
2179
2415
|
const sessionId = String(params.session_id ?? "");
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2416
|
+
const rawEvent = params.event;
|
|
2417
|
+
const event = typeof rawEvent === "object" && rawEvent !== null && !Array.isArray(rawEvent)
|
|
2418
|
+
? rawEvent
|
|
2419
|
+
: { invalid_event_envelope: rawEvent };
|
|
2420
|
+
const queue = this.eventQueues.get(sessionId);
|
|
2421
|
+
if (queue) {
|
|
2422
|
+
queue.put(event);
|
|
2423
|
+
}
|
|
2424
|
+
else if (this.pendingStreamQueues.size > 0) {
|
|
2425
|
+
// A stream is pending but its session_id is not yet bound. Buffer by
|
|
2426
|
+
// session_id; the create response that binds this session_id drains
|
|
2427
|
+
// exactly this buffer into the matching request's queue.
|
|
2428
|
+
const buffered = this.unmatchedStreamBuffer.get(sessionId) ?? [];
|
|
2429
|
+
buffered.push(event);
|
|
2430
|
+
this.unmatchedStreamBuffer.set(sessionId, buffered);
|
|
2194
2431
|
}
|
|
2195
2432
|
}
|
|
2196
2433
|
}
|
|
@@ -2266,6 +2503,31 @@ export class MeerkatClient {
|
|
|
2266
2503
|
}
|
|
2267
2504
|
return value;
|
|
2268
2505
|
}
|
|
2506
|
+
static requireOwnField(raw, field, context) {
|
|
2507
|
+
if (!Object.prototype.hasOwnProperty.call(raw, field)) {
|
|
2508
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${field}`);
|
|
2509
|
+
}
|
|
2510
|
+
return raw[field];
|
|
2511
|
+
}
|
|
2512
|
+
static requireClosedStringField(raw, field, allowed, context) {
|
|
2513
|
+
const value = MeerkatClient.requirePresentStringField(raw, field, context);
|
|
2514
|
+
if (!allowed.includes(value)) {
|
|
2515
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported ${field} ${JSON.stringify(value)}`);
|
|
2516
|
+
}
|
|
2517
|
+
return value;
|
|
2518
|
+
}
|
|
2519
|
+
static validateOptionalStringField(raw, field, context) {
|
|
2520
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2521
|
+
typeof raw[field] !== "string") {
|
|
2522
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be string`);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
static validateOptionalBooleanField(raw, field, context) {
|
|
2526
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2527
|
+
typeof raw[field] !== "boolean") {
|
|
2528
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be boolean`);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2269
2531
|
static requireNumberField(raw, field, context, displayField = field) {
|
|
2270
2532
|
if (!(field in raw)) {
|
|
2271
2533
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${displayField}`);
|
|
@@ -2278,14 +2540,15 @@ export class MeerkatClient {
|
|
|
2278
2540
|
}
|
|
2279
2541
|
/**
|
|
2280
2542
|
* Require a wire count/total field (Rust `usize`/`u64`). A present value
|
|
2281
|
-
* that is fractional
|
|
2282
|
-
*
|
|
2283
|
-
* an any-finite-number.
|
|
2284
|
-
* and Python
|
|
2543
|
+
* that is fractional, negative, or outside JavaScript's lossless integer
|
|
2544
|
+
* range can never be represented faithfully, so it is a contract violation
|
|
2545
|
+
* and fails closed instead of being accepted as an any-finite-number.
|
|
2546
|
+
* Mirrors the web SDK `requireNonNegativeIntegerField` and Python
|
|
2547
|
+
* `_require_non_negative_integer_field`.
|
|
2285
2548
|
*/
|
|
2286
2549
|
static requireNonNegativeIntegerField(raw, field, context, displayField = field) {
|
|
2287
2550
|
const value = MeerkatClient.requireNumberField(raw, field, context, displayField);
|
|
2288
|
-
if (!Number.
|
|
2551
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
2289
2552
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${displayField} must be a non-negative integer`);
|
|
2290
2553
|
}
|
|
2291
2554
|
return value;
|
|
@@ -2307,6 +2570,367 @@ export class MeerkatClient {
|
|
|
2307
2570
|
}
|
|
2308
2571
|
return value;
|
|
2309
2572
|
}
|
|
2573
|
+
static validateGeneratedContentBlock(raw, context, systemNoticeContent) {
|
|
2574
|
+
const allowed = systemNoticeContent
|
|
2575
|
+
? ["text", "image", "video", "structured", "skill_context"]
|
|
2576
|
+
: ["text", "image", "video", "structured", "unknown"];
|
|
2577
|
+
const type = MeerkatClient.requireClosedStringField(raw, "type", allowed, context);
|
|
2578
|
+
if (type === "text") {
|
|
2579
|
+
MeerkatClient.requirePresentStringField(raw, "text", context);
|
|
2580
|
+
}
|
|
2581
|
+
else if (type === "image") {
|
|
2582
|
+
MeerkatClient.requirePresentStringField(raw, "media_type", context);
|
|
2583
|
+
}
|
|
2584
|
+
else if (type === "video") {
|
|
2585
|
+
MeerkatClient.requirePresentStringField(raw, "media_type", context);
|
|
2586
|
+
MeerkatClient.requireNonNegativeIntegerField(raw, "duration_ms", context);
|
|
2587
|
+
}
|
|
2588
|
+
else if (type === "structured") {
|
|
2589
|
+
MeerkatClient.requireOwnField(raw, "data", context);
|
|
2590
|
+
}
|
|
2591
|
+
else if (type === "skill_context") {
|
|
2592
|
+
const skillKey = MeerkatClient.requireRecord(raw.skill_key, "skill_key", context);
|
|
2593
|
+
MeerkatClient.requireStringField(skillKey, "source_uuid", `${context}: skill_key`);
|
|
2594
|
+
MeerkatClient.requireStringField(skillKey, "skill_name", `${context}: skill_key`);
|
|
2595
|
+
MeerkatClient.requirePresentStringField(raw, "text", context);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
static validateOptionalMetaRecord(raw, context) {
|
|
2599
|
+
if (Object.prototype.hasOwnProperty.call(raw, "meta")) {
|
|
2600
|
+
MeerkatClient.requireRecord(raw.meta, "meta", context);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
static validateToolConfigStatus(raw, context) {
|
|
2604
|
+
const status = MeerkatClient.requireRecord(raw, "status_info", context);
|
|
2605
|
+
const kind = MeerkatClient.requireClosedStringField(status, "kind", [
|
|
2606
|
+
"boundary_applied",
|
|
2607
|
+
"deferred_catalog_delta",
|
|
2608
|
+
"warning_failed_closed",
|
|
2609
|
+
"external_tool_delta",
|
|
2610
|
+
], context);
|
|
2611
|
+
if (kind === "boundary_applied") {
|
|
2612
|
+
MeerkatClient.requireBooleanField(status, "base_changed", context);
|
|
2613
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "revision", context);
|
|
2614
|
+
MeerkatClient.requireBooleanField(status, "visible_changed", context);
|
|
2615
|
+
}
|
|
2616
|
+
else if (kind === "deferred_catalog_delta") {
|
|
2617
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "added_hidden_count", context);
|
|
2618
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "pending_source_count", context);
|
|
2619
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "removed_hidden_count", context);
|
|
2620
|
+
}
|
|
2621
|
+
else if (kind === "warning_failed_closed") {
|
|
2622
|
+
MeerkatClient.requirePresentStringField(status, "error", context);
|
|
2623
|
+
}
|
|
2624
|
+
else {
|
|
2625
|
+
MeerkatClient.requireClosedStringField(status, "phase", ["pending", "applied", "draining", "forced", "failed"], context);
|
|
2626
|
+
MeerkatClient.validateOptionalStringField(status, "detail", context);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
static validateToolConfigPayload(raw, context) {
|
|
2630
|
+
const payload = MeerkatClient.requireRecord(raw, "payload", context);
|
|
2631
|
+
MeerkatClient.requireClosedStringField(payload, "operation", ["add", "remove", "reload"], context);
|
|
2632
|
+
MeerkatClient.requireBooleanField(payload, "persisted", context);
|
|
2633
|
+
MeerkatClient.requirePresentStringField(payload, "target", context);
|
|
2634
|
+
MeerkatClient.validateToolConfigStatus(payload.status_info, `${context}: status_info`);
|
|
2635
|
+
if (Object.prototype.hasOwnProperty.call(payload, "applied_at_turn")) {
|
|
2636
|
+
MeerkatClient.requireNonNegativeIntegerField(payload, "applied_at_turn", context);
|
|
2637
|
+
}
|
|
2638
|
+
if (Object.prototype.hasOwnProperty.call(payload, "domain")) {
|
|
2639
|
+
MeerkatClient.requireClosedStringField(payload, "domain", ["tool_scope", "deferred_catalog"], context);
|
|
2640
|
+
}
|
|
2641
|
+
if (Object.prototype.hasOwnProperty.call(payload, "deferred_catalog_delta")) {
|
|
2642
|
+
const delta = MeerkatClient.requireRecord(payload.deferred_catalog_delta, "deferred_catalog_delta", context);
|
|
2643
|
+
for (const field of [
|
|
2644
|
+
"added_hidden_names",
|
|
2645
|
+
"pending_sources",
|
|
2646
|
+
"removed_hidden_names",
|
|
2647
|
+
]) {
|
|
2648
|
+
if (Object.prototype.hasOwnProperty.call(delta, field)) {
|
|
2649
|
+
MeerkatClient.requireStringArray(delta[field], `${context}: ${field}`);
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
static validateSystemNoticeBlock(raw, context) {
|
|
2655
|
+
const type = MeerkatClient.requireClosedStringField(raw, "type", [
|
|
2656
|
+
"comms",
|
|
2657
|
+
"external_event",
|
|
2658
|
+
"tool_config",
|
|
2659
|
+
"mcp",
|
|
2660
|
+
"background_job",
|
|
2661
|
+
"auth",
|
|
2662
|
+
"runtime_notice",
|
|
2663
|
+
"unknown",
|
|
2664
|
+
], context);
|
|
2665
|
+
const validateContent = () => {
|
|
2666
|
+
if (!Object.prototype.hasOwnProperty.call(raw, "content"))
|
|
2667
|
+
return;
|
|
2668
|
+
MeerkatClient.requireRecordArray(raw.content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, true));
|
|
2669
|
+
};
|
|
2670
|
+
if (type === "comms") {
|
|
2671
|
+
MeerkatClient.requireClosedStringField(raw, "direction", ["incoming", "outgoing", "internal"], context);
|
|
2672
|
+
MeerkatClient.requirePresentStringField(raw, "kind", context);
|
|
2673
|
+
for (const field of ["intent", "request_id", "status", "summary"]) {
|
|
2674
|
+
MeerkatClient.validateOptionalStringField(raw, field, context);
|
|
2675
|
+
}
|
|
2676
|
+
if (Object.prototype.hasOwnProperty.call(raw, "peer")) {
|
|
2677
|
+
const peer = MeerkatClient.requireRecord(raw.peer, "peer", context);
|
|
2678
|
+
MeerkatClient.requireStringField(peer, "id", `${context}: peer`);
|
|
2679
|
+
MeerkatClient.validateOptionalStringField(peer, "display_name", `${context}: peer`);
|
|
2680
|
+
}
|
|
2681
|
+
if (Object.prototype.hasOwnProperty.call(raw, "sender_taint")) {
|
|
2682
|
+
MeerkatClient.requireClosedStringField(raw, "sender_taint", ["clean", "tainted"], context);
|
|
2683
|
+
}
|
|
2684
|
+
validateContent();
|
|
2685
|
+
}
|
|
2686
|
+
else if (type === "external_event") {
|
|
2687
|
+
MeerkatClient.requirePresentStringField(raw, "event_type", context);
|
|
2688
|
+
MeerkatClient.requirePresentStringField(raw, "source", context);
|
|
2689
|
+
MeerkatClient.validateOptionalStringField(raw, "body", context);
|
|
2690
|
+
MeerkatClient.validateOptionalStringField(raw, "summary", context);
|
|
2691
|
+
validateContent();
|
|
2692
|
+
}
|
|
2693
|
+
else if (type === "tool_config") {
|
|
2694
|
+
MeerkatClient.validateToolConfigPayload(raw.payload, `${context}: payload`);
|
|
2695
|
+
}
|
|
2696
|
+
else if (type === "mcp") {
|
|
2697
|
+
for (const field of ["detail", "server_id"]) {
|
|
2698
|
+
MeerkatClient.validateOptionalStringField(raw, field, context);
|
|
2699
|
+
}
|
|
2700
|
+
if (Object.prototype.hasOwnProperty.call(raw, "operation")) {
|
|
2701
|
+
MeerkatClient.requireClosedStringField(raw, "operation", ["add", "remove", "reload"], context);
|
|
2702
|
+
}
|
|
2703
|
+
if (Object.prototype.hasOwnProperty.call(raw, "pending_sources")) {
|
|
2704
|
+
MeerkatClient.requireStringArray(raw.pending_sources, `${context}: pending_sources`);
|
|
2705
|
+
}
|
|
2706
|
+
MeerkatClient.validateOptionalBooleanField(raw, "persisted", context);
|
|
2707
|
+
if (Object.prototype.hasOwnProperty.call(raw, "phase")) {
|
|
2708
|
+
MeerkatClient.requireClosedStringField(raw, "phase", ["pending", "applied", "draining", "forced", "failed"], context);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
else if (type === "background_job") {
|
|
2712
|
+
MeerkatClient.requireStringField(raw, "job_id", context);
|
|
2713
|
+
MeerkatClient.requireClosedStringField(raw, "status", ["completed", "failed", "aborted", "cancelled", "retired", "terminated"], context);
|
|
2714
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2715
|
+
MeerkatClient.validateOptionalStringField(raw, "display_name", context);
|
|
2716
|
+
}
|
|
2717
|
+
else if (type === "auth") {
|
|
2718
|
+
MeerkatClient.requirePresentStringField(raw, "state", context);
|
|
2719
|
+
MeerkatClient.validateOptionalStringField(raw, "binding", context);
|
|
2720
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2721
|
+
}
|
|
2722
|
+
else if (type === "runtime_notice") {
|
|
2723
|
+
MeerkatClient.requirePresentStringField(raw, "category", context);
|
|
2724
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2725
|
+
}
|
|
2726
|
+
else {
|
|
2727
|
+
MeerkatClient.validateOptionalStringField(raw, "summary", context);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
static validateWireAssistantBlock(raw, context) {
|
|
2731
|
+
const blockType = MeerkatClient.requireClosedStringField(raw, "block_type", [
|
|
2732
|
+
"text",
|
|
2733
|
+
"transcript",
|
|
2734
|
+
"reasoning",
|
|
2735
|
+
"tool_use",
|
|
2736
|
+
"server_tool_content",
|
|
2737
|
+
"image",
|
|
2738
|
+
"unknown",
|
|
2739
|
+
], context);
|
|
2740
|
+
if (blockType === "unknown")
|
|
2741
|
+
return;
|
|
2742
|
+
const data = MeerkatClient.requireRecord(raw.data, "data", context);
|
|
2743
|
+
MeerkatClient.validateOptionalMetaRecord(data, `${context}: data`);
|
|
2744
|
+
if (blockType === "text") {
|
|
2745
|
+
MeerkatClient.requirePresentStringField(data, "text", `${context}: data`);
|
|
2746
|
+
}
|
|
2747
|
+
else if (blockType === "transcript") {
|
|
2748
|
+
MeerkatClient.requirePresentStringField(data, "text", `${context}: data`);
|
|
2749
|
+
const source = MeerkatClient.requireRecord(data.source, "source", `${context}: data`);
|
|
2750
|
+
const sourceKind = MeerkatClient.requireClosedStringField(source, "kind", ["spoken", "unknown"], `${context}: data.source`);
|
|
2751
|
+
if (sourceKind === "unknown") {
|
|
2752
|
+
MeerkatClient.requirePresentStringField(source, "debug", `${context}: data.source`);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
else if (blockType === "reasoning") {
|
|
2756
|
+
MeerkatClient.validateOptionalStringField(data, "text", `${context}: data`);
|
|
2757
|
+
}
|
|
2758
|
+
else if (blockType === "tool_use") {
|
|
2759
|
+
MeerkatClient.requireOwnField(data, "args", `${context}: data`);
|
|
2760
|
+
MeerkatClient.requireStringField(data, "id", `${context}: data`);
|
|
2761
|
+
MeerkatClient.requireStringField(data, "name", `${context}: data`);
|
|
2762
|
+
}
|
|
2763
|
+
else if (blockType === "server_tool_content") {
|
|
2764
|
+
MeerkatClient.requireOwnField(data, "content", `${context}: data`);
|
|
2765
|
+
MeerkatClient.requireRecord(data.kind, "kind", `${context}: data`);
|
|
2766
|
+
MeerkatClient.validateOptionalStringField(data, "id", `${context}: data`);
|
|
2767
|
+
}
|
|
2768
|
+
else {
|
|
2769
|
+
MeerkatClient.requireRecord(data.blob_ref, "blob_ref", `${context}: data`);
|
|
2770
|
+
MeerkatClient.requireNonNegativeIntegerField(data, "height", `${context}: data`);
|
|
2771
|
+
MeerkatClient.requireStringField(data, "image_id", `${context}: data`);
|
|
2772
|
+
MeerkatClient.requirePresentStringField(data, "media_type", `${context}: data`);
|
|
2773
|
+
MeerkatClient.requireRecord(data.meta, "meta", `${context}: data`);
|
|
2774
|
+
MeerkatClient.requireRecord(data.revised_prompt, "revised_prompt", `${context}: data`);
|
|
2775
|
+
MeerkatClient.requireNonNegativeIntegerField(data, "width", `${context}: data`);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
static validateWireToolResult(raw, context) {
|
|
2779
|
+
MeerkatClient.requireStringField(raw, "tool_use_id", context);
|
|
2780
|
+
const content = MeerkatClient.requireOwnField(raw, "content", context);
|
|
2781
|
+
if (typeof content !== "string" && !Array.isArray(content)) {
|
|
2782
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: invalid content`);
|
|
2783
|
+
}
|
|
2784
|
+
if (Array.isArray(content)) {
|
|
2785
|
+
MeerkatClient.requireRecordArray(content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, false));
|
|
2786
|
+
}
|
|
2787
|
+
MeerkatClient.validateOptionalBooleanField(raw, "is_error", context);
|
|
2788
|
+
}
|
|
2789
|
+
static validateWireHistoryRow(raw, context) {
|
|
2790
|
+
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
2791
|
+
MeerkatClient.requireStringField(raw, "created_at", context);
|
|
2792
|
+
for (const field of ["interaction_id", "run_id"]) {
|
|
2793
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2794
|
+
(typeof raw[field] !== "string" || raw[field].length === 0)) {
|
|
2795
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be non-empty string`);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
if (role === "system") {
|
|
2799
|
+
MeerkatClient.requirePresentStringField(raw, "content", context);
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
if (role === "system_notice") {
|
|
2803
|
+
const kind = MeerkatClient.requireStringField(raw, "kind", context);
|
|
2804
|
+
if (![
|
|
2805
|
+
"generic",
|
|
2806
|
+
"comms",
|
|
2807
|
+
"external_event",
|
|
2808
|
+
"mcp_pending",
|
|
2809
|
+
"mcp",
|
|
2810
|
+
"background_job",
|
|
2811
|
+
"tool_scope",
|
|
2812
|
+
"tool_scope_warning",
|
|
2813
|
+
"auth_reauth_required",
|
|
2814
|
+
].includes(kind)) {
|
|
2815
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported kind ${JSON.stringify(kind)}`);
|
|
2816
|
+
}
|
|
2817
|
+
MeerkatClient.validateOptionalStringField(raw, "body", context);
|
|
2818
|
+
if (Object.prototype.hasOwnProperty.call(raw, "blocks")) {
|
|
2819
|
+
MeerkatClient.requireRecordArray(raw.blocks, `${context}: blocks`).forEach((block, index) => MeerkatClient.validateSystemNoticeBlock(block, `${context}: blocks[${index}]`));
|
|
2820
|
+
}
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
if (role === "user") {
|
|
2824
|
+
if (raw.content == null || (!Array.isArray(raw.content) && typeof raw.content !== "string")) {
|
|
2825
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing content`);
|
|
2826
|
+
}
|
|
2827
|
+
if (Array.isArray(raw.content)) {
|
|
2828
|
+
MeerkatClient.requireRecordArray(raw.content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, false));
|
|
2829
|
+
}
|
|
2830
|
+
if (Object.prototype.hasOwnProperty.call(raw, "transcript_role")) {
|
|
2831
|
+
MeerkatClient.requireClosedStringField(raw, "transcript_role", ["conversational", "compaction_summary", "injected_context"], context);
|
|
2832
|
+
}
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
if (role === "block_assistant") {
|
|
2836
|
+
MeerkatClient.requireRecordArray(raw.blocks, `${context}: blocks`).forEach((block, index) => MeerkatClient.validateWireAssistantBlock(block, `${context}: blocks[${index}]`));
|
|
2837
|
+
const stopReason = MeerkatClient.requireStringField(raw, "stop_reason", context);
|
|
2838
|
+
if (![
|
|
2839
|
+
"end_turn",
|
|
2840
|
+
"tool_use",
|
|
2841
|
+
"max_tokens",
|
|
2842
|
+
"stop_sequence",
|
|
2843
|
+
"content_filter",
|
|
2844
|
+
"cancelled",
|
|
2845
|
+
].includes(stopReason)) {
|
|
2846
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported stop_reason ${JSON.stringify(stopReason)}`);
|
|
2847
|
+
}
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
if (role === "tool_results") {
|
|
2851
|
+
MeerkatClient.requireRecordArray(raw.results, `${context}: results`).forEach((result, index) => MeerkatClient.validateWireToolResult(result, `${context}: results[${index}]`));
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported role ${JSON.stringify(role)}`);
|
|
2855
|
+
}
|
|
2856
|
+
static parseMobHostCapabilities(raw, context) {
|
|
2857
|
+
const capabilities = MeerkatClient.requireRecord(raw, "capabilities", context);
|
|
2858
|
+
for (const field of [
|
|
2859
|
+
"approval_forwarding",
|
|
2860
|
+
"autonomous_members",
|
|
2861
|
+
"durable_sessions",
|
|
2862
|
+
"hard_cancel_member",
|
|
2863
|
+
"mcp",
|
|
2864
|
+
"memory_store",
|
|
2865
|
+
]) {
|
|
2866
|
+
MeerkatClient.requireBooleanField(capabilities, field, context);
|
|
2867
|
+
}
|
|
2868
|
+
const trackedInputCancel = Object.prototype.hasOwnProperty.call(capabilities, "tracked_input_cancel")
|
|
2869
|
+
? MeerkatClient.requireBooleanField(capabilities, "tracked_input_cancel", context)
|
|
2870
|
+
: false;
|
|
2871
|
+
MeerkatClient.requireStringField(capabilities, "engine_version", context);
|
|
2872
|
+
const protocolMin = MeerkatClient.requireNonNegativeIntegerField(capabilities, "protocol_min", context);
|
|
2873
|
+
const protocolMax = MeerkatClient.requireNonNegativeIntegerField(capabilities, "protocol_max", context);
|
|
2874
|
+
if (protocolMin > protocolMax) {
|
|
2875
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: protocol_min exceeds protocol_max`);
|
|
2876
|
+
}
|
|
2877
|
+
const liveEndpoint = MeerkatClient.optionalStringField(capabilities, "live_endpoint", context);
|
|
2878
|
+
if (liveEndpoint === "") {
|
|
2879
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: live_endpoint must be non-empty`);
|
|
2880
|
+
}
|
|
2881
|
+
const resolvableProviders = Object.prototype.hasOwnProperty.call(capabilities, "resolvable_providers")
|
|
2882
|
+
? MeerkatClient.requireStringArray(capabilities.resolvable_providers, `${context}: resolvable_providers`)
|
|
2883
|
+
: undefined;
|
|
2884
|
+
return {
|
|
2885
|
+
...capabilities,
|
|
2886
|
+
tracked_input_cancel: trackedInputCancel,
|
|
2887
|
+
...(resolvableProviders !== undefined
|
|
2888
|
+
? { resolvable_providers: resolvableProviders }
|
|
2889
|
+
: {}),
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
static parseMobHostStatus(raw, context) {
|
|
2893
|
+
const host = MeerkatClient.requireRecord(raw, "host", context);
|
|
2894
|
+
const bindPhase = MeerkatClient.requireStringField(host, "bind_phase", context);
|
|
2895
|
+
if (bindPhase !== "requested" && bindPhase !== "bound") {
|
|
2896
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported bind_phase ${JSON.stringify(bindPhase)}`);
|
|
2897
|
+
}
|
|
2898
|
+
MeerkatClient.requireStringField(host, "host_id", context);
|
|
2899
|
+
MeerkatClient.requireNonNegativeIntegerField(host, "materialized_member_count", context);
|
|
2900
|
+
const authorityEpoch = host.authority_epoch == null
|
|
2901
|
+
? undefined
|
|
2902
|
+
: MeerkatClient.requireNonNegativeIntegerField(host, "authority_epoch", context);
|
|
2903
|
+
const endpoint = MeerkatClient.optionalStringField(host, "endpoint", context);
|
|
2904
|
+
const capabilities = host.capabilities == null
|
|
2905
|
+
? undefined
|
|
2906
|
+
: MeerkatClient.parseMobHostCapabilities(host.capabilities, `${context}: capabilities`);
|
|
2907
|
+
if (bindPhase === "bound" &&
|
|
2908
|
+
(authorityEpoch === undefined || !endpoint || capabilities === undefined)) {
|
|
2909
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: bound host missing committed authority facts`);
|
|
2910
|
+
}
|
|
2911
|
+
if (bindPhase === "requested" &&
|
|
2912
|
+
(authorityEpoch !== undefined || endpoint !== undefined || capabilities !== undefined)) {
|
|
2913
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: requested host must not claim committed authority facts`);
|
|
2914
|
+
}
|
|
2915
|
+
if (host.control_reachability != null &&
|
|
2916
|
+
!["reachable", "stale", "unreachable", "unknown"].includes(String(host.control_reachability))) {
|
|
2917
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported control_reachability`);
|
|
2918
|
+
}
|
|
2919
|
+
if (host.last_seen_ms != null) {
|
|
2920
|
+
MeerkatClient.requireNonNegativeIntegerField(host, "last_seen_ms", context);
|
|
2921
|
+
}
|
|
2922
|
+
MeerkatClient.optionalStringField(host, "freshness_reason", context);
|
|
2923
|
+
return (capabilities === undefined
|
|
2924
|
+
? host
|
|
2925
|
+
: { ...host, capabilities });
|
|
2926
|
+
}
|
|
2927
|
+
static parseRouteInstallObligation(raw, context) {
|
|
2928
|
+
const obligation = MeerkatClient.requireRecord(raw, "obligation", context);
|
|
2929
|
+
MeerkatClient.requireStringField(obligation, "edge_a", context);
|
|
2930
|
+
MeerkatClient.requireStringField(obligation, "edge_b", context);
|
|
2931
|
+
MeerkatClient.requireStringField(obligation, "host", context);
|
|
2932
|
+
return obligation;
|
|
2933
|
+
}
|
|
2310
2934
|
static requireBooleanField(raw, field, context) {
|
|
2311
2935
|
const value = raw[field];
|
|
2312
2936
|
if (typeof value !== "boolean") {
|
|
@@ -2333,6 +2957,21 @@ export class MeerkatClient {
|
|
|
2333
2957
|
}
|
|
2334
2958
|
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2335
2959
|
}
|
|
2960
|
+
static parseWireReachability(raw, field) {
|
|
2961
|
+
if (raw == null) {
|
|
2962
|
+
return undefined;
|
|
2963
|
+
}
|
|
2964
|
+
const values = [
|
|
2965
|
+
"reachable",
|
|
2966
|
+
"stale",
|
|
2967
|
+
"unreachable",
|
|
2968
|
+
"unknown",
|
|
2969
|
+
];
|
|
2970
|
+
if (typeof raw === "string" && values.includes(raw)) {
|
|
2971
|
+
return raw;
|
|
2972
|
+
}
|
|
2973
|
+
throw new MeerkatError("INVALID_RESPONSE", `Invalid mob/member_status response: ${field} must be a valid reachability`);
|
|
2974
|
+
}
|
|
2336
2975
|
static parseMobPeerConnectivitySnapshot(raw, context) {
|
|
2337
2976
|
const record = MeerkatClient.requireRecord(raw, "peer_connectivity.snapshot", context);
|
|
2338
2977
|
const unreachableRaw = record.unreachable_peers;
|
|
@@ -2427,6 +3066,85 @@ export class MeerkatClient {
|
|
|
2427
3066
|
status: MeerkatClient.parseWireCapabilityStatus(record.status, context),
|
|
2428
3067
|
};
|
|
2429
3068
|
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Validate the generated `LiveOpenResult` contract without projecting or
|
|
3071
|
+
* normalizing its transport bootstrap. In particular, single-use tokens are
|
|
3072
|
+
* returned byte-for-byte as received and are never logged.
|
|
3073
|
+
*/
|
|
3074
|
+
static parseLiveOpenResult(raw, context) {
|
|
3075
|
+
const result = MeerkatClient.requireRecord(raw, "result", context);
|
|
3076
|
+
MeerkatClient.requireStringField(result, "channel_id", context);
|
|
3077
|
+
const capabilities = MeerkatClient.requireRecord(result.capabilities, "capabilities", context);
|
|
3078
|
+
for (const field of [
|
|
3079
|
+
"audio_in",
|
|
3080
|
+
"audio_out",
|
|
3081
|
+
"barge_in_supported",
|
|
3082
|
+
"image_in",
|
|
3083
|
+
"provider_native_resume",
|
|
3084
|
+
"text_in",
|
|
3085
|
+
"text_out",
|
|
3086
|
+
"transcript_supported",
|
|
3087
|
+
"video_in",
|
|
3088
|
+
]) {
|
|
3089
|
+
MeerkatClient.requireBooleanField(capabilities, field, `${context}: capabilities`);
|
|
3090
|
+
}
|
|
3091
|
+
const continuity = MeerkatClient.requireRecord(result.continuity, "continuity", context);
|
|
3092
|
+
const continuityMode = MeerkatClient.requireClosedStringField(continuity, "mode", [
|
|
3093
|
+
"fresh",
|
|
3094
|
+
"transcript_only",
|
|
3095
|
+
"degraded",
|
|
3096
|
+
"provider_native_resume",
|
|
3097
|
+
"unknown",
|
|
3098
|
+
], `${context}: continuity`);
|
|
3099
|
+
if (continuityMode === "provider_native_resume") {
|
|
3100
|
+
MeerkatClient.requireStringField(continuity, "provider_session_id", `${context}: continuity`);
|
|
3101
|
+
}
|
|
3102
|
+
else if (continuityMode === "unknown") {
|
|
3103
|
+
MeerkatClient.requirePresentStringField(continuity, "debug", `${context}: continuity`);
|
|
3104
|
+
}
|
|
3105
|
+
const transport = MeerkatClient.requireRecord(result.transport, "transport", context);
|
|
3106
|
+
const transportKind = MeerkatClient.requireClosedStringField(transport, "transport", ["websocket", "webrtc", "unknown"], `${context}: transport`);
|
|
3107
|
+
if (transportKind === "websocket") {
|
|
3108
|
+
MeerkatClient.requireStringField(transport, "url", `${context}: transport`);
|
|
3109
|
+
MeerkatClient.requireStringField(transport, "token", `${context}: transport`);
|
|
3110
|
+
}
|
|
3111
|
+
else if (transportKind === "webrtc") {
|
|
3112
|
+
MeerkatClient.requireStringField(transport, "answer_method", `${context}: transport`);
|
|
3113
|
+
MeerkatClient.requireStringField(transport, "token", `${context}: transport`);
|
|
3114
|
+
MeerkatClient.validateOptionalStringField(transport, "http_url", `${context}: transport`);
|
|
3115
|
+
}
|
|
3116
|
+
else {
|
|
3117
|
+
MeerkatClient.requirePresentStringField(transport, "debug", `${context}: transport`);
|
|
3118
|
+
}
|
|
3119
|
+
return result;
|
|
3120
|
+
}
|
|
3121
|
+
/** Validate the generated `LiveStatusResult` closed status vocabulary. */
|
|
3122
|
+
static parseLiveStatusResult(raw, context) {
|
|
3123
|
+
const result = MeerkatClient.requireRecord(raw, "result", context);
|
|
3124
|
+
MeerkatClient.requireStringField(result, "channel_id", context);
|
|
3125
|
+
const status = MeerkatClient.requireRecord(result.status, "status", context);
|
|
3126
|
+
const statusKind = MeerkatClient.requireClosedStringField(status, "status", ["idle", "opening", "ready", "degraded", "closing", "closed", "unknown"], `${context}: status`);
|
|
3127
|
+
if (statusKind === "degraded") {
|
|
3128
|
+
const reason = MeerkatClient.requireRecord(status.reason, "reason", `${context}: status`);
|
|
3129
|
+
const reasonKind = MeerkatClient.requireClosedStringField(reason, "kind", [
|
|
3130
|
+
"rate_limited",
|
|
3131
|
+
"provider_throttled",
|
|
3132
|
+
"network_unstable",
|
|
3133
|
+
"other",
|
|
3134
|
+
"unknown",
|
|
3135
|
+
], `${context}: status.reason`);
|
|
3136
|
+
if (reasonKind === "other") {
|
|
3137
|
+
MeerkatClient.requirePresentStringField(reason, "detail", `${context}: status.reason`);
|
|
3138
|
+
}
|
|
3139
|
+
else if (reasonKind === "unknown") {
|
|
3140
|
+
MeerkatClient.requirePresentStringField(reason, "debug", `${context}: status.reason`);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
else if (statusKind === "unknown") {
|
|
3144
|
+
MeerkatClient.requirePresentStringField(status, "debug", `${context}: status`);
|
|
3145
|
+
}
|
|
3146
|
+
return result;
|
|
3147
|
+
}
|
|
2430
3148
|
static parseLiveRefreshResult(raw) {
|
|
2431
3149
|
const context = "Invalid live/refresh response";
|
|
2432
3150
|
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
@@ -2700,6 +3418,37 @@ export class MeerkatClient {
|
|
|
2700
3418
|
}
|
|
2701
3419
|
return progress;
|
|
2702
3420
|
}
|
|
3421
|
+
static parseMemberLifecycleCapabilities(raw) {
|
|
3422
|
+
if (raw == null) {
|
|
3423
|
+
return undefined;
|
|
3424
|
+
}
|
|
3425
|
+
const context = "Invalid mob/member_status response";
|
|
3426
|
+
const record = MeerkatClient.requireRecord(raw, "lifecycle_capabilities", context);
|
|
3427
|
+
return {
|
|
3428
|
+
transcript_edits: MeerkatClient.requireBooleanField(record, "transcript_edits", context),
|
|
3429
|
+
revisions: MeerkatClient.requireBooleanField(record, "revisions", context),
|
|
3430
|
+
resume_after_restart: MeerkatClient.requireBooleanField(record, "resume_after_restart", context),
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
static parseNonPortableDisabled(raw) {
|
|
3434
|
+
if (raw == null) {
|
|
3435
|
+
return undefined;
|
|
3436
|
+
}
|
|
3437
|
+
const values = [
|
|
3438
|
+
"rust_bundles",
|
|
3439
|
+
"per_spawn_external_tools",
|
|
3440
|
+
"mob_default_external_tools",
|
|
3441
|
+
"default_llm_client_override",
|
|
3442
|
+
"host_surface_mcp_allowlist",
|
|
3443
|
+
"workgraph_tools",
|
|
3444
|
+
];
|
|
3445
|
+
if (!Array.isArray(raw) ||
|
|
3446
|
+
raw.some((entry) => typeof entry !== "string" ||
|
|
3447
|
+
!values.includes(entry))) {
|
|
3448
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/member_status response: non_portable_disabled must contain valid resource kinds");
|
|
3449
|
+
}
|
|
3450
|
+
return raw;
|
|
3451
|
+
}
|
|
2703
3452
|
static parseModelsCatalog(data) {
|
|
2704
3453
|
const context = "Invalid models/catalog response";
|
|
2705
3454
|
const contractVersion = MeerkatClient.parseContractVersion(data.contract_version, context);
|
|
@@ -2773,11 +3522,13 @@ export class MeerkatClient {
|
|
|
2773
3522
|
}
|
|
2774
3523
|
static parseContractVersion(raw, context) {
|
|
2775
3524
|
const parseComponent = (value, field) => {
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
3525
|
+
const parsed = typeof value === "number"
|
|
3526
|
+
? value
|
|
3527
|
+
: typeof value === "string" && /^\d+$/.test(value)
|
|
3528
|
+
? Number(value)
|
|
3529
|
+
: undefined;
|
|
3530
|
+
if (typeof parsed === "number" && Number.isSafeInteger(parsed) && parsed >= 0) {
|
|
3531
|
+
return parsed;
|
|
2781
3532
|
}
|
|
2782
3533
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: contract_version.${field} must be non-negative integer`);
|
|
2783
3534
|
};
|
|
@@ -3006,10 +3757,8 @@ export class MeerkatClient {
|
|
|
3006
3757
|
}
|
|
3007
3758
|
static parseSessionHistory(data) {
|
|
3008
3759
|
const context = "Invalid session history response";
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
}
|
|
3012
|
-
const rawMessages = data.messages;
|
|
3760
|
+
const rawMessages = MeerkatClient.requireRecordArray(data.messages, `${context}: messages`);
|
|
3761
|
+
rawMessages.forEach((message, index) => MeerkatClient.validateWireHistoryRow(message, `${context}: messages[${index}]`));
|
|
3013
3762
|
return {
|
|
3014
3763
|
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
3015
3764
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
@@ -3022,10 +3771,8 @@ export class MeerkatClient {
|
|
|
3022
3771
|
}
|
|
3023
3772
|
static parseSessionTranscriptRevision(data) {
|
|
3024
3773
|
const context = "Invalid session transcript revision response";
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
}
|
|
3028
|
-
const rawMessages = data.messages;
|
|
3774
|
+
const rawMessages = MeerkatClient.requireRecordArray(data.messages, `${context}: messages`);
|
|
3775
|
+
rawMessages.forEach((message, index) => MeerkatClient.validateWireHistoryRow(message, `${context}: messages[${index}]`));
|
|
3029
3776
|
return {
|
|
3030
3777
|
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
3031
3778
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
@@ -3135,7 +3882,12 @@ export class MeerkatClient {
|
|
|
3135
3882
|
stopReason: data.stop_reason != null ? String(data.stop_reason) : undefined,
|
|
3136
3883
|
interactionId: data.interaction_id != null ? String(data.interaction_id) : undefined,
|
|
3137
3884
|
runId: data.run_id != null ? String(data.run_id) : undefined,
|
|
3138
|
-
blocks
|
|
3885
|
+
// System-notice blocks have their own generated union and remain
|
|
3886
|
+
// available in `raw`; only block-assistant rows project through the
|
|
3887
|
+
// public assistant-block view.
|
|
3888
|
+
blocks: role === "block_assistant"
|
|
3889
|
+
? rawBlocks.map((block) => MeerkatClient.parseSessionAssistantBlock(block))
|
|
3890
|
+
: [],
|
|
3139
3891
|
results: rawResults.map((result) => {
|
|
3140
3892
|
if (typeof result !== "object" || result === null) {
|
|
3141
3893
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result must be an object`);
|
|
@@ -3230,60 +3982,22 @@ export class MeerkatClient {
|
|
|
3230
3982
|
}
|
|
3231
3983
|
static parseContentInput(value) {
|
|
3232
3984
|
if (Array.isArray(value)) {
|
|
3233
|
-
return value
|
|
3234
|
-
.filter((item) => typeof item === "object" && item !== null)
|
|
3235
|
-
.map((block) => MeerkatClient.parseContentBlock(block));
|
|
3985
|
+
return value.map((item, index) => MeerkatClient.parseContentBlock(MeerkatClient.requireRecord(item, `content[${index}]`, "Invalid session content")));
|
|
3236
3986
|
}
|
|
3237
|
-
|
|
3987
|
+
if (typeof value === "string")
|
|
3988
|
+
return value;
|
|
3989
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid session content: expected string or content-block array");
|
|
3238
3990
|
}
|
|
3239
3991
|
static parseContentBlock(data) {
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
const source = String(data.source ?? "inline");
|
|
3246
|
-
if (source === "blob") {
|
|
3247
|
-
return {
|
|
3248
|
-
type: "image",
|
|
3249
|
-
media_type: String(data.media_type ?? ""),
|
|
3250
|
-
source: "blob",
|
|
3251
|
-
blob_id: String(data.blob_id ?? ""),
|
|
3252
|
-
};
|
|
3253
|
-
}
|
|
3254
|
-
return {
|
|
3255
|
-
type: "image",
|
|
3256
|
-
media_type: String(data.media_type ?? ""),
|
|
3257
|
-
source: "inline",
|
|
3258
|
-
data: String(data.data ?? ""),
|
|
3259
|
-
};
|
|
3260
|
-
}
|
|
3261
|
-
if (type === "video") {
|
|
3262
|
-
const source = String(data.source ?? "inline");
|
|
3263
|
-
if (source === "uri") {
|
|
3264
|
-
return {
|
|
3265
|
-
type: "video",
|
|
3266
|
-
media_type: String(data.media_type ?? ""),
|
|
3267
|
-
duration_ms: Number(data.duration_ms ?? 0),
|
|
3268
|
-
source: "uri",
|
|
3269
|
-
uri: String(data.uri ?? ""),
|
|
3270
|
-
};
|
|
3271
|
-
}
|
|
3272
|
-
return {
|
|
3273
|
-
type: "video",
|
|
3274
|
-
media_type: String(data.media_type ?? ""),
|
|
3275
|
-
duration_ms: Number(data.duration_ms ?? 0),
|
|
3276
|
-
source: "inline",
|
|
3277
|
-
data: String(data.data ?? ""),
|
|
3278
|
-
};
|
|
3279
|
-
}
|
|
3280
|
-
return { type: "text", text: "" };
|
|
3992
|
+
MeerkatClient.validateGeneratedContentBlock(data, "Invalid session content block", false);
|
|
3993
|
+
// Preserve the validated wire object exactly. In particular, do not
|
|
3994
|
+
// invent inline bytes for history-only image/video projections or collapse
|
|
3995
|
+
// structured/unknown variants into empty text.
|
|
3996
|
+
return { ...data };
|
|
3281
3997
|
}
|
|
3282
3998
|
static parseSessionAssistantBlock(data) {
|
|
3283
3999
|
const context = "Invalid session assistant block";
|
|
3284
|
-
|
|
3285
|
-
throw new MeerkatError("INVALID_RESPONSE", `${context}: data must be an object`);
|
|
3286
|
-
}
|
|
4000
|
+
MeerkatClient.validateWireAssistantBlock(data, context);
|
|
3287
4001
|
const blockData = data.data ?? {};
|
|
3288
4002
|
const blobRef = blockData.blob_ref;
|
|
3289
4003
|
const revisedPrompt = blockData.revised_prompt != null && typeof blockData.revised_prompt === "object"
|
|
@@ -3302,9 +4016,9 @@ export class MeerkatClient {
|
|
|
3302
4016
|
height: blockData.height != null ? Number(blockData.height) : undefined,
|
|
3303
4017
|
revisedPrompt,
|
|
3304
4018
|
meta: blockData.meta,
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
4019
|
+
source: data.block_type === "transcript"
|
|
4020
|
+
? { ...blockData.source }
|
|
4021
|
+
: undefined,
|
|
3308
4022
|
raw: { ...data },
|
|
3309
4023
|
};
|
|
3310
4024
|
}
|
|
@@ -3379,7 +4093,7 @@ export class MeerkatClient {
|
|
|
3379
4093
|
params.model = options.model;
|
|
3380
4094
|
if (options.provider)
|
|
3381
4095
|
params.provider = options.provider;
|
|
3382
|
-
if (options.systemPrompt)
|
|
4096
|
+
if (options.systemPrompt !== undefined)
|
|
3383
4097
|
params.system_prompt = options.systemPrompt;
|
|
3384
4098
|
if (options.maxTokens)
|
|
3385
4099
|
params.max_tokens = options.maxTokens;
|