@mirasoth/soothe-client 0.5.5 → 0.5.8

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/index.js CHANGED
@@ -3,7 +3,6 @@ import {
3
3
  Client,
4
4
  ConnectionError,
5
5
  DEFAULT_CLIENT_CAPABILITIES,
6
- DEFAULT_DELIVERABLE_PHASES,
7
6
  DaemonError,
8
7
  DisconnectCause,
9
8
  EventAutopilotGoalCompleted,
@@ -43,13 +42,7 @@ import {
43
42
  EventToolCompleted,
44
43
  EventToolError,
45
44
  EventToolStarted,
46
- INTENT_HINT_EMBED,
47
- INTENT_HINT_IMAGE_TO_TEXT,
48
- INTENT_HINT_OCR,
49
- INTENT_HINT_TEXT_COMPLETION,
50
- LOOP_ASSISTANT_OUTPUT_PHASES,
51
45
  PROTO_VERSION,
52
- REMOVED_INTENT_HINTS,
53
46
  ReconnectError,
54
47
  STREAM_END,
55
48
  StaleLoopError,
@@ -82,9 +75,34 @@ import {
82
75
  shouldShow,
83
76
  splitWirePayload,
84
77
  subscribeEnvelope,
85
- unsubscribeEnvelope,
86
- validateLoopInputIntentHint
87
- } from "./chunk-XEZBHENR.js";
78
+ unsubscribeEnvelope
79
+ } from "./chunk-CFGENQFE.js";
80
+
81
+ // src/intent_hints.ts
82
+ var INTENT_HINT_TEXT_COMPLETION = "text_completion";
83
+ var INTENT_HINT_IMAGE_TO_TEXT = "image_to_text";
84
+ var INTENT_HINT_OCR = "ocr";
85
+ var INTENT_HINT_EMBED = "embed";
86
+ var LOOP_ASSISTANT_OUTPUT_PHASES = [
87
+ "goal_completion",
88
+ "quiz",
89
+ "autonomous_goal",
90
+ "chitchat",
91
+ "text_completion",
92
+ "image_to_text",
93
+ "ocr",
94
+ "embed",
95
+ "plan_direct"
96
+ ];
97
+ var DEFAULT_DELIVERABLE_PHASES = /* @__PURE__ */ new Set([
98
+ "quiz",
99
+ "goal_completion",
100
+ "chitchat",
101
+ "text_completion",
102
+ "image_to_text",
103
+ "ocr",
104
+ "embed"
105
+ ]);
88
106
 
89
107
  // src/session.ts
90
108
  async function bootstrapLoopSession(client, resumeLoopId, config, loopNew) {
@@ -270,6 +288,10 @@ var CommandClient = class {
270
288
  async autopilotGetJob(jobId) {
271
289
  return this.request("autopilot_get_job", { job_id: jobId });
272
290
  }
291
+ /** Jobs → goals → loops snapshot for CLI top. */
292
+ async autopilotTop(includeTerminal = false) {
293
+ return this.request("autopilot_top", { include_terminal: includeTerminal });
294
+ }
273
295
  async cronAdd(text, priority = 0) {
274
296
  const params = { text };
275
297
  if (priority > 0) params.priority = priority;
@@ -287,7 +309,7 @@ async function checkDaemonStatus(client, timeout) {
287
309
  return client.requestResponse("daemon_status", {}, "daemon_status", timeout ?? 5e3);
288
310
  }
289
311
  async function isDaemonLive(wsURL, timeout) {
290
- const { Client: Client2 } = await import("./client-TQNJNIH2.js");
312
+ const { Client: Client2 } = await import("./client-57KMIFVI.js");
291
313
  const t = timeout ?? 5e3;
292
314
  const client = new Client2(wsURL, defaultConfig());
293
315
  try {
@@ -371,7 +393,7 @@ async function fetchLoopMessages(client, loopID, opts) {
371
393
  );
372
394
  }
373
395
  async function connectedWebsocket(wsUrl, fn, timeoutMs = 3e4) {
374
- const { Client: Client2 } = await import("./client-TQNJNIH2.js");
396
+ const { Client: Client2 } = await import("./client-57KMIFVI.js");
375
397
  const client = new Client2(wsUrl, defaultConfig());
376
398
  const deadline = Date.now() + timeoutMs;
377
399
  try {
@@ -428,6 +450,59 @@ async function protocol1Rpc(wsUrl, method, params = null, opts = {}) {
428
450
  }
429
451
  }
430
452
 
453
+ // src/turn_boundary.ts
454
+ function formatTurnId(loopId, generation) {
455
+ const lid = String(loopId || "").trim();
456
+ const gen = Number(generation);
457
+ if (!lid || !Number.isFinite(gen) || gen <= 0) return "";
458
+ return `${lid}:${Math.trunc(gen)}`;
459
+ }
460
+ function parseTurnGeneration(turnId) {
461
+ const raw = String(turnId || "").trim();
462
+ if (!raw || !raw.includes(":")) return null;
463
+ const suffix = raw.split(":").pop() ?? "";
464
+ const gen = Number.parseInt(suffix, 10);
465
+ return Number.isFinite(gen) && gen > 0 ? gen : null;
466
+ }
467
+ function frameTurnId(frame) {
468
+ if (!frame || typeof frame !== "object") return null;
469
+ const tid = frame.turn_id;
470
+ if (typeof tid === "string" && tid.trim()) return tid.trim();
471
+ const data = frame.data;
472
+ if (data && typeof data === "object") {
473
+ const inner = data.turn_id;
474
+ if (typeof inner === "string" && inner.trim()) return inner.trim();
475
+ }
476
+ return null;
477
+ }
478
+ function frameSeq(frame) {
479
+ if (!frame || typeof frame !== "object") return null;
480
+ const raw = frame.seq;
481
+ if (typeof raw === "boolean") return null;
482
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0 && Number.isInteger(raw)) {
483
+ return raw;
484
+ }
485
+ return null;
486
+ }
487
+ function turnIdsMatch(expected, candidate) {
488
+ const exp = String(expected || "").trim();
489
+ const cand = String(candidate || "").trim();
490
+ return Boolean(exp) && Boolean(cand) && exp === cand;
491
+ }
492
+ function isTurnTerminalAllowed(opts) {
493
+ if (!opts.queryStarted || !opts.turnProgressSeen) return false;
494
+ return turnIdsMatch(opts.expectedTurnId, opts.frameTurnId);
495
+ }
496
+ function isIdleTerminalAllowed(opts) {
497
+ if (!opts.queryStarted || !String(opts.expectedTurnId || "").trim()) return false;
498
+ const cand = String(opts.frameTurnId || "").trim();
499
+ if (cand) {
500
+ if (!turnIdsMatch(opts.expectedTurnId, cand)) return false;
501
+ return Boolean(opts.turnProgressSeen || opts.cancellationSeen);
502
+ }
503
+ return Boolean(opts.cancellationSeen);
504
+ }
505
+
431
506
  // src/appkit/broadcaster.ts
432
507
  var SUBSCRIBER_QUEUE_CAP = 100;
433
508
  var SSEBroadcaster = class {
@@ -1384,8 +1459,9 @@ var TURN_END_IDLE = "status.idle";
1384
1459
  var TURN_END_STOPPED = "status.stopped";
1385
1460
  var TurnLifecycleGate = class {
1386
1461
  sawRunning = false;
1387
- sawStreamPayload = false;
1388
1462
  sawTurnProgress = false;
1463
+ expectedTurnId = null;
1464
+ cancellationSeen = false;
1389
1465
  observe(msg) {
1390
1466
  const frame = normalizeFrame(msg);
1391
1467
  if (!frame) return;
@@ -1393,22 +1469,43 @@ var TurnLifecycleGate = class {
1393
1469
  if (typ === "status") {
1394
1470
  if (String(frame.state ?? "").trim().toLowerCase() === "running") {
1395
1471
  this.sawRunning = true;
1472
+ const statusTurn = frameTurnId(frame);
1473
+ if (statusTurn) {
1474
+ const newGen = parseTurnGeneration(statusTurn);
1475
+ const oldGen = parseTurnGeneration(this.expectedTurnId);
1476
+ if (this.expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
1477
+ if (this.expectedTurnId && statusTurn !== this.expectedTurnId) {
1478
+ this.sawTurnProgress = false;
1479
+ }
1480
+ this.expectedTurnId = statusTurn;
1481
+ }
1482
+ }
1396
1483
  }
1397
1484
  return;
1398
1485
  }
1399
1486
  if (typ === "event") {
1400
- this.sawStreamPayload = true;
1401
1487
  const mode = String(frame.mode ?? "");
1402
1488
  if (isTurnProgressChunk(mode, frame.data)) {
1403
1489
  this.sawTurnProgress = true;
1404
1490
  }
1405
1491
  }
1406
1492
  }
1407
- allowStreamEnd() {
1408
- return this.sawRunning && this.sawTurnProgress;
1493
+ allowStreamEnd(frameTurn) {
1494
+ return isTurnTerminalAllowed({
1495
+ expectedTurnId: this.expectedTurnId,
1496
+ frameTurnId: frameTurn,
1497
+ queryStarted: this.sawRunning,
1498
+ turnProgressSeen: this.sawTurnProgress
1499
+ });
1409
1500
  }
1410
- allowIdleComplete() {
1411
- return this.sawRunning && this.sawStreamPayload;
1501
+ allowIdleComplete(frameTurn) {
1502
+ return isIdleTerminalAllowed({
1503
+ expectedTurnId: this.expectedTurnId,
1504
+ frameTurnId: frameTurn,
1505
+ queryStarted: this.sawRunning,
1506
+ turnProgressSeen: this.sawTurnProgress,
1507
+ cancellationSeen: this.cancellationSeen
1508
+ });
1412
1509
  }
1413
1510
  };
1414
1511
  var TurnBoundary = class {
@@ -1423,17 +1520,23 @@ var TurnBoundary = class {
1423
1520
  const typ = String(frame.type ?? "");
1424
1521
  if (typ === "status") {
1425
1522
  const state = String(frame.state ?? "").trim().toLowerCase();
1523
+ const frameTurn = frameTurnId(frame);
1426
1524
  if (state === "stopped" && this.gate.sawRunning) {
1525
+ if (this.gate.expectedTurnId && !turnIdsMatch(this.gate.expectedTurnId, frameTurn)) {
1526
+ return [false, ""];
1527
+ }
1427
1528
  return this.mark(TURN_END_STOPPED);
1428
1529
  }
1429
- if (state === "idle" && this.gate.allowIdleComplete()) {
1530
+ if (state === "idle" && this.gate.allowIdleComplete(frameTurn)) {
1430
1531
  return this.mark(TURN_END_IDLE);
1431
1532
  }
1432
1533
  return [false, ""];
1433
1534
  }
1434
1535
  if (typ === "event") {
1435
1536
  const mode = String(frame.mode ?? "");
1436
- if (mode === "custom" && isTurnEndCustomData(frame.data) && this.gate.allowStreamEnd()) {
1537
+ const data = frame.data;
1538
+ const dataTurn = frameTurnId(data) || frameTurnId(frame);
1539
+ if (mode === "custom" && isTurnEndCustomData(data) && this.gate.allowStreamEnd(dataTurn)) {
1437
1540
  return this.mark(TURN_END_STREAM_END);
1438
1541
  }
1439
1542
  }
@@ -1459,20 +1562,26 @@ function normalizeFrame(msg) {
1459
1562
  return inner;
1460
1563
  }
1461
1564
  if (inner && typeof inner === "object" && inner.mode) {
1462
- return {
1565
+ const out = {
1463
1566
  type: "event",
1464
1567
  mode: inner.mode,
1465
1568
  data: inner.data,
1466
1569
  namespace: inner.namespace ?? payload.namespace
1467
1570
  };
1571
+ const tid = inner.turn_id ?? payload.turn_id ?? m.turn_id;
1572
+ if (tid) out.turn_id = tid;
1573
+ return out;
1468
1574
  }
1469
1575
  if (payload.mode) {
1470
- return {
1576
+ const out = {
1471
1577
  type: "event",
1472
1578
  mode: payload.mode,
1473
1579
  data: payload.data,
1474
1580
  namespace: payload.namespace
1475
1581
  };
1582
+ const tid = payload.turn_id ?? m.turn_id;
1583
+ if (tid) out.turn_id = tid;
1584
+ return out;
1476
1585
  }
1477
1586
  return null;
1478
1587
  }
@@ -1505,13 +1614,10 @@ function inputMessageForLoop(text, loopID, attachments, opts) {
1505
1614
  if (attachments && attachments.length > 0) msg.attachments = attachments;
1506
1615
  if (opts) {
1507
1616
  if (opts.intentHint?.trim()) {
1508
- const hintError = validateLoopInputIntentHint(opts.intentHint);
1509
- if (hintError) {
1510
- throw new Error(hintError);
1511
- }
1512
1617
  msg.intent_hint = opts.intentHint.trim();
1513
1618
  }
1514
1619
  if (opts.preferredSubagent?.trim()) msg.preferred_subagent = opts.preferredSubagent.trim();
1620
+ if (opts.intakeScope?.trim()) msg.intake_scope = opts.intakeScope.trim();
1515
1621
  if (opts.responseSchema && Object.keys(opts.responseSchema).length > 0) {
1516
1622
  msg.response_schema = opts.responseSchema;
1517
1623
  }
@@ -2034,8 +2140,6 @@ var DaemonSession = class {
2034
2140
  if (!this.loopId) throw new Error("No active loop session");
2035
2141
  await this.client.sendInput(text, {
2036
2142
  loopID: this.loopId,
2037
- autonomous: options?.autonomous,
2038
- maxIterations: options?.maxIterations,
2039
2143
  subagent: options?.preferredSubagent,
2040
2144
  model: options?.model,
2041
2145
  modelParams: options?.modelParams,
@@ -2161,7 +2265,7 @@ var DaemonSession = class {
2161
2265
  this.lastTurnErrorMessage = null;
2162
2266
  let queryStarted = false;
2163
2267
  let expectedLoopId = this.loopId;
2164
- let streamPayloadSeen = false;
2268
+ let expectedTurnId = null;
2165
2269
  let turnProgressSeen = false;
2166
2270
  this.streaming = true;
2167
2271
  const absoluteDeadline = opts.maxWaitMs !== void 0 && opts.maxWaitMs > 0 ? Date.now() + opts.maxWaitMs : null;
@@ -2195,6 +2299,17 @@ var DaemonSession = class {
2195
2299
  if (expectedLoopId && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== expectedLoopId) {
2196
2300
  continue;
2197
2301
  }
2302
+ const evTurnId = frameTurnId(frame);
2303
+ const statusState = eventType === "status" ? String(frame.state ?? "") : "";
2304
+ const isRunningStatus = statusState === "running";
2305
+ const isTerminalStatus = statusState === "idle" || statusState === "stopped";
2306
+ if (expectedTurnId && (eventType === "event" || eventType === "status") && !isRunningStatus) {
2307
+ if (isTerminalStatus) {
2308
+ if (evTurnId && !turnIdsMatch(expectedTurnId, evTurnId)) continue;
2309
+ } else if (!turnIdsMatch(expectedTurnId, evTurnId)) {
2310
+ continue;
2311
+ }
2312
+ }
2198
2313
  if (eventType === "error") {
2199
2314
  const errObj = frame.error ?? {};
2200
2315
  throw new Error(String(errObj.message || frame.message || "daemon error"));
@@ -2205,16 +2320,37 @@ var DaemonSession = class {
2205
2320
  this.loopId = loopEv;
2206
2321
  expectedLoopId = loopEv;
2207
2322
  }
2208
- const state = String(frame.state ?? "");
2209
- if (state === "running") {
2323
+ if (statusState === "running") {
2210
2324
  queryStarted = true;
2211
- } else if (queryStarted && state === "stopped") {
2212
- this.lastTurnEndState = state;
2325
+ const statusTurn = frameTurnId(frame);
2326
+ if (statusTurn) {
2327
+ const newGen = parseTurnGeneration(statusTurn);
2328
+ const oldGen = parseTurnGeneration(expectedTurnId);
2329
+ if (expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
2330
+ if (expectedTurnId && statusTurn !== expectedTurnId) {
2331
+ turnProgressSeen = false;
2332
+ }
2333
+ expectedTurnId = statusTurn;
2334
+ }
2335
+ }
2336
+ } else if (queryStarted && statusState === "stopped") {
2337
+ const stopTurn = frameTurnId(frame);
2338
+ if (expectedTurnId && !turnIdsMatch(expectedTurnId, stopTurn)) continue;
2339
+ this.lastTurnEndState = statusState;
2213
2340
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
2214
2341
  break;
2215
- } else if (queryStarted && state === "idle") {
2216
- if (!streamPayloadSeen && !this.lastTurnCancellationSeen) continue;
2217
- this.lastTurnEndState = state;
2342
+ } else if (queryStarted && statusState === "idle") {
2343
+ const idleTurn = frameTurnId(frame);
2344
+ if (!isIdleTerminalAllowed({
2345
+ expectedTurnId,
2346
+ frameTurnId: idleTurn,
2347
+ queryStarted,
2348
+ turnProgressSeen,
2349
+ cancellationSeen: this.lastTurnCancellationSeen
2350
+ })) {
2351
+ continue;
2352
+ }
2353
+ this.lastTurnEndState = statusState;
2218
2354
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
2219
2355
  break;
2220
2356
  }
@@ -2236,9 +2372,16 @@ var DaemonSession = class {
2236
2372
  continue;
2237
2373
  }
2238
2374
  if (mode === "custom" && isTurnEndCustomData(data)) {
2239
- if (!queryStarted || !turnProgressSeen) continue;
2375
+ const dataTurn = frameTurnId(data) || evTurnId;
2376
+ if (!isTurnTerminalAllowed({
2377
+ expectedTurnId,
2378
+ frameTurnId: dataTurn,
2379
+ queryStarted,
2380
+ turnProgressSeen
2381
+ })) {
2382
+ continue;
2383
+ }
2240
2384
  }
2241
- streamPayloadSeen = true;
2242
2385
  if (isTurnProgressChunk(mode, data)) turnProgressSeen = true;
2243
2386
  yield [namespace, mode, data];
2244
2387
  if (mode === "custom" && isTurnEndCustomData(data)) {
@@ -2399,7 +2542,6 @@ export {
2399
2542
  PROTO_VERSION,
2400
2543
  PooledConn,
2401
2544
  QueryGate,
2402
- REMOVED_INTENT_HINTS,
2403
2545
  ReconnectError,
2404
2546
  SSEBroadcaster,
2405
2547
  STREAM_END,
@@ -2437,15 +2579,20 @@ export {
2437
2579
  fetchLoopHistory,
2438
2580
  fetchLoopMessages,
2439
2581
  fetchSkillsCatalog,
2582
+ formatTurnId,
2583
+ frameSeq,
2584
+ frameTurnId,
2440
2585
  idleTimeoutForTurn,
2441
2586
  inboundNeedsDeliveryAck,
2442
2587
  inputMessageForLoop,
2443
2588
  isCompletionEvent,
2444
2589
  isDaemonLive,
2445
2590
  isDaemonTurnEndEvent,
2591
+ isIdleTerminalAllowed,
2446
2592
  isSubagentProgressEvent,
2447
2593
  isTurnEndCustomData,
2448
2594
  isTurnProgressChunk,
2595
+ isTurnTerminalAllowed,
2449
2596
  isValidVerbosityLevel,
2450
2597
  loadConfigFromEnv,
2451
2598
  newLoopInputMessage,
@@ -2455,6 +2602,7 @@ export {
2455
2602
  notificationEnvelope,
2456
2603
  parseCardCustomPayload,
2457
2604
  parseNamespace,
2605
+ parseTurnGeneration,
2458
2606
  pingEnvelope,
2459
2607
  pongEnvelope,
2460
2608
  protocol1Rpc,
@@ -2465,8 +2613,8 @@ export {
2465
2613
  shouldShow,
2466
2614
  splitWirePayload,
2467
2615
  subscribeEnvelope,
2616
+ turnIdsMatch,
2468
2617
  unsubscribeEnvelope,
2469
- validateLoopInputIntentHint,
2470
2618
  waitDaemonReady,
2471
2619
  waitLoopStatusWithID,
2472
2620
  waitSubscriptionConfirmed