@rkat/web 0.5.2 → 0.6.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/auth.d.ts +181 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +134 -0
- package/dist/auth.js.map +1 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +3 -0
- package/dist/events.js.map +1 -1
- package/dist/generated/auth.d.ts +247 -0
- package/dist/generated/auth.d.ts.map +1 -0
- package/dist/generated/auth.js +502 -0
- package/dist/generated/auth.js.map +1 -0
- package/dist/generated/events.d.ts +232 -54
- package/dist/generated/events.d.ts.map +1 -1
- package/dist/generated/events.js +5 -3
- package/dist/generated/events.js.map +1 -1
- package/dist/generated/mob.d.ts +50 -0
- package/dist/generated/mob.d.ts.map +1 -0
- package/dist/generated/mob.js +4 -0
- package/dist/generated/mob.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mob.d.ts +23 -21
- package/dist/mob.d.ts.map +1 -1
- package/dist/mob.js +510 -47
- package/dist/mob.js.map +1 -1
- package/dist/runtime.d.ts +8 -8
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +29 -8
- package/dist/runtime.js.map +1 -1
- package/dist/session.d.ts +9 -6
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +44 -33
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +91 -45
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +67 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/wasm/.meerkat-wasm-build.json +5 -0
- package/wasm/meerkat_web_runtime.d.ts +68 -34
- package/wasm/meerkat_web_runtime.js +132 -67
- package/wasm/meerkat_web_runtime_bg.wasm +0 -0
- package/wasm/meerkat_web_runtime_bg.wasm.d.ts +12 -8
- package/wasm/package.json +1 -1
package/dist/mob.js
CHANGED
|
@@ -1,35 +1,444 @@
|
|
|
1
1
|
import { EventSubscription } from './events.js';
|
|
2
|
+
function encodeBase64UrlJson(payload) {
|
|
3
|
+
const bytes = new TextEncoder().encode(JSON.stringify(payload));
|
|
4
|
+
let binary = '';
|
|
5
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
6
|
+
binary += String.fromCharCode(bytes[i]);
|
|
7
|
+
}
|
|
8
|
+
const b64 = btoa(binary);
|
|
9
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
10
|
+
}
|
|
11
|
+
function encodeMemberRef(mobId, agentIdentity) {
|
|
12
|
+
return encodeBase64UrlJson({ m: mobId, a: agentIdentity });
|
|
13
|
+
}
|
|
14
|
+
function spawnSpecPayload(spec) {
|
|
15
|
+
return {
|
|
16
|
+
profile: spec.profile,
|
|
17
|
+
agent_identity: spec.agent_identity,
|
|
18
|
+
runtime_mode: spec.runtime_mode,
|
|
19
|
+
initial_message: spec.initial_message,
|
|
20
|
+
labels: spec.labels,
|
|
21
|
+
context: spec.context,
|
|
22
|
+
additional_instructions: spec.additional_instructions,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function requireOnlyKeys(value, allowedKeys, message) {
|
|
29
|
+
const allowed = new Set(allowedKeys);
|
|
30
|
+
for (const key of Object.keys(value)) {
|
|
31
|
+
if (!allowed.has(key)) {
|
|
32
|
+
throw new Error(message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function parseJsonPayload(json, context) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(json);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`${context}: invalid JSON`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function requireRecord(value, message) {
|
|
45
|
+
if (!isRecord(value)) {
|
|
46
|
+
throw new Error(message);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
function requireStringField(value, field, message) {
|
|
51
|
+
const raw = value[field];
|
|
52
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
53
|
+
throw new Error(message);
|
|
54
|
+
}
|
|
55
|
+
return raw;
|
|
56
|
+
}
|
|
57
|
+
function optionalStringField(value, field, message) {
|
|
58
|
+
const raw = value[field];
|
|
59
|
+
if (raw == null) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
if (typeof raw !== 'string') {
|
|
63
|
+
throw new Error(message);
|
|
64
|
+
}
|
|
65
|
+
return raw;
|
|
66
|
+
}
|
|
67
|
+
function optionalNonEmptyStringField(value, field, message) {
|
|
68
|
+
const raw = optionalStringField(value, field, message);
|
|
69
|
+
if (raw === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (raw.length === 0) {
|
|
73
|
+
throw new Error(message);
|
|
74
|
+
}
|
|
75
|
+
return raw;
|
|
76
|
+
}
|
|
77
|
+
function requireNumberField(value, field, message) {
|
|
78
|
+
const raw = value[field];
|
|
79
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
|
|
80
|
+
throw new Error(message);
|
|
81
|
+
}
|
|
82
|
+
return raw;
|
|
83
|
+
}
|
|
84
|
+
function requireNonNegativeIntegerField(value, field, message) {
|
|
85
|
+
const raw = requireNumberField(value, field, message);
|
|
86
|
+
if (!Number.isInteger(raw) || raw < 0) {
|
|
87
|
+
throw new Error(message);
|
|
88
|
+
}
|
|
89
|
+
return raw;
|
|
90
|
+
}
|
|
91
|
+
function optionalNumberField(value, field, message) {
|
|
92
|
+
const raw = value[field];
|
|
93
|
+
if (raw == null) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
|
|
97
|
+
throw new Error(message);
|
|
98
|
+
}
|
|
99
|
+
return raw;
|
|
100
|
+
}
|
|
101
|
+
function requireBooleanField(value, field, message) {
|
|
102
|
+
const raw = value[field];
|
|
103
|
+
if (typeof raw !== 'boolean') {
|
|
104
|
+
throw new Error(message);
|
|
105
|
+
}
|
|
106
|
+
return raw;
|
|
107
|
+
}
|
|
108
|
+
function optionalRecordField(value, field, message) {
|
|
109
|
+
const raw = value[field];
|
|
110
|
+
if (raw == null) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return requireRecord(raw, message);
|
|
114
|
+
}
|
|
115
|
+
function optionalStringArrayField(value, field, message) {
|
|
116
|
+
const raw = value[field];
|
|
117
|
+
if (raw == null) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
if (!Array.isArray(raw) || raw.some((entry) => typeof entry !== 'string')) {
|
|
121
|
+
throw new Error(message);
|
|
122
|
+
}
|
|
123
|
+
return [...raw];
|
|
124
|
+
}
|
|
125
|
+
const WIRE_MOB_MEMBER_STATUSES = [
|
|
126
|
+
'active',
|
|
127
|
+
'retiring',
|
|
128
|
+
'broken',
|
|
129
|
+
'completed',
|
|
130
|
+
'unknown',
|
|
131
|
+
];
|
|
132
|
+
function parseWireMobMemberStatus(raw, message) {
|
|
133
|
+
if (typeof raw === 'string' &&
|
|
134
|
+
WIRE_MOB_MEMBER_STATUSES.includes(raw)) {
|
|
135
|
+
return raw;
|
|
136
|
+
}
|
|
137
|
+
throw new Error(message);
|
|
138
|
+
}
|
|
139
|
+
const WIRE_HANDLING_MODES = ['queue', 'steer'];
|
|
140
|
+
function parseWireHandlingMode(raw, message) {
|
|
141
|
+
if (typeof raw === 'string' && WIRE_HANDLING_MODES.includes(raw)) {
|
|
142
|
+
return raw;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(message);
|
|
145
|
+
}
|
|
146
|
+
export function parseMobStatusResult(raw, context = 'Invalid mob/status response') {
|
|
147
|
+
const record = requireRecord(raw, `${context}: malformed envelope`);
|
|
148
|
+
const mobId = requireStringField(record, 'mob_id', `${context}: missing mob_id`);
|
|
149
|
+
const status = requireStringField(record, 'status', `${context}: missing status`);
|
|
150
|
+
return {
|
|
151
|
+
mob_id: mobId,
|
|
152
|
+
status,
|
|
153
|
+
state: status,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function parseMobAppendSystemContextResult(raw) {
|
|
157
|
+
const record = requireRecord(raw, 'Invalid mob append_system_context response: malformed envelope');
|
|
158
|
+
const status = requireStringField(record, 'status', 'Invalid mob append_system_context response: missing status');
|
|
159
|
+
if (status !== 'staged' && status !== 'duplicate') {
|
|
160
|
+
throw new Error('Invalid mob append_system_context response: invalid status');
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
mob_id: requireStringField(record, 'mob_id', 'Invalid mob append_system_context response: missing mob_id'),
|
|
164
|
+
agent_identity: requireStringField(record, 'agent_identity', 'Invalid mob append_system_context response: missing agent_identity'),
|
|
165
|
+
status,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function normalizeSpawnManyEntry(raw, mobId) {
|
|
169
|
+
if (!isRecord(raw)) {
|
|
170
|
+
throw new Error('Invalid mob spawn response: malformed result entry');
|
|
171
|
+
}
|
|
172
|
+
if ('ok' in raw) {
|
|
173
|
+
throw new Error('Invalid mob spawn response: legacy ok result row');
|
|
174
|
+
}
|
|
175
|
+
requireOnlyKeys(raw, ['status', 'result'], 'Invalid mob spawn response: malformed result entry');
|
|
176
|
+
const status = raw.status;
|
|
177
|
+
if (status !== 'spawned' && status !== 'failed') {
|
|
178
|
+
throw new Error('Invalid mob spawn response: invalid result status');
|
|
179
|
+
}
|
|
180
|
+
if (!isRecord(raw.result)) {
|
|
181
|
+
throw new Error('Invalid mob spawn response: missing result payload');
|
|
182
|
+
}
|
|
183
|
+
if (status === 'failed') {
|
|
184
|
+
requireOnlyKeys(raw.result, ['message'], 'Invalid mob spawn response: malformed failed result payload');
|
|
185
|
+
const message = raw.result.message;
|
|
186
|
+
if (typeof message !== 'string' || message.length === 0) {
|
|
187
|
+
throw new Error('Invalid mob spawn response: failed result missing message');
|
|
188
|
+
}
|
|
189
|
+
throw new Error(`Mob spawn failed: ${message}`);
|
|
190
|
+
}
|
|
191
|
+
requireOnlyKeys(raw.result, ['agent_identity', 'member_ref'], 'Invalid mob spawn response: malformed spawned result payload');
|
|
192
|
+
const agentIdentity = raw.result.agent_identity;
|
|
193
|
+
const memberRef = raw.result.member_ref;
|
|
194
|
+
if (typeof agentIdentity !== 'string' || agentIdentity.length === 0) {
|
|
195
|
+
throw new Error('Invalid mob spawn response: spawned result missing agent_identity');
|
|
196
|
+
}
|
|
197
|
+
if (typeof memberRef !== 'string' || memberRef.length === 0) {
|
|
198
|
+
throw new Error('Invalid mob spawn response: spawned result missing member_ref');
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
mob_id: mobId,
|
|
202
|
+
agent_identity: agentIdentity,
|
|
203
|
+
member_ref: memberRef,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function parseSubscriptionLaggedEvent(record, context) {
|
|
207
|
+
return {
|
|
208
|
+
type: 'lagged',
|
|
209
|
+
skipped: requireNumberField(record, 'skipped', `${context}: lagged skipped must be number`),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function normalizeEventSourceIdentity(raw, context) {
|
|
213
|
+
const source = requireRecord(raw, `${context}: missing source`);
|
|
214
|
+
const sourceType = requireStringField(source, 'type', `${context}: source missing type`);
|
|
215
|
+
switch (sourceType) {
|
|
216
|
+
case 'session': {
|
|
217
|
+
requireOnlyKeys(source, ['type', 'session_id', 'sessionId'], `${context}: malformed source`);
|
|
218
|
+
const sessionId = typeof source.session_id === 'string' ? source.session_id : source.sessionId;
|
|
219
|
+
if (typeof sessionId !== 'string' || sessionId.length === 0) {
|
|
220
|
+
throw new Error(`${context}: source missing session_id`);
|
|
221
|
+
}
|
|
222
|
+
return { type: 'session', session_id: sessionId };
|
|
223
|
+
}
|
|
224
|
+
case 'runtime': {
|
|
225
|
+
requireOnlyKeys(source, ['type', 'runtime_id', 'runtimeId'], `${context}: malformed source`);
|
|
226
|
+
const runtimeId = typeof source.runtime_id === 'string' ? source.runtime_id : source.runtimeId;
|
|
227
|
+
if (typeof runtimeId !== 'string' || runtimeId.length === 0) {
|
|
228
|
+
throw new Error(`${context}: source missing runtime_id`);
|
|
229
|
+
}
|
|
230
|
+
return { type: 'runtime', runtime_id: runtimeId };
|
|
231
|
+
}
|
|
232
|
+
case 'interaction': {
|
|
233
|
+
requireOnlyKeys(source, ['type', 'interaction_id', 'interactionId'], `${context}: malformed source`);
|
|
234
|
+
const interactionId = typeof source.interaction_id === 'string' ? source.interaction_id : source.interactionId;
|
|
235
|
+
if (typeof interactionId !== 'string' || interactionId.length === 0) {
|
|
236
|
+
throw new Error(`${context}: source missing interaction_id`);
|
|
237
|
+
}
|
|
238
|
+
return { type: 'interaction', interaction_id: interactionId };
|
|
239
|
+
}
|
|
240
|
+
case 'callback':
|
|
241
|
+
requireOnlyKeys(source, ['type'], `${context}: malformed source`);
|
|
242
|
+
return { type: 'callback' };
|
|
243
|
+
case 'external': {
|
|
244
|
+
requireOnlyKeys(source, ['type', 'source_id', 'sourceId'], `${context}: malformed source`);
|
|
245
|
+
const sourceId = typeof source.source_id === 'string' ? source.source_id : source.sourceId;
|
|
246
|
+
if (typeof sourceId !== 'string' || sourceId.length === 0) {
|
|
247
|
+
throw new Error(`${context}: source missing source_id`);
|
|
248
|
+
}
|
|
249
|
+
return { type: 'external', source_id: sourceId };
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
throw new Error(`${context}: unsupported source type`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function parseEventPayload(raw, context) {
|
|
256
|
+
const payload = requireRecord(raw, `${context}: missing payload`);
|
|
257
|
+
requireStringField(payload, 'type', `${context}: payload missing type`);
|
|
258
|
+
return payload;
|
|
259
|
+
}
|
|
260
|
+
function parseEventEnvelope(raw, context) {
|
|
261
|
+
const record = requireRecord(raw, `${context}: malformed envelope`);
|
|
262
|
+
const payload = parseEventPayload(record.payload, context);
|
|
263
|
+
const envelope = {
|
|
264
|
+
event_id: requireStringField(record, 'event_id', `${context}: missing event_id`),
|
|
265
|
+
source: normalizeEventSourceIdentity(record.source, context),
|
|
266
|
+
source_id: requireStringField(record, 'source_id', `${context}: missing source_id`),
|
|
267
|
+
seq: requireNumberField(record, 'seq', `${context}: missing seq`),
|
|
268
|
+
timestamp_ms: requireNumberField(record, 'timestamp_ms', `${context}: missing timestamp_ms`),
|
|
269
|
+
payload,
|
|
270
|
+
};
|
|
271
|
+
const mobId = optionalNonEmptyStringField(record, 'mob_id', `${context}: mob_id must be string`);
|
|
272
|
+
if (mobId !== undefined)
|
|
273
|
+
envelope.mob_id = mobId;
|
|
274
|
+
const agentIdentity = optionalNonEmptyStringField(record, 'agent_identity', `${context}: agent_identity must be string`);
|
|
275
|
+
if (agentIdentity !== undefined)
|
|
276
|
+
envelope.agent_identity = agentIdentity;
|
|
277
|
+
const memberRef = optionalNonEmptyStringField(record, 'member_ref', `${context}: member_ref must be string`);
|
|
278
|
+
if (memberRef !== undefined)
|
|
279
|
+
envelope.member_ref = memberRef;
|
|
280
|
+
const cursor = record.cursor;
|
|
281
|
+
if (cursor != null) {
|
|
282
|
+
if (typeof cursor !== 'string' && typeof cursor !== 'number') {
|
|
283
|
+
throw new Error(`${context}: cursor must be string or number`);
|
|
284
|
+
}
|
|
285
|
+
envelope.cursor = cursor;
|
|
286
|
+
}
|
|
287
|
+
return envelope;
|
|
288
|
+
}
|
|
289
|
+
function parseMemberEventItem(raw, context) {
|
|
290
|
+
const record = requireRecord(raw, `${context}: malformed event item`);
|
|
291
|
+
if (record.type === 'lagged') {
|
|
292
|
+
return parseSubscriptionLaggedEvent(record, context);
|
|
293
|
+
}
|
|
294
|
+
return parseEventEnvelope(record, context);
|
|
295
|
+
}
|
|
296
|
+
function parseAttributedSource(raw, context) {
|
|
297
|
+
const source = requireRecord(raw, `${context}: missing source`);
|
|
298
|
+
const identity = requireStringField(source, 'identity', `${context}: source missing identity`);
|
|
299
|
+
const generation = requireNonNegativeIntegerField(source, 'generation', `${context}: source generation must be a non-negative integer`);
|
|
300
|
+
return { identity, generation };
|
|
301
|
+
}
|
|
302
|
+
function parseAttributedEventItem(raw, context) {
|
|
303
|
+
const record = requireRecord(raw, `${context}: malformed attributed event`);
|
|
304
|
+
if (record.type === 'lagged') {
|
|
305
|
+
return parseSubscriptionLaggedEvent(record, context);
|
|
306
|
+
}
|
|
307
|
+
const source = parseAttributedSource(record.source, context);
|
|
308
|
+
const role = requireStringField(record, 'role', `${context}: missing role`);
|
|
309
|
+
const attributed = {
|
|
310
|
+
source,
|
|
311
|
+
role,
|
|
312
|
+
envelope: parseEventEnvelope(record.envelope, `${context}: envelope`),
|
|
313
|
+
};
|
|
314
|
+
const sourceFenceToken = optionalNumberField(record, 'source_fence_token', `${context}: source_fence_token must be number`);
|
|
315
|
+
return sourceFenceToken === undefined
|
|
316
|
+
? attributed
|
|
317
|
+
: { ...attributed, source_fence_token: sourceFenceToken };
|
|
318
|
+
}
|
|
319
|
+
function parseEventItems(raw, context, parseItem) {
|
|
320
|
+
if (!Array.isArray(raw)) {
|
|
321
|
+
throw new Error(`${context}: events must be a list`);
|
|
322
|
+
}
|
|
323
|
+
return raw.map((item, index) => parseItem(item, `${context}[${index}]`));
|
|
324
|
+
}
|
|
325
|
+
function parseMobEvent(raw, context) {
|
|
326
|
+
const record = requireRecord(raw, `${context}: malformed mob event`);
|
|
327
|
+
return {
|
|
328
|
+
cursor: requireNumberField(record, 'cursor', `${context}: cursor must be number`),
|
|
329
|
+
timestamp: requireStringField(record, 'timestamp', `${context}: missing timestamp`),
|
|
330
|
+
mob_id: requireStringField(record, 'mob_id', `${context}: missing mob_id`),
|
|
331
|
+
kind: requireRecord(record.kind, `${context}: missing kind`),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function parseMobEvents(raw) {
|
|
335
|
+
return parseEventItems(raw, 'Invalid mob/events response', parseMobEvent);
|
|
336
|
+
}
|
|
337
|
+
function parseMobMemberSnapshot(raw, mobId, agentIdentity) {
|
|
338
|
+
const snapshot = requireRecord(raw, 'Invalid mob member_status response: malformed envelope');
|
|
339
|
+
const currentSessionId = optionalStringField(snapshot, 'current_session_id', 'Invalid mob member_status response: current_session_id must be string');
|
|
340
|
+
const realtimeAttachmentStatus = optionalStringField(snapshot, 'realtime_attachment_status', 'Invalid mob member_status response: realtime_attachment_status must be string');
|
|
341
|
+
const result = {
|
|
342
|
+
status: parseWireMobMemberStatus(snapshot.status, 'Invalid mob member_status response: missing status'),
|
|
343
|
+
member_ref: encodeMemberRef(mobId, agentIdentity),
|
|
344
|
+
output_preview: optionalStringField(snapshot, 'output_preview', 'Invalid mob member_status response: output_preview must be string'),
|
|
345
|
+
error: optionalStringField(snapshot, 'error', 'Invalid mob member_status response: error must be string'),
|
|
346
|
+
tokens_used: requireNonNegativeIntegerField(snapshot, 'tokens_used', 'Invalid mob member_status response: tokens_used must be number'),
|
|
347
|
+
is_final: requireBooleanField(snapshot, 'is_final', 'Invalid mob member_status response: is_final must be boolean'),
|
|
348
|
+
kickoff: optionalRecordField(snapshot, 'kickoff', 'Invalid mob member_status response: kickoff must be object'),
|
|
349
|
+
peer_connectivity: optionalRecordField(snapshot, 'peer_connectivity', 'Invalid mob member_status response: peer_connectivity must be object'),
|
|
350
|
+
};
|
|
351
|
+
if (currentSessionId !== undefined) {
|
|
352
|
+
result.current_session_id = currentSessionId;
|
|
353
|
+
}
|
|
354
|
+
if (realtimeAttachmentStatus !== undefined) {
|
|
355
|
+
result.realtime_attachment_status = realtimeAttachmentStatus;
|
|
356
|
+
}
|
|
357
|
+
if (Object.prototype.hasOwnProperty.call(snapshot, 'external_member')) {
|
|
358
|
+
result.external_member = snapshot.external_member;
|
|
359
|
+
}
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
function parseMobRespawnResult(raw) {
|
|
363
|
+
const result = requireRecord(raw, 'Invalid mob respawn response: malformed envelope');
|
|
364
|
+
const status = requireStringField(result, 'status', 'Invalid mob respawn response: missing status');
|
|
365
|
+
if (status !== 'completed' && status !== 'topology_restore_failed') {
|
|
366
|
+
throw new Error('Invalid mob respawn response: invalid status');
|
|
367
|
+
}
|
|
368
|
+
const receipt = requireRecord(result.receipt, 'Invalid mob respawn response: missing receipt');
|
|
369
|
+
const memberRef = requireStringField(receipt, 'member_ref', 'Invalid mob respawn response: receipt missing member_ref');
|
|
370
|
+
const identity = requireStringField(receipt, 'identity', 'Invalid mob respawn response: receipt missing identity');
|
|
371
|
+
return {
|
|
372
|
+
status,
|
|
373
|
+
receipt: {
|
|
374
|
+
agent_identity: identity,
|
|
375
|
+
member_ref: memberRef,
|
|
376
|
+
},
|
|
377
|
+
failed_peer_ids: optionalStringArrayField(result, 'failed_peer_ids', 'Invalid mob respawn response: failed_peer_ids must be string list'),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function parseMemberDeliveryReceipt(raw, expectedMobId, expectedAgentIdentity) {
|
|
381
|
+
const receipt = requireRecord(raw, 'Invalid mob member delivery response: malformed envelope');
|
|
382
|
+
const mobId = requireStringField(receipt, 'mob_id', 'Invalid mob member delivery response: missing mob_id');
|
|
383
|
+
if (mobId !== expectedMobId) {
|
|
384
|
+
throw new Error('Invalid mob member delivery response: mob_id mismatch');
|
|
385
|
+
}
|
|
386
|
+
const agentIdentity = requireStringField(receipt, 'agent_identity', 'Invalid mob member delivery response: missing agent_identity');
|
|
387
|
+
if (agentIdentity !== expectedAgentIdentity) {
|
|
388
|
+
throw new Error('Invalid mob member delivery response: agent_identity mismatch');
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
agent_identity: agentIdentity,
|
|
392
|
+
member_ref: requireStringField(receipt, 'member_ref', 'Invalid mob member delivery response: missing member_ref'),
|
|
393
|
+
handling_mode: parseWireHandlingMode(receipt.handling_mode, 'Invalid mob member delivery response: missing handling_mode'),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function parseMobHelperResult(raw, context) {
|
|
397
|
+
const result = requireRecord(raw, `${context}: malformed envelope`);
|
|
398
|
+
return {
|
|
399
|
+
output: optionalStringField(result, 'output', `${context}: output must be string`),
|
|
400
|
+
tokens_used: requireNonNegativeIntegerField(result, 'tokens_used', `${context}: tokens_used must be number`),
|
|
401
|
+
agent_identity: requireStringField(result, 'agent_identity', `${context}: missing agent_identity`),
|
|
402
|
+
member_ref: requireStringField(result, 'member_ref', `${context}: missing member_ref`),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function parseMobFlowStatusResult(raw) {
|
|
406
|
+
const context = 'Invalid mob flow_status response';
|
|
407
|
+
const record = requireRecord(raw, `${context}: malformed envelope`);
|
|
408
|
+
if (!Object.prototype.hasOwnProperty.call(record, 'run')) {
|
|
409
|
+
throw new Error(`${context}: missing run`);
|
|
410
|
+
}
|
|
411
|
+
if (record.run == null) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
const run = requireRecord(record.run, `${context}: run must be object`);
|
|
415
|
+
return {
|
|
416
|
+
...run,
|
|
417
|
+
run_id: requireStringField(run, 'run_id', `${context}: run missing run_id`),
|
|
418
|
+
status: requireStringField(run, 'status', `${context}: run missing status`),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
2
421
|
/** Capability-bearing handle for one mob member. */
|
|
3
422
|
export class Member {
|
|
4
423
|
mobId;
|
|
5
|
-
|
|
424
|
+
agentIdentity;
|
|
6
425
|
bindings;
|
|
7
|
-
constructor(mobId,
|
|
426
|
+
constructor(mobId, agentIdentity, bindings) {
|
|
8
427
|
this.mobId = mobId;
|
|
9
|
-
this.
|
|
428
|
+
this.agentIdentity = agentIdentity;
|
|
10
429
|
this.bindings = bindings;
|
|
11
430
|
}
|
|
12
431
|
async send(content, handlingMode = 'queue', renderMetadata) {
|
|
13
|
-
const json = await this.bindings.mob_member_send(this.mobId, this.
|
|
432
|
+
const json = await this.bindings.mob_member_send(this.mobId, this.agentIdentity, JSON.stringify({
|
|
14
433
|
content,
|
|
15
434
|
handling_mode: handlingMode,
|
|
16
435
|
render_metadata: renderMetadata,
|
|
17
436
|
}));
|
|
18
|
-
|
|
19
|
-
if (typeof receipt.session_id !== 'string' || receipt.session_id.length === 0) {
|
|
20
|
-
throw new Error('Invalid mob member delivery response: missing session_id');
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
member_id: typeof receipt.member_id === 'string' && receipt.member_id.length > 0
|
|
24
|
-
? receipt.member_id
|
|
25
|
-
: this.meerkatId,
|
|
26
|
-
session_id: receipt.session_id,
|
|
27
|
-
handling_mode: receipt.handling_mode ?? handlingMode,
|
|
28
|
-
};
|
|
437
|
+
return parseMemberDeliveryReceipt(parseJsonPayload(json, 'Invalid mob member delivery response'), this.mobId, this.agentIdentity);
|
|
29
438
|
}
|
|
30
439
|
async subscribe() {
|
|
31
|
-
const handle = await this.bindings.mob_member_subscribe(this.mobId, this.
|
|
32
|
-
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) =>
|
|
440
|
+
const handle = await this.bindings.mob_member_subscribe(this.mobId, this.agentIdentity);
|
|
441
|
+
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) => parseEventItems(raw, 'Invalid mob member subscription event', parseMemberEventItem), () => this.bindings.close_subscription(handle));
|
|
33
442
|
}
|
|
34
443
|
}
|
|
35
444
|
/** A mob instance — a group of agents with shared orchestration. */
|
|
@@ -44,12 +453,16 @@ export class Mob {
|
|
|
44
453
|
}
|
|
45
454
|
/** Spawn one or more agents into the mob. */
|
|
46
455
|
async spawn(specs) {
|
|
47
|
-
const json = await this.bindings.mob_spawn(this.mobId, JSON.stringify(specs));
|
|
48
|
-
|
|
456
|
+
const json = await this.bindings.mob_spawn(this.mobId, JSON.stringify(specs.map(spawnSpecPayload)));
|
|
457
|
+
const parsed = parseJsonPayload(json, 'Invalid mob spawn response');
|
|
458
|
+
if (!Array.isArray(parsed)) {
|
|
459
|
+
throw new Error('Invalid mob spawn response: results must be a list');
|
|
460
|
+
}
|
|
461
|
+
return parsed.map((entry) => normalizeSpawnManyEntry(entry, this.mobId));
|
|
49
462
|
}
|
|
50
463
|
/** Retire an agent from the mob. */
|
|
51
|
-
async retire(
|
|
52
|
-
await this.bindings.mob_retire(this.mobId,
|
|
464
|
+
async retire(agentIdentity) {
|
|
465
|
+
await this.bindings.mob_retire(this.mobId, agentIdentity);
|
|
53
466
|
}
|
|
54
467
|
/** Wire two agents for comms trust. */
|
|
55
468
|
async wire(member, peer) {
|
|
@@ -78,69 +491,119 @@ export class Mob {
|
|
|
78
491
|
/** List all members in the mob. */
|
|
79
492
|
async listMembers() {
|
|
80
493
|
const json = await this.bindings.mob_list_members(this.mobId);
|
|
81
|
-
|
|
494
|
+
const parsed = parseJsonPayload(json, 'Invalid mob list_members response');
|
|
495
|
+
if (!Array.isArray(parsed)) {
|
|
496
|
+
throw new Error('Invalid mob list_members response: members must be a list');
|
|
497
|
+
}
|
|
498
|
+
return parsed.map((rawMember, index) => {
|
|
499
|
+
const member = requireRecord(rawMember, `Invalid mob list_members entry ${index}: malformed member`);
|
|
500
|
+
if (typeof member.agent_identity !== 'string' || member.agent_identity.length === 0) {
|
|
501
|
+
throw new Error('Invalid mob list_members entry: missing agent_identity');
|
|
502
|
+
}
|
|
503
|
+
const agentIdentity = member.agent_identity;
|
|
504
|
+
const memberRef = typeof member.member_ref === 'string' && member.member_ref.length > 0
|
|
505
|
+
? member.member_ref
|
|
506
|
+
: '';
|
|
507
|
+
if (!memberRef) {
|
|
508
|
+
throw new Error('Invalid mob list_members entry: missing member_ref');
|
|
509
|
+
}
|
|
510
|
+
const profile = typeof member.role === 'string' && member.role.length > 0
|
|
511
|
+
? member.role
|
|
512
|
+
: typeof member.profile === 'string' && member.profile.length > 0
|
|
513
|
+
? member.profile
|
|
514
|
+
: typeof member.profile_name === 'string' && member.profile_name.length > 0
|
|
515
|
+
? member.profile_name
|
|
516
|
+
: undefined;
|
|
517
|
+
if (!profile) {
|
|
518
|
+
throw new Error('Invalid mob list_members entry: missing profile');
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
agent_identity: agentIdentity,
|
|
522
|
+
member_ref: memberRef,
|
|
523
|
+
profile,
|
|
524
|
+
peer_id: member.peer_id != null ? String(member.peer_id) : undefined,
|
|
525
|
+
external_peer_specs: member.external_peer_specs && typeof member.external_peer_specs === 'object'
|
|
526
|
+
? Object.fromEntries(Object.entries(member.external_peer_specs).map(([key, value]) => [key, (value ?? {})]))
|
|
527
|
+
: undefined,
|
|
528
|
+
runtime_mode: member.runtime_mode != null ? String(member.runtime_mode) : undefined,
|
|
529
|
+
state: member.state != null ? String(member.state) : undefined,
|
|
530
|
+
wired_to: Array.isArray(member.wired_to)
|
|
531
|
+
? member.wired_to.map((peer) => String(peer))
|
|
532
|
+
: undefined,
|
|
533
|
+
labels: member.labels && typeof member.labels === 'object'
|
|
534
|
+
? Object.fromEntries(Object.entries(member.labels).map(([key, value]) => [key, String(value)]))
|
|
535
|
+
: undefined,
|
|
536
|
+
status: member.status != null ? String(member.status) : undefined,
|
|
537
|
+
error: member.error != null ? String(member.error) : undefined,
|
|
538
|
+
is_final: member.is_final != null ? Boolean(member.is_final) : undefined,
|
|
539
|
+
kickoff: member.kickoff && typeof member.kickoff === 'object'
|
|
540
|
+
? member.kickoff
|
|
541
|
+
: undefined,
|
|
542
|
+
};
|
|
543
|
+
});
|
|
82
544
|
}
|
|
83
545
|
/** Stage runtime system context for a specific member session. */
|
|
84
|
-
async appendSystemContext(
|
|
85
|
-
const json = await this.bindings.mob_append_system_context(this.mobId,
|
|
546
|
+
async appendSystemContext(agentIdentity, options) {
|
|
547
|
+
const json = await this.bindings.mob_append_system_context(this.mobId, agentIdentity, JSON.stringify({
|
|
86
548
|
text: options.text,
|
|
87
549
|
source: options.source,
|
|
88
550
|
idempotency_key: options.idempotencyKey,
|
|
89
551
|
}));
|
|
90
|
-
return
|
|
552
|
+
return parseMobAppendSystemContextResult(parseJsonPayload(json, 'Invalid mob append_system_context response'));
|
|
91
553
|
}
|
|
92
554
|
/** Get a capability-bearing handle for one member. */
|
|
93
|
-
member(
|
|
94
|
-
return new Member(this.mobId,
|
|
555
|
+
member(agentIdentity) {
|
|
556
|
+
return new Member(this.mobId, agentIdentity, this.bindings);
|
|
95
557
|
}
|
|
96
|
-
/**
|
|
97
558
|
/** Retire and re-spawn an agent with the same profile. Returns a result envelope with receipt. */
|
|
98
|
-
async respawn(
|
|
559
|
+
async respawn(agentIdentity, initialMessage) {
|
|
99
560
|
const payload = initialMessage != null
|
|
100
561
|
? typeof initialMessage === 'string'
|
|
101
562
|
? initialMessage
|
|
102
563
|
: JSON.stringify(initialMessage)
|
|
103
564
|
: undefined;
|
|
104
|
-
const json = await this.bindings.mob_respawn(this.mobId,
|
|
105
|
-
return
|
|
565
|
+
const json = await this.bindings.mob_respawn(this.mobId, agentIdentity, payload);
|
|
566
|
+
return parseMobRespawnResult(parseJsonPayload(json, 'Invalid mob respawn response'));
|
|
106
567
|
}
|
|
107
568
|
/** Force-cancel an active member turn. */
|
|
108
|
-
async forceCancel(
|
|
109
|
-
await this.bindings.mob_force_cancel(this.mobId,
|
|
569
|
+
async forceCancel(agentIdentity) {
|
|
570
|
+
await this.bindings.mob_force_cancel(this.mobId, agentIdentity);
|
|
110
571
|
}
|
|
111
572
|
/** Read the current execution snapshot for a member. */
|
|
112
|
-
async memberStatus(
|
|
113
|
-
const json = await this.bindings.mob_member_status(this.mobId,
|
|
114
|
-
return
|
|
573
|
+
async memberStatus(agentIdentity) {
|
|
574
|
+
const json = await this.bindings.mob_member_status(this.mobId, agentIdentity);
|
|
575
|
+
return parseMobMemberSnapshot(parseJsonPayload(json, 'Invalid mob member_status response'), this.mobId, agentIdentity);
|
|
115
576
|
}
|
|
116
577
|
/** Spawn a short-lived helper and return its terminal result. */
|
|
117
578
|
async spawnHelper(prompt, options) {
|
|
118
579
|
const json = await this.bindings.mob_spawn_helper(this.mobId, JSON.stringify({
|
|
119
580
|
prompt,
|
|
120
|
-
|
|
581
|
+
agent_identity: options?.agentIdentity,
|
|
121
582
|
profile_name: options?.profileName,
|
|
583
|
+
auth_binding: options?.authBinding,
|
|
122
584
|
runtime_mode: options?.runtimeMode,
|
|
123
585
|
backend: options?.backend,
|
|
124
586
|
}));
|
|
125
|
-
return
|
|
587
|
+
return parseMobHelperResult(parseJsonPayload(json, 'Invalid mob spawn_helper response'), 'Invalid mob spawn_helper response');
|
|
126
588
|
}
|
|
127
589
|
/** Fork a helper from an existing member and return its terminal result. */
|
|
128
590
|
async forkHelper(sourceMemberId, prompt, options) {
|
|
129
591
|
const json = await this.bindings.mob_fork_helper(this.mobId, JSON.stringify({
|
|
130
592
|
source_member_id: sourceMemberId,
|
|
131
593
|
prompt,
|
|
132
|
-
|
|
594
|
+
agent_identity: options?.agentIdentity,
|
|
133
595
|
profile_name: options?.profileName,
|
|
596
|
+
auth_binding: options?.authBinding,
|
|
134
597
|
fork_context: options?.forkContext,
|
|
135
598
|
runtime_mode: options?.runtimeMode,
|
|
136
599
|
backend: options?.backend,
|
|
137
600
|
}));
|
|
138
|
-
return
|
|
601
|
+
return parseMobHelperResult(parseJsonPayload(json, 'Invalid mob fork_helper response'), 'Invalid mob fork_helper response');
|
|
139
602
|
}
|
|
140
603
|
/** Get mob status. */
|
|
141
604
|
async status() {
|
|
142
605
|
const json = await this.bindings.mob_status(this.mobId);
|
|
143
|
-
return
|
|
606
|
+
return parseMobStatusResult(parseJsonPayload(json, 'Invalid mob/status response'));
|
|
144
607
|
}
|
|
145
608
|
/** Perform a lifecycle action (stop, resume, complete, destroy). */
|
|
146
609
|
async lifecycle(action) {
|
|
@@ -150,7 +613,7 @@ export class Mob {
|
|
|
150
613
|
async events(afterCursor = '', limit = 100) {
|
|
151
614
|
const numericCursor = afterCursor === '' ? 0 : Number(afterCursor);
|
|
152
615
|
const json = await this.bindings.mob_events(this.mobId, Number.isFinite(numericCursor) ? numericCursor : 0, limit);
|
|
153
|
-
return
|
|
616
|
+
return parseMobEvents(parseJsonPayload(json, 'Invalid mob/events response'));
|
|
154
617
|
}
|
|
155
618
|
/** Run a flow. Returns the run ID. */
|
|
156
619
|
async runFlow(flowId, params = {}) {
|
|
@@ -159,21 +622,21 @@ export class Mob {
|
|
|
159
622
|
/** Get flow status. */
|
|
160
623
|
async flowStatus(runId) {
|
|
161
624
|
const json = await this.bindings.mob_flow_status(this.mobId, runId);
|
|
162
|
-
return
|
|
625
|
+
return parseMobFlowStatusResult(parseJsonPayload(json, 'Invalid mob flow_status response'));
|
|
163
626
|
}
|
|
164
627
|
/** Cancel a running flow. */
|
|
165
628
|
async cancelFlow(runId) {
|
|
166
629
|
await this.bindings.mob_cancel_flow(this.mobId, runId);
|
|
167
630
|
}
|
|
168
631
|
/** Subscribe to events for a specific member. */
|
|
169
|
-
async subscribeMemberEvents(
|
|
170
|
-
const handle = await this.bindings.mob_member_subscribe(this.mobId,
|
|
171
|
-
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) =>
|
|
632
|
+
async subscribeMemberEvents(agentIdentity) {
|
|
633
|
+
const handle = await this.bindings.mob_member_subscribe(this.mobId, agentIdentity);
|
|
634
|
+
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) => parseEventItems(raw, 'Invalid mob member subscription event', parseMemberEventItem), () => this.bindings.close_subscription(handle));
|
|
172
635
|
}
|
|
173
636
|
/** Subscribe to all mob-wide attributed events. */
|
|
174
637
|
async subscribeEvents() {
|
|
175
638
|
const handle = await this.bindings.mob_subscribe_events(this.mobId);
|
|
176
|
-
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) =>
|
|
639
|
+
return new EventSubscription(() => this.bindings.poll_subscription(handle), (raw) => parseEventItems(raw, 'Invalid mob attributed subscription event', parseAttributedEventItem), () => this.bindings.close_subscription(handle));
|
|
177
640
|
}
|
|
178
641
|
}
|
|
179
642
|
//# sourceMappingURL=mob.js.map
|