@mirasoth/soothe-client 0.5.4 → 0.5.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/index.cjs CHANGED
@@ -1701,10 +1701,6 @@ var init_client = __esm({
1701
1701
  if (asNode) params.as_node = asNode;
1702
1702
  return this.sendMessage(requestEnvelope("loop_state_update", params));
1703
1703
  }
1704
- /** Requests display card ledger snapshot. */
1705
- sendLoopCardsFetch(loopID) {
1706
- return this.sendMessage(requestEnvelope("loop_cards_fetch", { loop_id: loopID }));
1707
- }
1708
1704
  /** Requests the full loop history. */
1709
1705
  sendLoopHistoryFetch(loopID) {
1710
1706
  return this.sendMessage(requestEnvelope("loop_history_fetch", { loop_id: loopID }));
@@ -1755,15 +1751,6 @@ var init_client = __esm({
1755
1751
  timeout ?? 15e3
1756
1752
  );
1757
1753
  }
1758
- /** Requests display cards and waits for response. */
1759
- fetchLoopCards(loopID, timeout) {
1760
- return this.requestResponse(
1761
- "loop_cards_fetch",
1762
- { loop_id: loopID },
1763
- "loop_cards_fetch",
1764
- timeout ?? 15e3
1765
- );
1766
- }
1767
1754
  /** Requests MCP status and waits for response. */
1768
1755
  getMCPStatus(timeout) {
1769
1756
  return this.requestResponse("mcp_status", {}, "mcp_status", timeout ?? 15e3);
@@ -1927,6 +1914,16 @@ var init_client = __esm({
1927
1914
  timeout ?? 15e3
1928
1915
  );
1929
1916
  }
1917
+ /** Jobs → goals → loops snapshot for CLI top. */
1918
+ autopilotTop(timeoutOrOptions) {
1919
+ const options = typeof timeoutOrOptions === "number" ? { timeout: timeoutOrOptions } : timeoutOrOptions;
1920
+ return this.requestResponse(
1921
+ "autopilot_top",
1922
+ { include_terminal: options?.includeTerminal ?? false },
1923
+ "autopilot_top",
1924
+ options?.timeout ?? 15e3
1925
+ );
1926
+ }
1930
1927
  /** Subscribes to autopilot worker events. */
1931
1928
  autopilotSubscribe(timeout) {
1932
1929
  return this.subscribe("autopilot_events", {}, timeout ?? 15e3);
@@ -2097,19 +2094,23 @@ __export(index_exports, {
2097
2094
  extractSootheLoopID: () => extractSootheLoopID,
2098
2095
  extractThinkingStep: () => extractThinkingStep,
2099
2096
  fetchConfigSection: () => fetchConfigSection,
2100
- fetchLoopCards: () => fetchLoopCards,
2101
2097
  fetchLoopHistory: () => fetchLoopHistory,
2102
2098
  fetchLoopMessages: () => fetchLoopMessages,
2103
2099
  fetchSkillsCatalog: () => fetchSkillsCatalog,
2100
+ formatTurnId: () => formatTurnId,
2101
+ frameSeq: () => frameSeq,
2102
+ frameTurnId: () => frameTurnId,
2104
2103
  idleTimeoutForTurn: () => idleTimeoutForTurn,
2105
2104
  inboundNeedsDeliveryAck: () => inboundNeedsDeliveryAck,
2106
2105
  inputMessageForLoop: () => inputMessageForLoop,
2107
2106
  isCompletionEvent: () => isCompletionEvent,
2108
2107
  isDaemonLive: () => isDaemonLive,
2109
2108
  isDaemonTurnEndEvent: () => isDaemonTurnEndEvent,
2109
+ isIdleTerminalAllowed: () => isIdleTerminalAllowed,
2110
2110
  isSubagentProgressEvent: () => isSubagentProgressEvent,
2111
2111
  isTurnEndCustomData: () => isTurnEndCustomData,
2112
2112
  isTurnProgressChunk: () => isTurnProgressChunk,
2113
+ isTurnTerminalAllowed: () => isTurnTerminalAllowed,
2113
2114
  isValidVerbosityLevel: () => isValidVerbosityLevel,
2114
2115
  loadConfigFromEnv: () => loadConfigFromEnv,
2115
2116
  newLoopInputMessage: () => newLoopInputMessage,
@@ -2119,6 +2120,7 @@ __export(index_exports, {
2119
2120
  notificationEnvelope: () => notificationEnvelope,
2120
2121
  parseCardCustomPayload: () => parseCardCustomPayload,
2121
2122
  parseNamespace: () => parseNamespace,
2123
+ parseTurnGeneration: () => parseTurnGeneration,
2122
2124
  pingEnvelope: () => pingEnvelope,
2123
2125
  pongEnvelope: () => pongEnvelope,
2124
2126
  protocol1Rpc: () => protocol1Rpc,
@@ -2129,6 +2131,7 @@ __export(index_exports, {
2129
2131
  shouldShow: () => shouldShow,
2130
2132
  splitWirePayload: () => splitWirePayload,
2131
2133
  subscribeEnvelope: () => subscribeEnvelope,
2134
+ turnIdsMatch: () => turnIdsMatch,
2132
2135
  unsubscribeEnvelope: () => unsubscribeEnvelope,
2133
2136
  validateLoopInputIntentHint: () => validateLoopInputIntentHint,
2134
2137
  waitDaemonReady: () => waitDaemonReady,
@@ -2335,6 +2338,10 @@ var CommandClient = class {
2335
2338
  async autopilotGetJob(jobId) {
2336
2339
  return this.request("autopilot_get_job", { job_id: jobId });
2337
2340
  }
2341
+ /** Jobs → goals → loops snapshot for CLI top. */
2342
+ async autopilotTop(includeTerminal = false) {
2343
+ return this.request("autopilot_top", { include_terminal: includeTerminal });
2344
+ }
2338
2345
  async cronAdd(text, priority = 0) {
2339
2346
  const params = { text };
2340
2347
  if (priority > 0) params.priority = priority;
@@ -2427,9 +2434,6 @@ async function refreshAuthToken(client, refreshToken, timeout) {
2427
2434
  timeout ?? 15e3
2428
2435
  );
2429
2436
  }
2430
- async function fetchLoopCards(client, loopID, timeout) {
2431
- return client.fetchLoopCards(loopID, timeout);
2432
- }
2433
2437
  async function fetchLoopMessages(client, loopID, opts) {
2434
2438
  return client.getLoopMessages(
2435
2439
  loopID,
@@ -2500,6 +2504,59 @@ async function protocol1Rpc(wsUrl, method, params = null, opts = {}) {
2500
2504
  // src/index.ts
2501
2505
  init_stream_terminal();
2502
2506
 
2507
+ // src/turn_boundary.ts
2508
+ function formatTurnId(loopId, generation) {
2509
+ const lid = String(loopId || "").trim();
2510
+ const gen = Number(generation);
2511
+ if (!lid || !Number.isFinite(gen) || gen <= 0) return "";
2512
+ return `${lid}:${Math.trunc(gen)}`;
2513
+ }
2514
+ function parseTurnGeneration(turnId) {
2515
+ const raw = String(turnId || "").trim();
2516
+ if (!raw || !raw.includes(":")) return null;
2517
+ const suffix = raw.split(":").pop() ?? "";
2518
+ const gen = Number.parseInt(suffix, 10);
2519
+ return Number.isFinite(gen) && gen > 0 ? gen : null;
2520
+ }
2521
+ function frameTurnId(frame) {
2522
+ if (!frame || typeof frame !== "object") return null;
2523
+ const tid = frame.turn_id;
2524
+ if (typeof tid === "string" && tid.trim()) return tid.trim();
2525
+ const data = frame.data;
2526
+ if (data && typeof data === "object") {
2527
+ const inner = data.turn_id;
2528
+ if (typeof inner === "string" && inner.trim()) return inner.trim();
2529
+ }
2530
+ return null;
2531
+ }
2532
+ function frameSeq(frame) {
2533
+ if (!frame || typeof frame !== "object") return null;
2534
+ const raw = frame.seq;
2535
+ if (typeof raw === "boolean") return null;
2536
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0 && Number.isInteger(raw)) {
2537
+ return raw;
2538
+ }
2539
+ return null;
2540
+ }
2541
+ function turnIdsMatch(expected, candidate) {
2542
+ const exp = String(expected || "").trim();
2543
+ const cand = String(candidate || "").trim();
2544
+ return Boolean(exp) && Boolean(cand) && exp === cand;
2545
+ }
2546
+ function isTurnTerminalAllowed(opts) {
2547
+ if (!opts.queryStarted || !opts.turnProgressSeen) return false;
2548
+ return turnIdsMatch(opts.expectedTurnId, opts.frameTurnId);
2549
+ }
2550
+ function isIdleTerminalAllowed(opts) {
2551
+ if (!opts.queryStarted || !String(opts.expectedTurnId || "").trim()) return false;
2552
+ const cand = String(opts.frameTurnId || "").trim();
2553
+ if (cand) {
2554
+ if (!turnIdsMatch(opts.expectedTurnId, cand)) return false;
2555
+ return Boolean(opts.turnProgressSeen || opts.cancellationSeen);
2556
+ }
2557
+ return Boolean(opts.cancellationSeen);
2558
+ }
2559
+
2503
2560
  // src/appkit/broadcaster.ts
2504
2561
  var SUBSCRIBER_QUEUE_CAP = 100;
2505
2562
  var SSEBroadcaster = class {
@@ -3470,8 +3527,9 @@ var TURN_END_IDLE = "status.idle";
3470
3527
  var TURN_END_STOPPED = "status.stopped";
3471
3528
  var TurnLifecycleGate = class {
3472
3529
  sawRunning = false;
3473
- sawStreamPayload = false;
3474
3530
  sawTurnProgress = false;
3531
+ expectedTurnId = null;
3532
+ cancellationSeen = false;
3475
3533
  observe(msg) {
3476
3534
  const frame = normalizeFrame(msg);
3477
3535
  if (!frame) return;
@@ -3479,22 +3537,43 @@ var TurnLifecycleGate = class {
3479
3537
  if (typ === "status") {
3480
3538
  if (String(frame.state ?? "").trim().toLowerCase() === "running") {
3481
3539
  this.sawRunning = true;
3540
+ const statusTurn = frameTurnId(frame);
3541
+ if (statusTurn) {
3542
+ const newGen = parseTurnGeneration(statusTurn);
3543
+ const oldGen = parseTurnGeneration(this.expectedTurnId);
3544
+ if (this.expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
3545
+ if (this.expectedTurnId && statusTurn !== this.expectedTurnId) {
3546
+ this.sawTurnProgress = false;
3547
+ }
3548
+ this.expectedTurnId = statusTurn;
3549
+ }
3550
+ }
3482
3551
  }
3483
3552
  return;
3484
3553
  }
3485
3554
  if (typ === "event") {
3486
- this.sawStreamPayload = true;
3487
3555
  const mode = String(frame.mode ?? "");
3488
3556
  if (isTurnProgressChunk(mode, frame.data)) {
3489
3557
  this.sawTurnProgress = true;
3490
3558
  }
3491
3559
  }
3492
3560
  }
3493
- allowStreamEnd() {
3494
- return this.sawRunning && this.sawTurnProgress;
3561
+ allowStreamEnd(frameTurn) {
3562
+ return isTurnTerminalAllowed({
3563
+ expectedTurnId: this.expectedTurnId,
3564
+ frameTurnId: frameTurn,
3565
+ queryStarted: this.sawRunning,
3566
+ turnProgressSeen: this.sawTurnProgress
3567
+ });
3495
3568
  }
3496
- allowIdleComplete() {
3497
- return this.sawRunning && this.sawStreamPayload;
3569
+ allowIdleComplete(frameTurn) {
3570
+ return isIdleTerminalAllowed({
3571
+ expectedTurnId: this.expectedTurnId,
3572
+ frameTurnId: frameTurn,
3573
+ queryStarted: this.sawRunning,
3574
+ turnProgressSeen: this.sawTurnProgress,
3575
+ cancellationSeen: this.cancellationSeen
3576
+ });
3498
3577
  }
3499
3578
  };
3500
3579
  var TurnBoundary = class {
@@ -3509,17 +3588,23 @@ var TurnBoundary = class {
3509
3588
  const typ = String(frame.type ?? "");
3510
3589
  if (typ === "status") {
3511
3590
  const state = String(frame.state ?? "").trim().toLowerCase();
3591
+ const frameTurn = frameTurnId(frame);
3512
3592
  if (state === "stopped" && this.gate.sawRunning) {
3593
+ if (this.gate.expectedTurnId && !turnIdsMatch(this.gate.expectedTurnId, frameTurn)) {
3594
+ return [false, ""];
3595
+ }
3513
3596
  return this.mark(TURN_END_STOPPED);
3514
3597
  }
3515
- if (state === "idle" && this.gate.allowIdleComplete()) {
3598
+ if (state === "idle" && this.gate.allowIdleComplete(frameTurn)) {
3516
3599
  return this.mark(TURN_END_IDLE);
3517
3600
  }
3518
3601
  return [false, ""];
3519
3602
  }
3520
3603
  if (typ === "event") {
3521
3604
  const mode = String(frame.mode ?? "");
3522
- if (mode === "custom" && isTurnEndCustomData(frame.data) && this.gate.allowStreamEnd()) {
3605
+ const data = frame.data;
3606
+ const dataTurn = frameTurnId(data) || frameTurnId(frame);
3607
+ if (mode === "custom" && isTurnEndCustomData(data) && this.gate.allowStreamEnd(dataTurn)) {
3523
3608
  return this.mark(TURN_END_STREAM_END);
3524
3609
  }
3525
3610
  }
@@ -3545,20 +3630,26 @@ function normalizeFrame(msg) {
3545
3630
  return inner;
3546
3631
  }
3547
3632
  if (inner && typeof inner === "object" && inner.mode) {
3548
- return {
3633
+ const out = {
3549
3634
  type: "event",
3550
3635
  mode: inner.mode,
3551
3636
  data: inner.data,
3552
3637
  namespace: inner.namespace ?? payload.namespace
3553
3638
  };
3639
+ const tid = inner.turn_id ?? payload.turn_id ?? m.turn_id;
3640
+ if (tid) out.turn_id = tid;
3641
+ return out;
3554
3642
  }
3555
3643
  if (payload.mode) {
3556
- return {
3644
+ const out = {
3557
3645
  type: "event",
3558
3646
  mode: payload.mode,
3559
3647
  data: payload.data,
3560
3648
  namespace: payload.namespace
3561
3649
  };
3650
+ const tid = payload.turn_id ?? m.turn_id;
3651
+ if (tid) out.turn_id = tid;
3652
+ return out;
3562
3653
  }
3563
3654
  return null;
3564
3655
  }
@@ -4202,25 +4293,6 @@ var DaemonSession = class {
4202
4293
  return this.rpcClient.listLoops(15e3);
4203
4294
  });
4204
4295
  }
4205
- async fetchLoopCards(loopId) {
4206
- const lid = String(loopId || "").trim();
4207
- if (!lid) return { cards: [], seq: 0, contextTokens: 0, success: false };
4208
- return this.withRpcLock(async () => {
4209
- await this.ensureRpcConnected();
4210
- try {
4211
- const resp = await this.rpcClient.fetchLoopCards(lid, 3e4);
4212
- const rawCards = resp.cards;
4213
- return {
4214
- cards: Array.isArray(rawCards) ? rawCards : [],
4215
- seq: Number(resp.seq ?? 0),
4216
- contextTokens: typeof resp.context_tokens === "number" && resp.context_tokens >= 0 ? resp.context_tokens : 0,
4217
- success: true
4218
- };
4219
- } catch {
4220
- return { cards: [], seq: 0, contextTokens: 0, success: false };
4221
- }
4222
- });
4223
- }
4224
4296
  async fetchLoopHistory(loopId) {
4225
4297
  const lid = String(loopId || "").trim();
4226
4298
  if (!lid) {
@@ -4272,7 +4344,7 @@ var DaemonSession = class {
4272
4344
  this.lastTurnErrorMessage = null;
4273
4345
  let queryStarted = false;
4274
4346
  let expectedLoopId = this.loopId;
4275
- let streamPayloadSeen = false;
4347
+ let expectedTurnId = null;
4276
4348
  let turnProgressSeen = false;
4277
4349
  this.streaming = true;
4278
4350
  const absoluteDeadline = opts.maxWaitMs !== void 0 && opts.maxWaitMs > 0 ? Date.now() + opts.maxWaitMs : null;
@@ -4306,6 +4378,17 @@ var DaemonSession = class {
4306
4378
  if (expectedLoopId && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== expectedLoopId) {
4307
4379
  continue;
4308
4380
  }
4381
+ const evTurnId = frameTurnId(frame);
4382
+ const statusState = eventType === "status" ? String(frame.state ?? "") : "";
4383
+ const isRunningStatus = statusState === "running";
4384
+ const isTerminalStatus = statusState === "idle" || statusState === "stopped";
4385
+ if (expectedTurnId && (eventType === "event" || eventType === "status") && !isRunningStatus) {
4386
+ if (isTerminalStatus) {
4387
+ if (evTurnId && !turnIdsMatch(expectedTurnId, evTurnId)) continue;
4388
+ } else if (!turnIdsMatch(expectedTurnId, evTurnId)) {
4389
+ continue;
4390
+ }
4391
+ }
4309
4392
  if (eventType === "error") {
4310
4393
  const errObj = frame.error ?? {};
4311
4394
  throw new Error(String(errObj.message || frame.message || "daemon error"));
@@ -4316,16 +4399,37 @@ var DaemonSession = class {
4316
4399
  this.loopId = loopEv;
4317
4400
  expectedLoopId = loopEv;
4318
4401
  }
4319
- const state = String(frame.state ?? "");
4320
- if (state === "running") {
4402
+ if (statusState === "running") {
4321
4403
  queryStarted = true;
4322
- } else if (queryStarted && state === "stopped") {
4323
- this.lastTurnEndState = state;
4404
+ const statusTurn = frameTurnId(frame);
4405
+ if (statusTurn) {
4406
+ const newGen = parseTurnGeneration(statusTurn);
4407
+ const oldGen = parseTurnGeneration(expectedTurnId);
4408
+ if (expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
4409
+ if (expectedTurnId && statusTurn !== expectedTurnId) {
4410
+ turnProgressSeen = false;
4411
+ }
4412
+ expectedTurnId = statusTurn;
4413
+ }
4414
+ }
4415
+ } else if (queryStarted && statusState === "stopped") {
4416
+ const stopTurn = frameTurnId(frame);
4417
+ if (expectedTurnId && !turnIdsMatch(expectedTurnId, stopTurn)) continue;
4418
+ this.lastTurnEndState = statusState;
4324
4419
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4325
4420
  break;
4326
- } else if (queryStarted && state === "idle") {
4327
- if (!streamPayloadSeen && !this.lastTurnCancellationSeen) continue;
4328
- this.lastTurnEndState = state;
4421
+ } else if (queryStarted && statusState === "idle") {
4422
+ const idleTurn = frameTurnId(frame);
4423
+ if (!isIdleTerminalAllowed({
4424
+ expectedTurnId,
4425
+ frameTurnId: idleTurn,
4426
+ queryStarted,
4427
+ turnProgressSeen,
4428
+ cancellationSeen: this.lastTurnCancellationSeen
4429
+ })) {
4430
+ continue;
4431
+ }
4432
+ this.lastTurnEndState = statusState;
4329
4433
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4330
4434
  break;
4331
4435
  }
@@ -4347,9 +4451,16 @@ var DaemonSession = class {
4347
4451
  continue;
4348
4452
  }
4349
4453
  if (mode === "custom" && isTurnEndCustomData(data)) {
4350
- if (!queryStarted || !turnProgressSeen) continue;
4454
+ const dataTurn = frameTurnId(data) || evTurnId;
4455
+ if (!isTurnTerminalAllowed({
4456
+ expectedTurnId,
4457
+ frameTurnId: dataTurn,
4458
+ queryStarted,
4459
+ turnProgressSeen
4460
+ })) {
4461
+ continue;
4462
+ }
4351
4463
  }
4352
- streamPayloadSeen = true;
4353
4464
  if (isTurnProgressChunk(mode, data)) turnProgressSeen = true;
4354
4465
  yield [namespace, mode, data];
4355
4466
  if (mode === "custom" && isTurnEndCustomData(data)) {
@@ -4547,19 +4658,23 @@ var CardProjection = class {
4547
4658
  extractSootheLoopID,
4548
4659
  extractThinkingStep,
4549
4660
  fetchConfigSection,
4550
- fetchLoopCards,
4551
4661
  fetchLoopHistory,
4552
4662
  fetchLoopMessages,
4553
4663
  fetchSkillsCatalog,
4664
+ formatTurnId,
4665
+ frameSeq,
4666
+ frameTurnId,
4554
4667
  idleTimeoutForTurn,
4555
4668
  inboundNeedsDeliveryAck,
4556
4669
  inputMessageForLoop,
4557
4670
  isCompletionEvent,
4558
4671
  isDaemonLive,
4559
4672
  isDaemonTurnEndEvent,
4673
+ isIdleTerminalAllowed,
4560
4674
  isSubagentProgressEvent,
4561
4675
  isTurnEndCustomData,
4562
4676
  isTurnProgressChunk,
4677
+ isTurnTerminalAllowed,
4563
4678
  isValidVerbosityLevel,
4564
4679
  loadConfigFromEnv,
4565
4680
  newLoopInputMessage,
@@ -4569,6 +4684,7 @@ var CardProjection = class {
4569
4684
  notificationEnvelope,
4570
4685
  parseCardCustomPayload,
4571
4686
  parseNamespace,
4687
+ parseTurnGeneration,
4572
4688
  pingEnvelope,
4573
4689
  pongEnvelope,
4574
4690
  protocol1Rpc,
@@ -4579,6 +4695,7 @@ var CardProjection = class {
4579
4695
  shouldShow,
4580
4696
  splitWirePayload,
4581
4697
  subscribeEnvelope,
4698
+ turnIdsMatch,
4582
4699
  unsubscribeEnvelope,
4583
4700
  validateLoopInputIntentHint,
4584
4701
  waitDaemonReady,