@rkat/sdk 0.4.5 → 0.4.7
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 +85 -20
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +388 -58
- package/dist/client.js.map +1 -1
- package/dist/generated/types.d.ts +36 -1
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/generated/types.js +2 -2
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/mob.d.ts +29 -0
- package/dist/mob.d.ts.map +1 -0
- package/dist/mob.js +58 -0
- package/dist/mob.js.map +1 -0
- package/dist/session.d.ts +51 -41
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -43
- package/dist/session.js.map +1 -1
- package/dist/streaming.d.ts +8 -18
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +95 -54
- package/dist/streaming.js.map +1 -1
- package/dist/subscription.d.ts +23 -0
- package/dist/subscription.d.ts.map +1 -0
- package/dist/subscription.js +42 -0
- package/dist/subscription.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
package/dist/client.js
CHANGED
|
@@ -31,8 +31,11 @@ import path from "node:path";
|
|
|
31
31
|
import { createInterface } from "node:readline";
|
|
32
32
|
import { MeerkatError, CapabilityUnavailableError } from "./generated/errors.js";
|
|
33
33
|
import { CONTRACT_VERSION, } from "./generated/types.js";
|
|
34
|
-
import { Session } from "./session.js";
|
|
35
|
-
import {
|
|
34
|
+
import { DeferredSession, Session } from "./session.js";
|
|
35
|
+
import { Mob } from "./mob.js";
|
|
36
|
+
import { parseCoreEvent } from "./events.js";
|
|
37
|
+
import { EventStream, AsyncQueue } from "./streaming.js";
|
|
38
|
+
import { EventSubscription } from "./subscription.js";
|
|
36
39
|
const MEERKAT_REPO = "lukacf/meerkat";
|
|
37
40
|
const MEERKAT_RELEASE_BINARY = "rkat-rpc";
|
|
38
41
|
const MEERKAT_BINARY_CACHE_ROOT = path.join(os.homedir(), ".cache", "meerkat", "bin", MEERKAT_RELEASE_BINARY);
|
|
@@ -63,17 +66,43 @@ export class MeerkatClient {
|
|
|
63
66
|
process = null;
|
|
64
67
|
requestId = 0;
|
|
65
68
|
_capabilities = [];
|
|
69
|
+
_methods = new Set();
|
|
66
70
|
rkatPath;
|
|
67
71
|
pendingRequests = new Map();
|
|
68
72
|
eventQueues = new Map();
|
|
69
|
-
|
|
73
|
+
streamQueues = new Map();
|
|
70
74
|
pendingStreamQueue = null;
|
|
71
75
|
pendingStreamRequestId = null;
|
|
72
76
|
unmatchedStreamBuffer = new Map();
|
|
77
|
+
unmatchedStandaloneStreamBuffer = new Map();
|
|
78
|
+
unmatchedStandaloneStreamEnd = new Map();
|
|
79
|
+
streamTerminalOutcomes = new Map();
|
|
73
80
|
rl = null;
|
|
81
|
+
toolHandlers = new Map();
|
|
74
82
|
constructor(rkatPath = "rkat-rpc") {
|
|
75
83
|
this.rkatPath = rkatPath;
|
|
76
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Register a callback tool handler.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* client.registerTool("search", "Search the web", { type: "object" }, async (args) => {
|
|
91
|
+
* return `Results for ${args.q}`;
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
registerTool(name, description, inputSchema, handler) {
|
|
96
|
+
this.toolHandlers.set(name, { description, inputSchema, handler });
|
|
97
|
+
// If already connected, register the new tool with the server immediately.
|
|
98
|
+
if (this.process?.stdin) {
|
|
99
|
+
this.request("tools/register", {
|
|
100
|
+
tools: [{ name, description, input_schema: inputSchema }],
|
|
101
|
+
}).catch(() => {
|
|
102
|
+
// Best-effort: tool registration may fail if connection is closing.
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
77
106
|
// -- Connection ---------------------------------------------------------
|
|
78
107
|
async connect(options) {
|
|
79
108
|
if (options?.realmId && options?.isolated) {
|
|
@@ -96,6 +125,9 @@ export class MeerkatClient {
|
|
|
96
125
|
if (!MeerkatClient.checkVersionCompatible(serverVersion, CONTRACT_VERSION)) {
|
|
97
126
|
throw new MeerkatError("VERSION_MISMATCH", `Server version ${serverVersion} incompatible with SDK ${CONTRACT_VERSION}`);
|
|
98
127
|
}
|
|
128
|
+
this._methods = new Set(Array.isArray(initResult.methods)
|
|
129
|
+
? initResult.methods.map((method) => String(method))
|
|
130
|
+
: []);
|
|
99
131
|
// Fetch capabilities
|
|
100
132
|
const capsResult = await this.request("capabilities/get", {});
|
|
101
133
|
const rawCaps = capsResult.capabilities ?? [];
|
|
@@ -104,6 +136,15 @@ export class MeerkatClient {
|
|
|
104
136
|
description: String(cap.description ?? ""),
|
|
105
137
|
status: MeerkatClient.normalizeStatus(cap.status),
|
|
106
138
|
}));
|
|
139
|
+
// Register callback tools if any were declared.
|
|
140
|
+
if (this.toolHandlers.size > 0) {
|
|
141
|
+
const tools = Array.from(this.toolHandlers.entries()).map(([name, { description, inputSchema }]) => ({
|
|
142
|
+
name,
|
|
143
|
+
description,
|
|
144
|
+
input_schema: inputSchema,
|
|
145
|
+
}));
|
|
146
|
+
await this.request("tools/register", { tools });
|
|
147
|
+
}
|
|
107
148
|
return this;
|
|
108
149
|
}
|
|
109
150
|
async close() {
|
|
@@ -123,10 +164,11 @@ export class MeerkatClient {
|
|
|
123
164
|
queue.put(null);
|
|
124
165
|
}
|
|
125
166
|
this.eventQueues.clear();
|
|
126
|
-
for (const [, queue] of this.
|
|
167
|
+
for (const [, queue] of this.streamQueues) {
|
|
127
168
|
queue.put(null);
|
|
128
169
|
}
|
|
129
|
-
this.
|
|
170
|
+
this.streamQueues.clear();
|
|
171
|
+
this.streamTerminalOutcomes.clear();
|
|
130
172
|
if (this.pendingStreamQueue) {
|
|
131
173
|
this.pendingStreamQueue.put(null);
|
|
132
174
|
this.pendingStreamQueue = null;
|
|
@@ -196,6 +238,18 @@ export class MeerkatClient {
|
|
|
196
238
|
parseResult: MeerkatClient.parseRunResult,
|
|
197
239
|
});
|
|
198
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Create a new session without running the first turn.
|
|
243
|
+
*
|
|
244
|
+
* Returns a {@link DeferredSession} whose `startTurn()` executes the first
|
|
245
|
+
* turn with optional per-turn overrides.
|
|
246
|
+
*/
|
|
247
|
+
async createDeferredSession(prompt, options) {
|
|
248
|
+
const params = MeerkatClient.buildCreateParams(prompt, options);
|
|
249
|
+
params.initial_turn = "deferred";
|
|
250
|
+
const raw = await this.request("session/create", params);
|
|
251
|
+
return new DeferredSession(this, String(raw.session_id ?? ""), raw.session_ref != null ? String(raw.session_ref) : undefined);
|
|
252
|
+
}
|
|
199
253
|
// -- Session queries ----------------------------------------------------
|
|
200
254
|
async listSessions() {
|
|
201
255
|
const result = await this.request("session/list", {});
|
|
@@ -213,11 +267,27 @@ export class MeerkatClient {
|
|
|
213
267
|
async readSession(sessionId) {
|
|
214
268
|
return this.request("session/read", { session_id: sessionId });
|
|
215
269
|
}
|
|
270
|
+
async readSessionHistory(sessionId, options) {
|
|
271
|
+
const params = {
|
|
272
|
+
session_id: sessionId,
|
|
273
|
+
offset: options?.offset ?? 0,
|
|
274
|
+
};
|
|
275
|
+
if (options?.limit !== undefined) {
|
|
276
|
+
params.limit = options.limit;
|
|
277
|
+
}
|
|
278
|
+
const raw = await this.request("session/history", params);
|
|
279
|
+
return MeerkatClient.parseSessionHistory(raw);
|
|
280
|
+
}
|
|
216
281
|
// -- Capabilities -------------------------------------------------------
|
|
217
282
|
get capabilities() {
|
|
218
283
|
return this._capabilities;
|
|
219
284
|
}
|
|
220
285
|
hasCapability(capabilityId) {
|
|
286
|
+
if (capabilityId === "mob") {
|
|
287
|
+
return (this._methods.has("mob/create")
|
|
288
|
+
|| this._methods.has("mob/list")
|
|
289
|
+
|| this._methods.has("mob/call"));
|
|
290
|
+
}
|
|
221
291
|
return this._capabilities.some((c) => c.id === capabilityId && c.status === "Available");
|
|
222
292
|
}
|
|
223
293
|
requireCapability(capabilityId) {
|
|
@@ -294,6 +364,172 @@ export class MeerkatClient {
|
|
|
294
364
|
arguments: argumentsPayload ?? {},
|
|
295
365
|
});
|
|
296
366
|
}
|
|
367
|
+
async subscribeSessionEvents(sessionId) {
|
|
368
|
+
return this.openEventSubscription("session/stream_open", { session_id: sessionId }, "session/stream_close", MeerkatClient.parseAgentEventEnvelope);
|
|
369
|
+
}
|
|
370
|
+
async createMob(options) {
|
|
371
|
+
this.requireCapability("mob");
|
|
372
|
+
const result = await this.request("mob/create", options);
|
|
373
|
+
return new Mob(this, String(result.mob_id ?? ""));
|
|
374
|
+
}
|
|
375
|
+
mob(mobId) {
|
|
376
|
+
return new Mob(this, mobId);
|
|
377
|
+
}
|
|
378
|
+
async listMobs() {
|
|
379
|
+
this.requireCapability("mob");
|
|
380
|
+
const result = await this.request("mob/list", {});
|
|
381
|
+
const mobs = result.mobs ?? [];
|
|
382
|
+
return mobs.map((mob) => ({
|
|
383
|
+
mobId: String(mob.mob_id ?? mob.mobId ?? ""),
|
|
384
|
+
status: String(mob.status ?? ""),
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
async mobStatus(mobId) {
|
|
388
|
+
const result = await this.request("mob/status", { mob_id: mobId });
|
|
389
|
+
const rawStatus = result.status;
|
|
390
|
+
const status = typeof rawStatus === "string"
|
|
391
|
+
? rawStatus
|
|
392
|
+
: (typeof rawStatus === "object" && rawStatus !== null
|
|
393
|
+
? String(Object.keys(rawStatus)[0] ?? "unknown")
|
|
394
|
+
: String(rawStatus ?? "unknown"));
|
|
395
|
+
return { mobId: String(result.mob_id ?? mobId), status };
|
|
396
|
+
}
|
|
397
|
+
async listMobMembers(mobId) {
|
|
398
|
+
const result = await this.request("mob/members", { mob_id: mobId });
|
|
399
|
+
const members = result.members ?? [];
|
|
400
|
+
return members.map((member) => ({
|
|
401
|
+
meerkatId: String(member.meerkat_id ?? member.meerkatId ?? ""),
|
|
402
|
+
profile: String(member.profile_name ?? member.profile ?? ""),
|
|
403
|
+
memberRef: member.member_ref,
|
|
404
|
+
runtimeMode: member.runtime_mode != null ? String(member.runtime_mode) : undefined,
|
|
405
|
+
state: member.state != null ? String(member.state) : undefined,
|
|
406
|
+
wiredTo: Array.isArray(member.wired_to)
|
|
407
|
+
? member.wired_to.map((peer) => String(peer))
|
|
408
|
+
: undefined,
|
|
409
|
+
labels: member.labels && typeof member.labels === 'object'
|
|
410
|
+
? Object.fromEntries(Object.entries(member.labels).map(([key, value]) => [key, String(value)]))
|
|
411
|
+
: undefined,
|
|
412
|
+
sessionId: member.member_ref && typeof member.member_ref === 'object'
|
|
413
|
+
? member.member_ref.session_id != null
|
|
414
|
+
? String(member.member_ref.session_id)
|
|
415
|
+
: undefined
|
|
416
|
+
: undefined,
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
async spawnMobMember(mobId, options) {
|
|
420
|
+
return this.request("mob/spawn", {
|
|
421
|
+
mob_id: mobId,
|
|
422
|
+
profile: options.profile,
|
|
423
|
+
meerkat_id: options.meerkatId,
|
|
424
|
+
initial_message: options.initialMessage,
|
|
425
|
+
runtime_mode: options.runtimeMode,
|
|
426
|
+
backend: options.backend,
|
|
427
|
+
resume_session_id: options.resumeSessionId,
|
|
428
|
+
labels: options.labels,
|
|
429
|
+
context: options.context,
|
|
430
|
+
additional_instructions: options.additionalInstructions,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
async retireMobMember(mobId, meerkatId) {
|
|
434
|
+
await this.request("mob/retire", { mob_id: mobId, meerkat_id: meerkatId });
|
|
435
|
+
}
|
|
436
|
+
async respawnMobMember(mobId, meerkatId, initialMessage) {
|
|
437
|
+
await this.request("mob/respawn", { mob_id: mobId, meerkat_id: meerkatId, initial_message: initialMessage });
|
|
438
|
+
}
|
|
439
|
+
async wireMobMembers(mobId, a, b) {
|
|
440
|
+
await this.request("mob/wire", { mob_id: mobId, a, b });
|
|
441
|
+
}
|
|
442
|
+
async unwireMobMembers(mobId, a, b) {
|
|
443
|
+
await this.request("mob/unwire", { mob_id: mobId, a, b });
|
|
444
|
+
}
|
|
445
|
+
async mobLifecycle(mobId, action) {
|
|
446
|
+
await this.request("mob/lifecycle", { mob_id: mobId, action });
|
|
447
|
+
}
|
|
448
|
+
async sendMobMessage(mobId, meerkatId, message) {
|
|
449
|
+
await this.request("mob/send", { mob_id: mobId, meerkat_id: meerkatId, message });
|
|
450
|
+
}
|
|
451
|
+
async appendMobSystemContext(mobId, meerkatId, text, options) {
|
|
452
|
+
return this.request("mob/append_system_context", {
|
|
453
|
+
mob_id: mobId,
|
|
454
|
+
meerkat_id: meerkatId,
|
|
455
|
+
text,
|
|
456
|
+
source: options?.source,
|
|
457
|
+
idempotency_key: options?.idempotencyKey,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
async listMobFlows(mobId) {
|
|
461
|
+
const result = await this.request("mob/flows", { mob_id: mobId });
|
|
462
|
+
return result.flows ?? [];
|
|
463
|
+
}
|
|
464
|
+
async runMobFlow(mobId, flowId, params = {}) {
|
|
465
|
+
const result = await this.request("mob/flow_run", { mob_id: mobId, flow_id: flowId, params });
|
|
466
|
+
return String(result.run_id ?? "");
|
|
467
|
+
}
|
|
468
|
+
async getMobFlowStatus(mobId, runId) {
|
|
469
|
+
const result = await this.request("mob/flow_status", { mob_id: mobId, run_id: runId });
|
|
470
|
+
return result.run == null ? null : { run: result.run };
|
|
471
|
+
}
|
|
472
|
+
async cancelMobFlow(mobId, runId) {
|
|
473
|
+
await this.request("mob/flow_cancel", { mob_id: mobId, run_id: runId });
|
|
474
|
+
}
|
|
475
|
+
async subscribeMobEvents(mobId) {
|
|
476
|
+
return this.openEventSubscription("mob/stream_open", { mob_id: mobId }, "mob/stream_close", MeerkatClient.parseAttributedMobEvent);
|
|
477
|
+
}
|
|
478
|
+
async subscribeMobMemberEvents(mobId, meerkatId) {
|
|
479
|
+
return this.openEventSubscription("mob/stream_open", { mob_id: mobId, member_id: meerkatId }, "mob/stream_close", MeerkatClient.parseAgentEventEnvelope);
|
|
480
|
+
}
|
|
481
|
+
async openEventSubscription(openMethod, params, closeMethod, parse) {
|
|
482
|
+
const result = this.process?.stdin
|
|
483
|
+
? await (async () => {
|
|
484
|
+
this.requestId++;
|
|
485
|
+
const requestId = this.requestId;
|
|
486
|
+
const responsePromise = this.registerRequest(requestId);
|
|
487
|
+
const rpcRequest = { jsonrpc: "2.0", id: requestId, method: openMethod, params };
|
|
488
|
+
this.process.stdin.write(JSON.stringify(rpcRequest) + "\n");
|
|
489
|
+
return responsePromise;
|
|
490
|
+
})()
|
|
491
|
+
: await this.request(openMethod, params);
|
|
492
|
+
const streamId = String(result.stream_id ?? "");
|
|
493
|
+
if (!streamId) {
|
|
494
|
+
throw new MeerkatError("INVALID_RESPONSE", `${openMethod} did not return stream_id`);
|
|
495
|
+
}
|
|
496
|
+
const queue = new AsyncQueue();
|
|
497
|
+
this.streamQueues.set(streamId, queue);
|
|
498
|
+
const buffered = this.unmatchedStandaloneStreamBuffer.get(streamId) ?? [];
|
|
499
|
+
for (const event of buffered) {
|
|
500
|
+
queue.put(event);
|
|
501
|
+
}
|
|
502
|
+
this.unmatchedStandaloneStreamBuffer.delete(streamId);
|
|
503
|
+
if (this.unmatchedStandaloneStreamEnd.delete(streamId)) {
|
|
504
|
+
queue.put(null);
|
|
505
|
+
}
|
|
506
|
+
return new EventSubscription({
|
|
507
|
+
streamId,
|
|
508
|
+
queue,
|
|
509
|
+
closeRemote: async (id) => {
|
|
510
|
+
this.streamQueues.delete(id);
|
|
511
|
+
await this.request(closeMethod, { stream_id: id });
|
|
512
|
+
},
|
|
513
|
+
parseEvent: parse,
|
|
514
|
+
getTerminalOutcome: () => this.streamTerminalOutcomes.get(streamId),
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
static parseAgentEventEnvelope(raw) {
|
|
518
|
+
return {
|
|
519
|
+
eventId: String(raw.event_id ?? raw.eventId ?? ""),
|
|
520
|
+
sourceId: String(raw.source_id ?? raw.sourceId ?? ""),
|
|
521
|
+
seq: Number(raw.seq ?? 0),
|
|
522
|
+
timestampMs: Number(raw.timestamp_ms ?? raw.timestampMs ?? 0),
|
|
523
|
+
payload: parseCoreEvent((raw.payload ?? {})),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
static parseAttributedMobEvent(raw) {
|
|
527
|
+
return {
|
|
528
|
+
source: String(raw.source ?? ""),
|
|
529
|
+
profile: String(raw.profile ?? ""),
|
|
530
|
+
envelope: MeerkatClient.parseAgentEventEnvelope((raw.envelope ?? {})),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
297
533
|
// -- Internal methods used by Session -----------------------------------
|
|
298
534
|
/** @internal */
|
|
299
535
|
async _startTurn(sessionId, prompt, options) {
|
|
@@ -311,6 +547,23 @@ export class MeerkatClient {
|
|
|
311
547
|
blocked_tools: options.flowToolOverlay.blockedTools,
|
|
312
548
|
};
|
|
313
549
|
}
|
|
550
|
+
if (options?.hostMode != null)
|
|
551
|
+
params.host_mode = options.hostMode;
|
|
552
|
+
if (options?.model)
|
|
553
|
+
params.model = options.model;
|
|
554
|
+
if (options?.provider)
|
|
555
|
+
params.provider = options.provider;
|
|
556
|
+
if (options?.maxTokens)
|
|
557
|
+
params.max_tokens = options.maxTokens;
|
|
558
|
+
if (options?.systemPrompt)
|
|
559
|
+
params.system_prompt = options.systemPrompt;
|
|
560
|
+
if (options?.outputSchema)
|
|
561
|
+
params.output_schema = options.outputSchema;
|
|
562
|
+
if (options?.structuredOutputRetries != null) {
|
|
563
|
+
params.structured_output_retries = options.structuredOutputRetries;
|
|
564
|
+
}
|
|
565
|
+
if (options?.providerParams)
|
|
566
|
+
params.provider_params = options.providerParams;
|
|
314
567
|
const raw = await this.request("turn/start", params);
|
|
315
568
|
return MeerkatClient.parseRunResult(raw);
|
|
316
569
|
}
|
|
@@ -370,54 +623,6 @@ export class MeerkatClient {
|
|
|
370
623
|
async peers(sessionId) {
|
|
371
624
|
return this.request("comms/peers", { session_id: sessionId });
|
|
372
625
|
}
|
|
373
|
-
async openCommsStream(sessionId, options) {
|
|
374
|
-
const scope = options?.scope ?? "session";
|
|
375
|
-
const params = { session_id: sessionId, scope };
|
|
376
|
-
if (options?.interactionId) {
|
|
377
|
-
params.interaction_id = options.interactionId;
|
|
378
|
-
}
|
|
379
|
-
const opened = await this.request("comms/stream_open", params);
|
|
380
|
-
const streamId = String(opened.stream_id ?? "");
|
|
381
|
-
if (!streamId) {
|
|
382
|
-
throw new MeerkatError("INVALID_RESPONSE", "Missing stream_id in comms/stream_open response");
|
|
383
|
-
}
|
|
384
|
-
const queue = new AsyncQueue();
|
|
385
|
-
this.commsStreamQueues.set(streamId, queue);
|
|
386
|
-
return new CommsEventStream({
|
|
387
|
-
streamId,
|
|
388
|
-
eventQueue: queue,
|
|
389
|
-
onClose: async (id) => {
|
|
390
|
-
await this.request("comms/stream_close", { stream_id: id });
|
|
391
|
-
const q = this.commsStreamQueues.get(id);
|
|
392
|
-
if (q) {
|
|
393
|
-
q.put(null);
|
|
394
|
-
}
|
|
395
|
-
this.commsStreamQueues.delete(id);
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
async open_comms_stream(sessionId, options) {
|
|
400
|
-
return this.openCommsStream(sessionId, options);
|
|
401
|
-
}
|
|
402
|
-
async sendAndStream(sessionId, command) {
|
|
403
|
-
const receipt = await this.send(sessionId, command);
|
|
404
|
-
const interactionId = String(receipt.interaction_id ?? "");
|
|
405
|
-
const streamReserved = Boolean(receipt.stream_reserved ?? false);
|
|
406
|
-
if (!interactionId) {
|
|
407
|
-
throw new MeerkatError("INVALID_RESPONSE", "comms/send response missing interaction_id for sendAndStream");
|
|
408
|
-
}
|
|
409
|
-
if (!streamReserved) {
|
|
410
|
-
throw new MeerkatError("INVALID_RESPONSE", "comms/send response did not reserve stream for sendAndStream");
|
|
411
|
-
}
|
|
412
|
-
const stream = await this.openCommsStream(sessionId, {
|
|
413
|
-
scope: "interaction",
|
|
414
|
-
interactionId,
|
|
415
|
-
});
|
|
416
|
-
return { receipt, stream };
|
|
417
|
-
}
|
|
418
|
-
async send_and_stream(sessionId, command) {
|
|
419
|
-
return this.sendAndStream(sessionId, command);
|
|
420
|
-
}
|
|
421
626
|
// -- Transport ----------------------------------------------------------
|
|
422
627
|
handleLine(line) {
|
|
423
628
|
let data;
|
|
@@ -427,6 +632,11 @@ export class MeerkatClient {
|
|
|
427
632
|
catch {
|
|
428
633
|
return;
|
|
429
634
|
}
|
|
635
|
+
// Server→client callback request (has both id and method).
|
|
636
|
+
if ("id" in data && "method" in data) {
|
|
637
|
+
this.handleCallbackRequest(data);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
430
640
|
if ("id" in data && typeof data.id === "number") {
|
|
431
641
|
const pending = this.pendingRequests.get(data.id);
|
|
432
642
|
if (pending) {
|
|
@@ -441,16 +651,42 @@ export class MeerkatClient {
|
|
|
441
651
|
}
|
|
442
652
|
}
|
|
443
653
|
else if ("method" in data) {
|
|
444
|
-
|
|
445
|
-
|
|
654
|
+
const method = String(data.method ?? "");
|
|
655
|
+
const params = (data.params ?? {});
|
|
656
|
+
if (method === "session/stream_event" || method === "mob/stream_event") {
|
|
657
|
+
const streamId = String(params.stream_id ?? "");
|
|
658
|
+
const queue = this.streamQueues.get(streamId);
|
|
659
|
+
const rawEvent = (params.event ?? params);
|
|
660
|
+
// Preserve scope fields when present (sub-agent / mob-member scoped events).
|
|
661
|
+
const scopeId = params.scope_id;
|
|
662
|
+
const scopePath = params.scope_path;
|
|
663
|
+
const event = scopeId != null || scopePath != null
|
|
664
|
+
? { event: rawEvent, scope_id: scopeId, scope_path: scopePath }
|
|
665
|
+
: rawEvent;
|
|
666
|
+
if (queue && event) {
|
|
667
|
+
queue.put(event);
|
|
668
|
+
}
|
|
669
|
+
else if (streamId && event) {
|
|
670
|
+
const buffered = this.unmatchedStandaloneStreamBuffer.get(streamId) ?? [];
|
|
671
|
+
buffered.push(event);
|
|
672
|
+
this.unmatchedStandaloneStreamBuffer.set(streamId, buffered);
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (method === "session/stream_end" || method === "mob/stream_end") {
|
|
446
677
|
const streamId = String(params.stream_id ?? "");
|
|
447
|
-
|
|
678
|
+
if (streamId) {
|
|
679
|
+
this.streamTerminalOutcomes.set(streamId, params);
|
|
680
|
+
}
|
|
681
|
+
const queue = this.streamQueues.get(streamId);
|
|
448
682
|
if (queue) {
|
|
449
|
-
queue.put(
|
|
683
|
+
queue.put(null);
|
|
684
|
+
}
|
|
685
|
+
else if (streamId) {
|
|
686
|
+
this.unmatchedStandaloneStreamEnd.set(streamId, params);
|
|
450
687
|
}
|
|
451
688
|
return;
|
|
452
689
|
}
|
|
453
|
-
const params = (data.params ?? {});
|
|
454
690
|
const sessionId = String(params.session_id ?? "");
|
|
455
691
|
const event = params.event;
|
|
456
692
|
if (event) {
|
|
@@ -567,6 +803,58 @@ export class MeerkatClient {
|
|
|
567
803
|
skillDiagnostics: MeerkatClient.parseSkillDiagnostics(data.skill_diagnostics),
|
|
568
804
|
};
|
|
569
805
|
}
|
|
806
|
+
static parseSessionHistory(data) {
|
|
807
|
+
const rawMessages = Array.isArray(data.messages)
|
|
808
|
+
? data.messages
|
|
809
|
+
: [];
|
|
810
|
+
return {
|
|
811
|
+
sessionId: String(data.session_id ?? ""),
|
|
812
|
+
sessionRef: data.session_ref != null ? String(data.session_ref) : undefined,
|
|
813
|
+
messageCount: Number(data.message_count ?? 0),
|
|
814
|
+
offset: Number(data.offset ?? 0),
|
|
815
|
+
limit: data.limit != null ? Number(data.limit) : undefined,
|
|
816
|
+
hasMore: Boolean(data.has_more ?? false),
|
|
817
|
+
messages: rawMessages.map((message) => MeerkatClient.parseSessionMessage(message)),
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
static parseSessionMessage(data) {
|
|
821
|
+
const rawToolCalls = Array.isArray(data.tool_calls)
|
|
822
|
+
? data.tool_calls
|
|
823
|
+
: [];
|
|
824
|
+
const rawBlocks = Array.isArray(data.blocks)
|
|
825
|
+
? data.blocks
|
|
826
|
+
: [];
|
|
827
|
+
const rawResults = Array.isArray(data.results)
|
|
828
|
+
? data.results
|
|
829
|
+
: [];
|
|
830
|
+
return {
|
|
831
|
+
role: String(data.role ?? ""),
|
|
832
|
+
content: data.content != null ? String(data.content) : undefined,
|
|
833
|
+
toolCalls: rawToolCalls.map((toolCall) => ({
|
|
834
|
+
id: String(toolCall.id ?? ""),
|
|
835
|
+
name: String(toolCall.name ?? ""),
|
|
836
|
+
args: toolCall.args,
|
|
837
|
+
})),
|
|
838
|
+
stopReason: data.stop_reason != null ? String(data.stop_reason) : undefined,
|
|
839
|
+
blocks: rawBlocks.map((block) => MeerkatClient.parseSessionAssistantBlock(block)),
|
|
840
|
+
results: rawResults.map((result) => ({
|
|
841
|
+
toolUseId: String(result.tool_use_id ?? ""),
|
|
842
|
+
content: String(result.content ?? ""),
|
|
843
|
+
isError: Boolean(result.is_error ?? false),
|
|
844
|
+
})),
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
static parseSessionAssistantBlock(data) {
|
|
848
|
+
const blockData = data.data ?? {};
|
|
849
|
+
return {
|
|
850
|
+
blockType: String(data.block_type ?? ""),
|
|
851
|
+
text: blockData.text != null ? String(blockData.text) : undefined,
|
|
852
|
+
id: blockData.id != null ? String(blockData.id) : undefined,
|
|
853
|
+
name: blockData.name != null ? String(blockData.name) : undefined,
|
|
854
|
+
args: blockData.args,
|
|
855
|
+
meta: blockData.meta,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
570
858
|
static parseMcpLiveOpResponse(raw) {
|
|
571
859
|
const sessionId = raw.session_id;
|
|
572
860
|
const operation = raw.operation;
|
|
@@ -586,6 +874,48 @@ export class MeerkatClient {
|
|
|
586
874
|
}
|
|
587
875
|
return raw;
|
|
588
876
|
}
|
|
877
|
+
handleCallbackRequest(data) {
|
|
878
|
+
const requestId = data.id;
|
|
879
|
+
const method = String(data.method ?? "");
|
|
880
|
+
const params = (data.params ?? {});
|
|
881
|
+
if (method === "tool/execute") {
|
|
882
|
+
const toolName = String(params.name ?? "");
|
|
883
|
+
const handler = this.toolHandlers.get(toolName);
|
|
884
|
+
if (handler) {
|
|
885
|
+
const args = (params.arguments ?? {});
|
|
886
|
+
handler
|
|
887
|
+
.handler(args)
|
|
888
|
+
.then((content) => {
|
|
889
|
+
this.writeCallbackResponse(requestId, { content, is_error: false });
|
|
890
|
+
})
|
|
891
|
+
.catch((err) => {
|
|
892
|
+
this.writeCallbackResponse(requestId, {
|
|
893
|
+
content: `Tool error: ${err}`,
|
|
894
|
+
is_error: true,
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
this.writeCallbackResponse(requestId, {
|
|
900
|
+
content: `Unknown tool: ${toolName}`,
|
|
901
|
+
is_error: true,
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
// Unknown callback method.
|
|
907
|
+
const response = {
|
|
908
|
+
jsonrpc: "2.0",
|
|
909
|
+
id: requestId,
|
|
910
|
+
error: { code: -32601, message: `Method not supported: ${method}` },
|
|
911
|
+
};
|
|
912
|
+
this.process?.stdin?.write(JSON.stringify(response) + "\n");
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
writeCallbackResponse(requestId, result) {
|
|
916
|
+
const response = { jsonrpc: "2.0", id: requestId, result };
|
|
917
|
+
this.process?.stdin?.write(JSON.stringify(response) + "\n");
|
|
918
|
+
}
|
|
589
919
|
static buildCreateParams(prompt, options) {
|
|
590
920
|
const params = { prompt };
|
|
591
921
|
if (!options)
|