@rkat/sdk 0.7.30 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +92 -15
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +838 -81
- package/dist/client.js.map +1 -1
- 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/types.d.ts +128 -75
- 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,7 +32,7 @@ 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";
|
|
@@ -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
|
/**
|
|
@@ -1688,7 +1945,7 @@ export class MeerkatClient {
|
|
|
1688
1945
|
const source = MeerkatClient.requireRecord(raw.source, "source", context);
|
|
1689
1946
|
const identity = MeerkatClient.requireStringField(source, "identity", context);
|
|
1690
1947
|
const generation = MeerkatClient.requireNumberField(source, "generation", context);
|
|
1691
|
-
if (!Number.
|
|
1948
|
+
if (!Number.isSafeInteger(generation) || generation < 0) {
|
|
1692
1949
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: source generation must be a non-negative integer`);
|
|
1693
1950
|
}
|
|
1694
1951
|
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
@@ -1724,10 +1981,14 @@ export class MeerkatClient {
|
|
|
1724
1981
|
params.model = options.model;
|
|
1725
1982
|
if (options?.provider)
|
|
1726
1983
|
params.provider = options.provider;
|
|
1984
|
+
if (options?.selfHostedServerId != null) {
|
|
1985
|
+
params.self_hosted_server_id = options.selfHostedServerId;
|
|
1986
|
+
}
|
|
1727
1987
|
if (options?.maxTokens)
|
|
1728
1988
|
params.max_tokens = options.maxTokens;
|
|
1729
|
-
if (options?.systemPrompt)
|
|
1989
|
+
if (options?.systemPrompt !== undefined) {
|
|
1730
1990
|
params.system_prompt = options.systemPrompt;
|
|
1991
|
+
}
|
|
1731
1992
|
if (options?.outputSchema)
|
|
1732
1993
|
params.output_schema = options.outputSchema;
|
|
1733
1994
|
if (options?.structuredOutputRetries != null) {
|
|
@@ -1771,10 +2032,14 @@ export class MeerkatClient {
|
|
|
1771
2032
|
params.model = options.model;
|
|
1772
2033
|
if (options?.provider)
|
|
1773
2034
|
params.provider = options.provider;
|
|
2035
|
+
if (options?.selfHostedServerId != null) {
|
|
2036
|
+
params.self_hosted_server_id = options.selfHostedServerId;
|
|
2037
|
+
}
|
|
1774
2038
|
if (options?.maxTokens)
|
|
1775
2039
|
params.max_tokens = options.maxTokens;
|
|
1776
|
-
if (options?.systemPrompt)
|
|
2040
|
+
if (options?.systemPrompt !== undefined) {
|
|
1777
2041
|
params.system_prompt = options.systemPrompt;
|
|
2042
|
+
}
|
|
1778
2043
|
if (options?.outputSchema)
|
|
1779
2044
|
params.output_schema = options.outputSchema;
|
|
1780
2045
|
if (options?.structuredOutputRetries != null) {
|
|
@@ -1865,7 +2130,11 @@ export class MeerkatClient {
|
|
|
1865
2130
|
return MeerkatClient.parseCommsSendReceipt(result);
|
|
1866
2131
|
}
|
|
1867
2132
|
async peers(sessionId) {
|
|
1868
|
-
|
|
2133
|
+
const result = await this.request("comms/peers", { session_id: sessionId });
|
|
2134
|
+
if (!Array.isArray(result.peers)) {
|
|
2135
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid comms/peers response: missing peers");
|
|
2136
|
+
}
|
|
2137
|
+
return { peers: result.peers };
|
|
1869
2138
|
}
|
|
1870
2139
|
/** Idempotent spawn: spawns or returns the existing member entry. */
|
|
1871
2140
|
async mobEnsureMember(mobId, spec) {
|
|
@@ -1895,7 +2164,7 @@ export class MeerkatClient {
|
|
|
1895
2164
|
// schemas regenerated into `./generated/types.ts`. See I52/I53.
|
|
1896
2165
|
async liveOpen(params) {
|
|
1897
2166
|
const result = await this.request("live/open", params);
|
|
1898
|
-
return result;
|
|
2167
|
+
return MeerkatClient.parseLiveOpenResult(result, "Invalid live/open response");
|
|
1899
2168
|
}
|
|
1900
2169
|
async liveWebrtcAnswer(params) {
|
|
1901
2170
|
const result = await this.request("live/webrtc/answer", params);
|
|
@@ -1903,7 +2172,7 @@ export class MeerkatClient {
|
|
|
1903
2172
|
}
|
|
1904
2173
|
async liveStatus(params) {
|
|
1905
2174
|
const result = await this.request("live/status", params);
|
|
1906
|
-
return result;
|
|
2175
|
+
return MeerkatClient.parseLiveStatusResult(result, "Invalid live/status response");
|
|
1907
2176
|
}
|
|
1908
2177
|
async liveClose(params) {
|
|
1909
2178
|
const result = await this.request("live/close", params);
|
|
@@ -2132,7 +2401,18 @@ export class MeerkatClient {
|
|
|
2132
2401
|
const error = data.error;
|
|
2133
2402
|
if (error) {
|
|
2134
2403
|
const normalized = MeerkatClient.parseRpcErrorPayload(error);
|
|
2135
|
-
|
|
2404
|
+
const errorData = typeof error.data === "object" &&
|
|
2405
|
+
error.data !== null &&
|
|
2406
|
+
!Array.isArray(error.data)
|
|
2407
|
+
? error.data
|
|
2408
|
+
: undefined;
|
|
2409
|
+
const semanticCode = typeof errorData?.code === "string" && errorData.code.length > 0
|
|
2410
|
+
? errorData.code
|
|
2411
|
+
: undefined;
|
|
2412
|
+
const rpcCode = typeof error.code === "number" || typeof error.code === "string"
|
|
2413
|
+
? error.code
|
|
2414
|
+
: "UNKNOWN";
|
|
2415
|
+
pending.reject(meerkatErrorFromJsonRpcCode(rpcCode, semanticCode, normalized.message, normalized.details));
|
|
2136
2416
|
}
|
|
2137
2417
|
else {
|
|
2138
2418
|
pending.resolve((data.result ?? {}));
|
|
@@ -2266,6 +2546,31 @@ export class MeerkatClient {
|
|
|
2266
2546
|
}
|
|
2267
2547
|
return value;
|
|
2268
2548
|
}
|
|
2549
|
+
static requireOwnField(raw, field, context) {
|
|
2550
|
+
if (!Object.prototype.hasOwnProperty.call(raw, field)) {
|
|
2551
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${field}`);
|
|
2552
|
+
}
|
|
2553
|
+
return raw[field];
|
|
2554
|
+
}
|
|
2555
|
+
static requireClosedStringField(raw, field, allowed, context) {
|
|
2556
|
+
const value = MeerkatClient.requirePresentStringField(raw, field, context);
|
|
2557
|
+
if (!allowed.includes(value)) {
|
|
2558
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported ${field} ${JSON.stringify(value)}`);
|
|
2559
|
+
}
|
|
2560
|
+
return value;
|
|
2561
|
+
}
|
|
2562
|
+
static validateOptionalStringField(raw, field, context) {
|
|
2563
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2564
|
+
typeof raw[field] !== "string") {
|
|
2565
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be string`);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
static validateOptionalBooleanField(raw, field, context) {
|
|
2569
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2570
|
+
typeof raw[field] !== "boolean") {
|
|
2571
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be boolean`);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2269
2574
|
static requireNumberField(raw, field, context, displayField = field) {
|
|
2270
2575
|
if (!(field in raw)) {
|
|
2271
2576
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing ${displayField}`);
|
|
@@ -2278,14 +2583,15 @@ export class MeerkatClient {
|
|
|
2278
2583
|
}
|
|
2279
2584
|
/**
|
|
2280
2585
|
* 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
|
|
2586
|
+
* that is fractional, negative, or outside JavaScript's lossless integer
|
|
2587
|
+
* range can never be represented faithfully, so it is a contract violation
|
|
2588
|
+
* and fails closed instead of being accepted as an any-finite-number.
|
|
2589
|
+
* Mirrors the web SDK `requireNonNegativeIntegerField` and Python
|
|
2590
|
+
* `_require_non_negative_integer_field`.
|
|
2285
2591
|
*/
|
|
2286
2592
|
static requireNonNegativeIntegerField(raw, field, context, displayField = field) {
|
|
2287
2593
|
const value = MeerkatClient.requireNumberField(raw, field, context, displayField);
|
|
2288
|
-
if (!Number.
|
|
2594
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
2289
2595
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${displayField} must be a non-negative integer`);
|
|
2290
2596
|
}
|
|
2291
2597
|
return value;
|
|
@@ -2307,6 +2613,367 @@ export class MeerkatClient {
|
|
|
2307
2613
|
}
|
|
2308
2614
|
return value;
|
|
2309
2615
|
}
|
|
2616
|
+
static validateGeneratedContentBlock(raw, context, systemNoticeContent) {
|
|
2617
|
+
const allowed = systemNoticeContent
|
|
2618
|
+
? ["text", "image", "video", "structured", "skill_context"]
|
|
2619
|
+
: ["text", "image", "video", "structured", "unknown"];
|
|
2620
|
+
const type = MeerkatClient.requireClosedStringField(raw, "type", allowed, context);
|
|
2621
|
+
if (type === "text") {
|
|
2622
|
+
MeerkatClient.requirePresentStringField(raw, "text", context);
|
|
2623
|
+
}
|
|
2624
|
+
else if (type === "image") {
|
|
2625
|
+
MeerkatClient.requirePresentStringField(raw, "media_type", context);
|
|
2626
|
+
}
|
|
2627
|
+
else if (type === "video") {
|
|
2628
|
+
MeerkatClient.requirePresentStringField(raw, "media_type", context);
|
|
2629
|
+
MeerkatClient.requireNonNegativeIntegerField(raw, "duration_ms", context);
|
|
2630
|
+
}
|
|
2631
|
+
else if (type === "structured") {
|
|
2632
|
+
MeerkatClient.requireOwnField(raw, "data", context);
|
|
2633
|
+
}
|
|
2634
|
+
else if (type === "skill_context") {
|
|
2635
|
+
const skillKey = MeerkatClient.requireRecord(raw.skill_key, "skill_key", context);
|
|
2636
|
+
MeerkatClient.requireStringField(skillKey, "source_uuid", `${context}: skill_key`);
|
|
2637
|
+
MeerkatClient.requireStringField(skillKey, "skill_name", `${context}: skill_key`);
|
|
2638
|
+
MeerkatClient.requirePresentStringField(raw, "text", context);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
static validateOptionalMetaRecord(raw, context) {
|
|
2642
|
+
if (Object.prototype.hasOwnProperty.call(raw, "meta")) {
|
|
2643
|
+
MeerkatClient.requireRecord(raw.meta, "meta", context);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
static validateToolConfigStatus(raw, context) {
|
|
2647
|
+
const status = MeerkatClient.requireRecord(raw, "status_info", context);
|
|
2648
|
+
const kind = MeerkatClient.requireClosedStringField(status, "kind", [
|
|
2649
|
+
"boundary_applied",
|
|
2650
|
+
"deferred_catalog_delta",
|
|
2651
|
+
"warning_failed_closed",
|
|
2652
|
+
"external_tool_delta",
|
|
2653
|
+
], context);
|
|
2654
|
+
if (kind === "boundary_applied") {
|
|
2655
|
+
MeerkatClient.requireBooleanField(status, "base_changed", context);
|
|
2656
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "revision", context);
|
|
2657
|
+
MeerkatClient.requireBooleanField(status, "visible_changed", context);
|
|
2658
|
+
}
|
|
2659
|
+
else if (kind === "deferred_catalog_delta") {
|
|
2660
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "added_hidden_count", context);
|
|
2661
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "pending_source_count", context);
|
|
2662
|
+
MeerkatClient.requireNonNegativeIntegerField(status, "removed_hidden_count", context);
|
|
2663
|
+
}
|
|
2664
|
+
else if (kind === "warning_failed_closed") {
|
|
2665
|
+
MeerkatClient.requirePresentStringField(status, "error", context);
|
|
2666
|
+
}
|
|
2667
|
+
else {
|
|
2668
|
+
MeerkatClient.requireClosedStringField(status, "phase", ["pending", "applied", "draining", "forced", "failed"], context);
|
|
2669
|
+
MeerkatClient.validateOptionalStringField(status, "detail", context);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
static validateToolConfigPayload(raw, context) {
|
|
2673
|
+
const payload = MeerkatClient.requireRecord(raw, "payload", context);
|
|
2674
|
+
MeerkatClient.requireClosedStringField(payload, "operation", ["add", "remove", "reload"], context);
|
|
2675
|
+
MeerkatClient.requireBooleanField(payload, "persisted", context);
|
|
2676
|
+
MeerkatClient.requirePresentStringField(payload, "target", context);
|
|
2677
|
+
MeerkatClient.validateToolConfigStatus(payload.status_info, `${context}: status_info`);
|
|
2678
|
+
if (Object.prototype.hasOwnProperty.call(payload, "applied_at_turn")) {
|
|
2679
|
+
MeerkatClient.requireNonNegativeIntegerField(payload, "applied_at_turn", context);
|
|
2680
|
+
}
|
|
2681
|
+
if (Object.prototype.hasOwnProperty.call(payload, "domain")) {
|
|
2682
|
+
MeerkatClient.requireClosedStringField(payload, "domain", ["tool_scope", "deferred_catalog"], context);
|
|
2683
|
+
}
|
|
2684
|
+
if (Object.prototype.hasOwnProperty.call(payload, "deferred_catalog_delta")) {
|
|
2685
|
+
const delta = MeerkatClient.requireRecord(payload.deferred_catalog_delta, "deferred_catalog_delta", context);
|
|
2686
|
+
for (const field of [
|
|
2687
|
+
"added_hidden_names",
|
|
2688
|
+
"pending_sources",
|
|
2689
|
+
"removed_hidden_names",
|
|
2690
|
+
]) {
|
|
2691
|
+
if (Object.prototype.hasOwnProperty.call(delta, field)) {
|
|
2692
|
+
MeerkatClient.requireStringArray(delta[field], `${context}: ${field}`);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
static validateSystemNoticeBlock(raw, context) {
|
|
2698
|
+
const type = MeerkatClient.requireClosedStringField(raw, "type", [
|
|
2699
|
+
"comms",
|
|
2700
|
+
"external_event",
|
|
2701
|
+
"tool_config",
|
|
2702
|
+
"mcp",
|
|
2703
|
+
"background_job",
|
|
2704
|
+
"auth",
|
|
2705
|
+
"runtime_notice",
|
|
2706
|
+
"unknown",
|
|
2707
|
+
], context);
|
|
2708
|
+
const validateContent = () => {
|
|
2709
|
+
if (!Object.prototype.hasOwnProperty.call(raw, "content"))
|
|
2710
|
+
return;
|
|
2711
|
+
MeerkatClient.requireRecordArray(raw.content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, true));
|
|
2712
|
+
};
|
|
2713
|
+
if (type === "comms") {
|
|
2714
|
+
MeerkatClient.requireClosedStringField(raw, "direction", ["incoming", "outgoing", "internal"], context);
|
|
2715
|
+
MeerkatClient.requirePresentStringField(raw, "kind", context);
|
|
2716
|
+
for (const field of ["intent", "request_id", "status", "summary"]) {
|
|
2717
|
+
MeerkatClient.validateOptionalStringField(raw, field, context);
|
|
2718
|
+
}
|
|
2719
|
+
if (Object.prototype.hasOwnProperty.call(raw, "peer")) {
|
|
2720
|
+
const peer = MeerkatClient.requireRecord(raw.peer, "peer", context);
|
|
2721
|
+
MeerkatClient.requireStringField(peer, "id", `${context}: peer`);
|
|
2722
|
+
MeerkatClient.validateOptionalStringField(peer, "display_name", `${context}: peer`);
|
|
2723
|
+
}
|
|
2724
|
+
if (Object.prototype.hasOwnProperty.call(raw, "sender_taint")) {
|
|
2725
|
+
MeerkatClient.requireClosedStringField(raw, "sender_taint", ["clean", "tainted"], context);
|
|
2726
|
+
}
|
|
2727
|
+
validateContent();
|
|
2728
|
+
}
|
|
2729
|
+
else if (type === "external_event") {
|
|
2730
|
+
MeerkatClient.requirePresentStringField(raw, "event_type", context);
|
|
2731
|
+
MeerkatClient.requirePresentStringField(raw, "source", context);
|
|
2732
|
+
MeerkatClient.validateOptionalStringField(raw, "body", context);
|
|
2733
|
+
MeerkatClient.validateOptionalStringField(raw, "summary", context);
|
|
2734
|
+
validateContent();
|
|
2735
|
+
}
|
|
2736
|
+
else if (type === "tool_config") {
|
|
2737
|
+
MeerkatClient.validateToolConfigPayload(raw.payload, `${context}: payload`);
|
|
2738
|
+
}
|
|
2739
|
+
else if (type === "mcp") {
|
|
2740
|
+
for (const field of ["detail", "server_id"]) {
|
|
2741
|
+
MeerkatClient.validateOptionalStringField(raw, field, context);
|
|
2742
|
+
}
|
|
2743
|
+
if (Object.prototype.hasOwnProperty.call(raw, "operation")) {
|
|
2744
|
+
MeerkatClient.requireClosedStringField(raw, "operation", ["add", "remove", "reload"], context);
|
|
2745
|
+
}
|
|
2746
|
+
if (Object.prototype.hasOwnProperty.call(raw, "pending_sources")) {
|
|
2747
|
+
MeerkatClient.requireStringArray(raw.pending_sources, `${context}: pending_sources`);
|
|
2748
|
+
}
|
|
2749
|
+
MeerkatClient.validateOptionalBooleanField(raw, "persisted", context);
|
|
2750
|
+
if (Object.prototype.hasOwnProperty.call(raw, "phase")) {
|
|
2751
|
+
MeerkatClient.requireClosedStringField(raw, "phase", ["pending", "applied", "draining", "forced", "failed"], context);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
else if (type === "background_job") {
|
|
2755
|
+
MeerkatClient.requireStringField(raw, "job_id", context);
|
|
2756
|
+
MeerkatClient.requireClosedStringField(raw, "status", ["completed", "failed", "aborted", "cancelled", "retired", "terminated"], context);
|
|
2757
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2758
|
+
MeerkatClient.validateOptionalStringField(raw, "display_name", context);
|
|
2759
|
+
}
|
|
2760
|
+
else if (type === "auth") {
|
|
2761
|
+
MeerkatClient.requirePresentStringField(raw, "state", context);
|
|
2762
|
+
MeerkatClient.validateOptionalStringField(raw, "binding", context);
|
|
2763
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2764
|
+
}
|
|
2765
|
+
else if (type === "runtime_notice") {
|
|
2766
|
+
MeerkatClient.requirePresentStringField(raw, "category", context);
|
|
2767
|
+
MeerkatClient.validateOptionalStringField(raw, "detail", context);
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
MeerkatClient.validateOptionalStringField(raw, "summary", context);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
static validateWireAssistantBlock(raw, context) {
|
|
2774
|
+
const blockType = MeerkatClient.requireClosedStringField(raw, "block_type", [
|
|
2775
|
+
"text",
|
|
2776
|
+
"transcript",
|
|
2777
|
+
"reasoning",
|
|
2778
|
+
"tool_use",
|
|
2779
|
+
"server_tool_content",
|
|
2780
|
+
"image",
|
|
2781
|
+
"unknown",
|
|
2782
|
+
], context);
|
|
2783
|
+
if (blockType === "unknown")
|
|
2784
|
+
return;
|
|
2785
|
+
const data = MeerkatClient.requireRecord(raw.data, "data", context);
|
|
2786
|
+
MeerkatClient.validateOptionalMetaRecord(data, `${context}: data`);
|
|
2787
|
+
if (blockType === "text") {
|
|
2788
|
+
MeerkatClient.requirePresentStringField(data, "text", `${context}: data`);
|
|
2789
|
+
}
|
|
2790
|
+
else if (blockType === "transcript") {
|
|
2791
|
+
MeerkatClient.requirePresentStringField(data, "text", `${context}: data`);
|
|
2792
|
+
const source = MeerkatClient.requireRecord(data.source, "source", `${context}: data`);
|
|
2793
|
+
const sourceKind = MeerkatClient.requireClosedStringField(source, "kind", ["spoken", "unknown"], `${context}: data.source`);
|
|
2794
|
+
if (sourceKind === "unknown") {
|
|
2795
|
+
MeerkatClient.requirePresentStringField(source, "debug", `${context}: data.source`);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
else if (blockType === "reasoning") {
|
|
2799
|
+
MeerkatClient.validateOptionalStringField(data, "text", `${context}: data`);
|
|
2800
|
+
}
|
|
2801
|
+
else if (blockType === "tool_use") {
|
|
2802
|
+
MeerkatClient.requireOwnField(data, "args", `${context}: data`);
|
|
2803
|
+
MeerkatClient.requireStringField(data, "id", `${context}: data`);
|
|
2804
|
+
MeerkatClient.requireStringField(data, "name", `${context}: data`);
|
|
2805
|
+
}
|
|
2806
|
+
else if (blockType === "server_tool_content") {
|
|
2807
|
+
MeerkatClient.requireOwnField(data, "content", `${context}: data`);
|
|
2808
|
+
MeerkatClient.requireRecord(data.kind, "kind", `${context}: data`);
|
|
2809
|
+
MeerkatClient.validateOptionalStringField(data, "id", `${context}: data`);
|
|
2810
|
+
}
|
|
2811
|
+
else {
|
|
2812
|
+
MeerkatClient.requireRecord(data.blob_ref, "blob_ref", `${context}: data`);
|
|
2813
|
+
MeerkatClient.requireNonNegativeIntegerField(data, "height", `${context}: data`);
|
|
2814
|
+
MeerkatClient.requireStringField(data, "image_id", `${context}: data`);
|
|
2815
|
+
MeerkatClient.requirePresentStringField(data, "media_type", `${context}: data`);
|
|
2816
|
+
MeerkatClient.requireRecord(data.meta, "meta", `${context}: data`);
|
|
2817
|
+
MeerkatClient.requireRecord(data.revised_prompt, "revised_prompt", `${context}: data`);
|
|
2818
|
+
MeerkatClient.requireNonNegativeIntegerField(data, "width", `${context}: data`);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
static validateWireToolResult(raw, context) {
|
|
2822
|
+
MeerkatClient.requireStringField(raw, "tool_use_id", context);
|
|
2823
|
+
const content = MeerkatClient.requireOwnField(raw, "content", context);
|
|
2824
|
+
if (typeof content !== "string" && !Array.isArray(content)) {
|
|
2825
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: invalid content`);
|
|
2826
|
+
}
|
|
2827
|
+
if (Array.isArray(content)) {
|
|
2828
|
+
MeerkatClient.requireRecordArray(content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, false));
|
|
2829
|
+
}
|
|
2830
|
+
MeerkatClient.validateOptionalBooleanField(raw, "is_error", context);
|
|
2831
|
+
}
|
|
2832
|
+
static validateWireHistoryRow(raw, context) {
|
|
2833
|
+
const role = MeerkatClient.requireStringField(raw, "role", context);
|
|
2834
|
+
MeerkatClient.requireStringField(raw, "created_at", context);
|
|
2835
|
+
for (const field of ["interaction_id", "run_id"]) {
|
|
2836
|
+
if (Object.prototype.hasOwnProperty.call(raw, field) &&
|
|
2837
|
+
(typeof raw[field] !== "string" || raw[field].length === 0)) {
|
|
2838
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: ${field} must be non-empty string`);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
if (role === "system") {
|
|
2842
|
+
MeerkatClient.requirePresentStringField(raw, "content", context);
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
if (role === "system_notice") {
|
|
2846
|
+
const kind = MeerkatClient.requireStringField(raw, "kind", context);
|
|
2847
|
+
if (![
|
|
2848
|
+
"generic",
|
|
2849
|
+
"comms",
|
|
2850
|
+
"external_event",
|
|
2851
|
+
"mcp_pending",
|
|
2852
|
+
"mcp",
|
|
2853
|
+
"background_job",
|
|
2854
|
+
"tool_scope",
|
|
2855
|
+
"tool_scope_warning",
|
|
2856
|
+
"auth_reauth_required",
|
|
2857
|
+
].includes(kind)) {
|
|
2858
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported kind ${JSON.stringify(kind)}`);
|
|
2859
|
+
}
|
|
2860
|
+
MeerkatClient.validateOptionalStringField(raw, "body", context);
|
|
2861
|
+
if (Object.prototype.hasOwnProperty.call(raw, "blocks")) {
|
|
2862
|
+
MeerkatClient.requireRecordArray(raw.blocks, `${context}: blocks`).forEach((block, index) => MeerkatClient.validateSystemNoticeBlock(block, `${context}: blocks[${index}]`));
|
|
2863
|
+
}
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
if (role === "user") {
|
|
2867
|
+
if (raw.content == null || (!Array.isArray(raw.content) && typeof raw.content !== "string")) {
|
|
2868
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: missing content`);
|
|
2869
|
+
}
|
|
2870
|
+
if (Array.isArray(raw.content)) {
|
|
2871
|
+
MeerkatClient.requireRecordArray(raw.content, `${context}: content`).forEach((block, index) => MeerkatClient.validateGeneratedContentBlock(block, `${context}: content[${index}]`, false));
|
|
2872
|
+
}
|
|
2873
|
+
if (Object.prototype.hasOwnProperty.call(raw, "transcript_role")) {
|
|
2874
|
+
MeerkatClient.requireClosedStringField(raw, "transcript_role", ["conversational", "compaction_summary", "injected_context"], context);
|
|
2875
|
+
}
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
if (role === "block_assistant") {
|
|
2879
|
+
MeerkatClient.requireRecordArray(raw.blocks, `${context}: blocks`).forEach((block, index) => MeerkatClient.validateWireAssistantBlock(block, `${context}: blocks[${index}]`));
|
|
2880
|
+
const stopReason = MeerkatClient.requireStringField(raw, "stop_reason", context);
|
|
2881
|
+
if (![
|
|
2882
|
+
"end_turn",
|
|
2883
|
+
"tool_use",
|
|
2884
|
+
"max_tokens",
|
|
2885
|
+
"stop_sequence",
|
|
2886
|
+
"content_filter",
|
|
2887
|
+
"cancelled",
|
|
2888
|
+
].includes(stopReason)) {
|
|
2889
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported stop_reason ${JSON.stringify(stopReason)}`);
|
|
2890
|
+
}
|
|
2891
|
+
return;
|
|
2892
|
+
}
|
|
2893
|
+
if (role === "tool_results") {
|
|
2894
|
+
MeerkatClient.requireRecordArray(raw.results, `${context}: results`).forEach((result, index) => MeerkatClient.validateWireToolResult(result, `${context}: results[${index}]`));
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported role ${JSON.stringify(role)}`);
|
|
2898
|
+
}
|
|
2899
|
+
static parseMobHostCapabilities(raw, context) {
|
|
2900
|
+
const capabilities = MeerkatClient.requireRecord(raw, "capabilities", context);
|
|
2901
|
+
for (const field of [
|
|
2902
|
+
"approval_forwarding",
|
|
2903
|
+
"autonomous_members",
|
|
2904
|
+
"durable_sessions",
|
|
2905
|
+
"hard_cancel_member",
|
|
2906
|
+
"mcp",
|
|
2907
|
+
"memory_store",
|
|
2908
|
+
]) {
|
|
2909
|
+
MeerkatClient.requireBooleanField(capabilities, field, context);
|
|
2910
|
+
}
|
|
2911
|
+
const trackedInputCancel = Object.prototype.hasOwnProperty.call(capabilities, "tracked_input_cancel")
|
|
2912
|
+
? MeerkatClient.requireBooleanField(capabilities, "tracked_input_cancel", context)
|
|
2913
|
+
: false;
|
|
2914
|
+
MeerkatClient.requireStringField(capabilities, "engine_version", context);
|
|
2915
|
+
const protocolMin = MeerkatClient.requireNonNegativeIntegerField(capabilities, "protocol_min", context);
|
|
2916
|
+
const protocolMax = MeerkatClient.requireNonNegativeIntegerField(capabilities, "protocol_max", context);
|
|
2917
|
+
if (protocolMin > protocolMax) {
|
|
2918
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: protocol_min exceeds protocol_max`);
|
|
2919
|
+
}
|
|
2920
|
+
const liveEndpoint = MeerkatClient.optionalStringField(capabilities, "live_endpoint", context);
|
|
2921
|
+
if (liveEndpoint === "") {
|
|
2922
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: live_endpoint must be non-empty`);
|
|
2923
|
+
}
|
|
2924
|
+
const resolvableProviders = Object.prototype.hasOwnProperty.call(capabilities, "resolvable_providers")
|
|
2925
|
+
? MeerkatClient.requireStringArray(capabilities.resolvable_providers, `${context}: resolvable_providers`)
|
|
2926
|
+
: undefined;
|
|
2927
|
+
return {
|
|
2928
|
+
...capabilities,
|
|
2929
|
+
tracked_input_cancel: trackedInputCancel,
|
|
2930
|
+
...(resolvableProviders !== undefined
|
|
2931
|
+
? { resolvable_providers: resolvableProviders }
|
|
2932
|
+
: {}),
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
static parseMobHostStatus(raw, context) {
|
|
2936
|
+
const host = MeerkatClient.requireRecord(raw, "host", context);
|
|
2937
|
+
const bindPhase = MeerkatClient.requireStringField(host, "bind_phase", context);
|
|
2938
|
+
if (bindPhase !== "requested" && bindPhase !== "bound") {
|
|
2939
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported bind_phase ${JSON.stringify(bindPhase)}`);
|
|
2940
|
+
}
|
|
2941
|
+
MeerkatClient.requireStringField(host, "host_id", context);
|
|
2942
|
+
MeerkatClient.requireNonNegativeIntegerField(host, "materialized_member_count", context);
|
|
2943
|
+
const authorityEpoch = host.authority_epoch == null
|
|
2944
|
+
? undefined
|
|
2945
|
+
: MeerkatClient.requireNonNegativeIntegerField(host, "authority_epoch", context);
|
|
2946
|
+
const endpoint = MeerkatClient.optionalStringField(host, "endpoint", context);
|
|
2947
|
+
const capabilities = host.capabilities == null
|
|
2948
|
+
? undefined
|
|
2949
|
+
: MeerkatClient.parseMobHostCapabilities(host.capabilities, `${context}: capabilities`);
|
|
2950
|
+
if (bindPhase === "bound" &&
|
|
2951
|
+
(authorityEpoch === undefined || !endpoint || capabilities === undefined)) {
|
|
2952
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: bound host missing committed authority facts`);
|
|
2953
|
+
}
|
|
2954
|
+
if (bindPhase === "requested" &&
|
|
2955
|
+
(authorityEpoch !== undefined || endpoint !== undefined || capabilities !== undefined)) {
|
|
2956
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: requested host must not claim committed authority facts`);
|
|
2957
|
+
}
|
|
2958
|
+
if (host.control_reachability != null &&
|
|
2959
|
+
!["reachable", "stale", "unreachable", "unknown"].includes(String(host.control_reachability))) {
|
|
2960
|
+
throw new MeerkatError("INVALID_RESPONSE", `${context}: unsupported control_reachability`);
|
|
2961
|
+
}
|
|
2962
|
+
if (host.last_seen_ms != null) {
|
|
2963
|
+
MeerkatClient.requireNonNegativeIntegerField(host, "last_seen_ms", context);
|
|
2964
|
+
}
|
|
2965
|
+
MeerkatClient.optionalStringField(host, "freshness_reason", context);
|
|
2966
|
+
return (capabilities === undefined
|
|
2967
|
+
? host
|
|
2968
|
+
: { ...host, capabilities });
|
|
2969
|
+
}
|
|
2970
|
+
static parseRouteInstallObligation(raw, context) {
|
|
2971
|
+
const obligation = MeerkatClient.requireRecord(raw, "obligation", context);
|
|
2972
|
+
MeerkatClient.requireStringField(obligation, "edge_a", context);
|
|
2973
|
+
MeerkatClient.requireStringField(obligation, "edge_b", context);
|
|
2974
|
+
MeerkatClient.requireStringField(obligation, "host", context);
|
|
2975
|
+
return obligation;
|
|
2976
|
+
}
|
|
2310
2977
|
static requireBooleanField(raw, field, context) {
|
|
2311
2978
|
const value = raw[field];
|
|
2312
2979
|
if (typeof value !== "boolean") {
|
|
@@ -2333,6 +3000,21 @@ export class MeerkatClient {
|
|
|
2333
3000
|
}
|
|
2334
3001
|
throw new MeerkatError("INVALID_RESPONSE", message);
|
|
2335
3002
|
}
|
|
3003
|
+
static parseWireReachability(raw, field) {
|
|
3004
|
+
if (raw == null) {
|
|
3005
|
+
return undefined;
|
|
3006
|
+
}
|
|
3007
|
+
const values = [
|
|
3008
|
+
"reachable",
|
|
3009
|
+
"stale",
|
|
3010
|
+
"unreachable",
|
|
3011
|
+
"unknown",
|
|
3012
|
+
];
|
|
3013
|
+
if (typeof raw === "string" && values.includes(raw)) {
|
|
3014
|
+
return raw;
|
|
3015
|
+
}
|
|
3016
|
+
throw new MeerkatError("INVALID_RESPONSE", `Invalid mob/member_status response: ${field} must be a valid reachability`);
|
|
3017
|
+
}
|
|
2336
3018
|
static parseMobPeerConnectivitySnapshot(raw, context) {
|
|
2337
3019
|
const record = MeerkatClient.requireRecord(raw, "peer_connectivity.snapshot", context);
|
|
2338
3020
|
const unreachableRaw = record.unreachable_peers;
|
|
@@ -2427,6 +3109,85 @@ export class MeerkatClient {
|
|
|
2427
3109
|
status: MeerkatClient.parseWireCapabilityStatus(record.status, context),
|
|
2428
3110
|
};
|
|
2429
3111
|
}
|
|
3112
|
+
/**
|
|
3113
|
+
* Validate the generated `LiveOpenResult` contract without projecting or
|
|
3114
|
+
* normalizing its transport bootstrap. In particular, single-use tokens are
|
|
3115
|
+
* returned byte-for-byte as received and are never logged.
|
|
3116
|
+
*/
|
|
3117
|
+
static parseLiveOpenResult(raw, context) {
|
|
3118
|
+
const result = MeerkatClient.requireRecord(raw, "result", context);
|
|
3119
|
+
MeerkatClient.requireStringField(result, "channel_id", context);
|
|
3120
|
+
const capabilities = MeerkatClient.requireRecord(result.capabilities, "capabilities", context);
|
|
3121
|
+
for (const field of [
|
|
3122
|
+
"audio_in",
|
|
3123
|
+
"audio_out",
|
|
3124
|
+
"barge_in_supported",
|
|
3125
|
+
"image_in",
|
|
3126
|
+
"provider_native_resume",
|
|
3127
|
+
"text_in",
|
|
3128
|
+
"text_out",
|
|
3129
|
+
"transcript_supported",
|
|
3130
|
+
"video_in",
|
|
3131
|
+
]) {
|
|
3132
|
+
MeerkatClient.requireBooleanField(capabilities, field, `${context}: capabilities`);
|
|
3133
|
+
}
|
|
3134
|
+
const continuity = MeerkatClient.requireRecord(result.continuity, "continuity", context);
|
|
3135
|
+
const continuityMode = MeerkatClient.requireClosedStringField(continuity, "mode", [
|
|
3136
|
+
"fresh",
|
|
3137
|
+
"transcript_only",
|
|
3138
|
+
"degraded",
|
|
3139
|
+
"provider_native_resume",
|
|
3140
|
+
"unknown",
|
|
3141
|
+
], `${context}: continuity`);
|
|
3142
|
+
if (continuityMode === "provider_native_resume") {
|
|
3143
|
+
MeerkatClient.requireStringField(continuity, "provider_session_id", `${context}: continuity`);
|
|
3144
|
+
}
|
|
3145
|
+
else if (continuityMode === "unknown") {
|
|
3146
|
+
MeerkatClient.requirePresentStringField(continuity, "debug", `${context}: continuity`);
|
|
3147
|
+
}
|
|
3148
|
+
const transport = MeerkatClient.requireRecord(result.transport, "transport", context);
|
|
3149
|
+
const transportKind = MeerkatClient.requireClosedStringField(transport, "transport", ["websocket", "webrtc", "unknown"], `${context}: transport`);
|
|
3150
|
+
if (transportKind === "websocket") {
|
|
3151
|
+
MeerkatClient.requireStringField(transport, "url", `${context}: transport`);
|
|
3152
|
+
MeerkatClient.requireStringField(transport, "token", `${context}: transport`);
|
|
3153
|
+
}
|
|
3154
|
+
else if (transportKind === "webrtc") {
|
|
3155
|
+
MeerkatClient.requireStringField(transport, "answer_method", `${context}: transport`);
|
|
3156
|
+
MeerkatClient.requireStringField(transport, "token", `${context}: transport`);
|
|
3157
|
+
MeerkatClient.validateOptionalStringField(transport, "http_url", `${context}: transport`);
|
|
3158
|
+
}
|
|
3159
|
+
else {
|
|
3160
|
+
MeerkatClient.requirePresentStringField(transport, "debug", `${context}: transport`);
|
|
3161
|
+
}
|
|
3162
|
+
return result;
|
|
3163
|
+
}
|
|
3164
|
+
/** Validate the generated `LiveStatusResult` closed status vocabulary. */
|
|
3165
|
+
static parseLiveStatusResult(raw, context) {
|
|
3166
|
+
const result = MeerkatClient.requireRecord(raw, "result", context);
|
|
3167
|
+
MeerkatClient.requireStringField(result, "channel_id", context);
|
|
3168
|
+
const status = MeerkatClient.requireRecord(result.status, "status", context);
|
|
3169
|
+
const statusKind = MeerkatClient.requireClosedStringField(status, "status", ["idle", "opening", "ready", "degraded", "closing", "closed", "unknown"], `${context}: status`);
|
|
3170
|
+
if (statusKind === "degraded") {
|
|
3171
|
+
const reason = MeerkatClient.requireRecord(status.reason, "reason", `${context}: status`);
|
|
3172
|
+
const reasonKind = MeerkatClient.requireClosedStringField(reason, "kind", [
|
|
3173
|
+
"rate_limited",
|
|
3174
|
+
"provider_throttled",
|
|
3175
|
+
"network_unstable",
|
|
3176
|
+
"other",
|
|
3177
|
+
"unknown",
|
|
3178
|
+
], `${context}: status.reason`);
|
|
3179
|
+
if (reasonKind === "other") {
|
|
3180
|
+
MeerkatClient.requirePresentStringField(reason, "detail", `${context}: status.reason`);
|
|
3181
|
+
}
|
|
3182
|
+
else if (reasonKind === "unknown") {
|
|
3183
|
+
MeerkatClient.requirePresentStringField(reason, "debug", `${context}: status.reason`);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
else if (statusKind === "unknown") {
|
|
3187
|
+
MeerkatClient.requirePresentStringField(status, "debug", `${context}: status`);
|
|
3188
|
+
}
|
|
3189
|
+
return result;
|
|
3190
|
+
}
|
|
2430
3191
|
static parseLiveRefreshResult(raw) {
|
|
2431
3192
|
const context = "Invalid live/refresh response";
|
|
2432
3193
|
const record = MeerkatClient.requireRecord(raw, "result", context);
|
|
@@ -2700,6 +3461,37 @@ export class MeerkatClient {
|
|
|
2700
3461
|
}
|
|
2701
3462
|
return progress;
|
|
2702
3463
|
}
|
|
3464
|
+
static parseMemberLifecycleCapabilities(raw) {
|
|
3465
|
+
if (raw == null) {
|
|
3466
|
+
return undefined;
|
|
3467
|
+
}
|
|
3468
|
+
const context = "Invalid mob/member_status response";
|
|
3469
|
+
const record = MeerkatClient.requireRecord(raw, "lifecycle_capabilities", context);
|
|
3470
|
+
return {
|
|
3471
|
+
transcript_edits: MeerkatClient.requireBooleanField(record, "transcript_edits", context),
|
|
3472
|
+
revisions: MeerkatClient.requireBooleanField(record, "revisions", context),
|
|
3473
|
+
resume_after_restart: MeerkatClient.requireBooleanField(record, "resume_after_restart", context),
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
static parseNonPortableDisabled(raw) {
|
|
3477
|
+
if (raw == null) {
|
|
3478
|
+
return undefined;
|
|
3479
|
+
}
|
|
3480
|
+
const values = [
|
|
3481
|
+
"rust_bundles",
|
|
3482
|
+
"per_spawn_external_tools",
|
|
3483
|
+
"mob_default_external_tools",
|
|
3484
|
+
"default_llm_client_override",
|
|
3485
|
+
"host_surface_mcp_allowlist",
|
|
3486
|
+
"workgraph_tools",
|
|
3487
|
+
];
|
|
3488
|
+
if (!Array.isArray(raw) ||
|
|
3489
|
+
raw.some((entry) => typeof entry !== "string" ||
|
|
3490
|
+
!values.includes(entry))) {
|
|
3491
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid mob/member_status response: non_portable_disabled must contain valid resource kinds");
|
|
3492
|
+
}
|
|
3493
|
+
return raw;
|
|
3494
|
+
}
|
|
2703
3495
|
static parseModelsCatalog(data) {
|
|
2704
3496
|
const context = "Invalid models/catalog response";
|
|
2705
3497
|
const contractVersion = MeerkatClient.parseContractVersion(data.contract_version, context);
|
|
@@ -2773,11 +3565,13 @@ export class MeerkatClient {
|
|
|
2773
3565
|
}
|
|
2774
3566
|
static parseContractVersion(raw, context) {
|
|
2775
3567
|
const parseComponent = (value, field) => {
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
3568
|
+
const parsed = typeof value === "number"
|
|
3569
|
+
? value
|
|
3570
|
+
: typeof value === "string" && /^\d+$/.test(value)
|
|
3571
|
+
? Number(value)
|
|
3572
|
+
: undefined;
|
|
3573
|
+
if (typeof parsed === "number" && Number.isSafeInteger(parsed) && parsed >= 0) {
|
|
3574
|
+
return parsed;
|
|
2781
3575
|
}
|
|
2782
3576
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: contract_version.${field} must be non-negative integer`);
|
|
2783
3577
|
};
|
|
@@ -3006,10 +3800,8 @@ export class MeerkatClient {
|
|
|
3006
3800
|
}
|
|
3007
3801
|
static parseSessionHistory(data) {
|
|
3008
3802
|
const context = "Invalid session history response";
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
}
|
|
3012
|
-
const rawMessages = data.messages;
|
|
3803
|
+
const rawMessages = MeerkatClient.requireRecordArray(data.messages, `${context}: messages`);
|
|
3804
|
+
rawMessages.forEach((message, index) => MeerkatClient.validateWireHistoryRow(message, `${context}: messages[${index}]`));
|
|
3013
3805
|
return {
|
|
3014
3806
|
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
3015
3807
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
@@ -3022,10 +3814,8 @@ export class MeerkatClient {
|
|
|
3022
3814
|
}
|
|
3023
3815
|
static parseSessionTranscriptRevision(data) {
|
|
3024
3816
|
const context = "Invalid session transcript revision response";
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
}
|
|
3028
|
-
const rawMessages = data.messages;
|
|
3817
|
+
const rawMessages = MeerkatClient.requireRecordArray(data.messages, `${context}: messages`);
|
|
3818
|
+
rawMessages.forEach((message, index) => MeerkatClient.validateWireHistoryRow(message, `${context}: messages[${index}]`));
|
|
3029
3819
|
return {
|
|
3030
3820
|
sessionId: MeerkatClient.requireStringField(data, "session_id", context),
|
|
3031
3821
|
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
@@ -3135,7 +3925,12 @@ export class MeerkatClient {
|
|
|
3135
3925
|
stopReason: data.stop_reason != null ? String(data.stop_reason) : undefined,
|
|
3136
3926
|
interactionId: data.interaction_id != null ? String(data.interaction_id) : undefined,
|
|
3137
3927
|
runId: data.run_id != null ? String(data.run_id) : undefined,
|
|
3138
|
-
blocks
|
|
3928
|
+
// System-notice blocks have their own generated union and remain
|
|
3929
|
+
// available in `raw`; only block-assistant rows project through the
|
|
3930
|
+
// public assistant-block view.
|
|
3931
|
+
blocks: role === "block_assistant"
|
|
3932
|
+
? rawBlocks.map((block) => MeerkatClient.parseSessionAssistantBlock(block))
|
|
3933
|
+
: [],
|
|
3139
3934
|
results: rawResults.map((result) => {
|
|
3140
3935
|
if (typeof result !== "object" || result === null) {
|
|
3141
3936
|
throw new MeerkatError("INVALID_RESPONSE", `${context}: tool result must be an object`);
|
|
@@ -3230,60 +4025,22 @@ export class MeerkatClient {
|
|
|
3230
4025
|
}
|
|
3231
4026
|
static parseContentInput(value) {
|
|
3232
4027
|
if (Array.isArray(value)) {
|
|
3233
|
-
return value
|
|
3234
|
-
.filter((item) => typeof item === "object" && item !== null)
|
|
3235
|
-
.map((block) => MeerkatClient.parseContentBlock(block));
|
|
4028
|
+
return value.map((item, index) => MeerkatClient.parseContentBlock(MeerkatClient.requireRecord(item, `content[${index}]`, "Invalid session content")));
|
|
3236
4029
|
}
|
|
3237
|
-
|
|
4030
|
+
if (typeof value === "string")
|
|
4031
|
+
return value;
|
|
4032
|
+
throw new MeerkatError("INVALID_RESPONSE", "Invalid session content: expected string or content-block array");
|
|
3238
4033
|
}
|
|
3239
4034
|
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: "" };
|
|
4035
|
+
MeerkatClient.validateGeneratedContentBlock(data, "Invalid session content block", false);
|
|
4036
|
+
// Preserve the validated wire object exactly. In particular, do not
|
|
4037
|
+
// invent inline bytes for history-only image/video projections or collapse
|
|
4038
|
+
// structured/unknown variants into empty text.
|
|
4039
|
+
return { ...data };
|
|
3281
4040
|
}
|
|
3282
4041
|
static parseSessionAssistantBlock(data) {
|
|
3283
4042
|
const context = "Invalid session assistant block";
|
|
3284
|
-
|
|
3285
|
-
throw new MeerkatError("INVALID_RESPONSE", `${context}: data must be an object`);
|
|
3286
|
-
}
|
|
4043
|
+
MeerkatClient.validateWireAssistantBlock(data, context);
|
|
3287
4044
|
const blockData = data.data ?? {};
|
|
3288
4045
|
const blobRef = blockData.blob_ref;
|
|
3289
4046
|
const revisedPrompt = blockData.revised_prompt != null && typeof blockData.revised_prompt === "object"
|
|
@@ -3302,9 +4059,9 @@ export class MeerkatClient {
|
|
|
3302
4059
|
height: blockData.height != null ? Number(blockData.height) : undefined,
|
|
3303
4060
|
revisedPrompt,
|
|
3304
4061
|
meta: blockData.meta,
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
4062
|
+
source: data.block_type === "transcript"
|
|
4063
|
+
? { ...blockData.source }
|
|
4064
|
+
: undefined,
|
|
3308
4065
|
raw: { ...data },
|
|
3309
4066
|
};
|
|
3310
4067
|
}
|
|
@@ -3379,7 +4136,7 @@ export class MeerkatClient {
|
|
|
3379
4136
|
params.model = options.model;
|
|
3380
4137
|
if (options.provider)
|
|
3381
4138
|
params.provider = options.provider;
|
|
3382
|
-
if (options.systemPrompt)
|
|
4139
|
+
if (options.systemPrompt !== undefined)
|
|
3383
4140
|
params.system_prompt = options.systemPrompt;
|
|
3384
4141
|
if (options.maxTokens)
|
|
3385
4142
|
params.max_tokens = options.maxTokens;
|