@mirasoth/soothe-client 0.5.5 → 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
@@ -1914,6 +1914,16 @@ var init_client = __esm({
1914
1914
  timeout ?? 15e3
1915
1915
  );
1916
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
+ }
1917
1927
  /** Subscribes to autopilot worker events. */
1918
1928
  autopilotSubscribe(timeout) {
1919
1929
  return this.subscribe("autopilot_events", {}, timeout ?? 15e3);
@@ -2087,15 +2097,20 @@ __export(index_exports, {
2087
2097
  fetchLoopHistory: () => fetchLoopHistory,
2088
2098
  fetchLoopMessages: () => fetchLoopMessages,
2089
2099
  fetchSkillsCatalog: () => fetchSkillsCatalog,
2100
+ formatTurnId: () => formatTurnId,
2101
+ frameSeq: () => frameSeq,
2102
+ frameTurnId: () => frameTurnId,
2090
2103
  idleTimeoutForTurn: () => idleTimeoutForTurn,
2091
2104
  inboundNeedsDeliveryAck: () => inboundNeedsDeliveryAck,
2092
2105
  inputMessageForLoop: () => inputMessageForLoop,
2093
2106
  isCompletionEvent: () => isCompletionEvent,
2094
2107
  isDaemonLive: () => isDaemonLive,
2095
2108
  isDaemonTurnEndEvent: () => isDaemonTurnEndEvent,
2109
+ isIdleTerminalAllowed: () => isIdleTerminalAllowed,
2096
2110
  isSubagentProgressEvent: () => isSubagentProgressEvent,
2097
2111
  isTurnEndCustomData: () => isTurnEndCustomData,
2098
2112
  isTurnProgressChunk: () => isTurnProgressChunk,
2113
+ isTurnTerminalAllowed: () => isTurnTerminalAllowed,
2099
2114
  isValidVerbosityLevel: () => isValidVerbosityLevel,
2100
2115
  loadConfigFromEnv: () => loadConfigFromEnv,
2101
2116
  newLoopInputMessage: () => newLoopInputMessage,
@@ -2105,6 +2120,7 @@ __export(index_exports, {
2105
2120
  notificationEnvelope: () => notificationEnvelope,
2106
2121
  parseCardCustomPayload: () => parseCardCustomPayload,
2107
2122
  parseNamespace: () => parseNamespace,
2123
+ parseTurnGeneration: () => parseTurnGeneration,
2108
2124
  pingEnvelope: () => pingEnvelope,
2109
2125
  pongEnvelope: () => pongEnvelope,
2110
2126
  protocol1Rpc: () => protocol1Rpc,
@@ -2115,6 +2131,7 @@ __export(index_exports, {
2115
2131
  shouldShow: () => shouldShow,
2116
2132
  splitWirePayload: () => splitWirePayload,
2117
2133
  subscribeEnvelope: () => subscribeEnvelope,
2134
+ turnIdsMatch: () => turnIdsMatch,
2118
2135
  unsubscribeEnvelope: () => unsubscribeEnvelope,
2119
2136
  validateLoopInputIntentHint: () => validateLoopInputIntentHint,
2120
2137
  waitDaemonReady: () => waitDaemonReady,
@@ -2321,6 +2338,10 @@ var CommandClient = class {
2321
2338
  async autopilotGetJob(jobId) {
2322
2339
  return this.request("autopilot_get_job", { job_id: jobId });
2323
2340
  }
2341
+ /** Jobs → goals → loops snapshot for CLI top. */
2342
+ async autopilotTop(includeTerminal = false) {
2343
+ return this.request("autopilot_top", { include_terminal: includeTerminal });
2344
+ }
2324
2345
  async cronAdd(text, priority = 0) {
2325
2346
  const params = { text };
2326
2347
  if (priority > 0) params.priority = priority;
@@ -2483,6 +2504,59 @@ async function protocol1Rpc(wsUrl, method, params = null, opts = {}) {
2483
2504
  // src/index.ts
2484
2505
  init_stream_terminal();
2485
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
+
2486
2560
  // src/appkit/broadcaster.ts
2487
2561
  var SUBSCRIBER_QUEUE_CAP = 100;
2488
2562
  var SSEBroadcaster = class {
@@ -3453,8 +3527,9 @@ var TURN_END_IDLE = "status.idle";
3453
3527
  var TURN_END_STOPPED = "status.stopped";
3454
3528
  var TurnLifecycleGate = class {
3455
3529
  sawRunning = false;
3456
- sawStreamPayload = false;
3457
3530
  sawTurnProgress = false;
3531
+ expectedTurnId = null;
3532
+ cancellationSeen = false;
3458
3533
  observe(msg) {
3459
3534
  const frame = normalizeFrame(msg);
3460
3535
  if (!frame) return;
@@ -3462,22 +3537,43 @@ var TurnLifecycleGate = class {
3462
3537
  if (typ === "status") {
3463
3538
  if (String(frame.state ?? "").trim().toLowerCase() === "running") {
3464
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
+ }
3465
3551
  }
3466
3552
  return;
3467
3553
  }
3468
3554
  if (typ === "event") {
3469
- this.sawStreamPayload = true;
3470
3555
  const mode = String(frame.mode ?? "");
3471
3556
  if (isTurnProgressChunk(mode, frame.data)) {
3472
3557
  this.sawTurnProgress = true;
3473
3558
  }
3474
3559
  }
3475
3560
  }
3476
- allowStreamEnd() {
3477
- 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
+ });
3478
3568
  }
3479
- allowIdleComplete() {
3480
- 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
+ });
3481
3577
  }
3482
3578
  };
3483
3579
  var TurnBoundary = class {
@@ -3492,17 +3588,23 @@ var TurnBoundary = class {
3492
3588
  const typ = String(frame.type ?? "");
3493
3589
  if (typ === "status") {
3494
3590
  const state = String(frame.state ?? "").trim().toLowerCase();
3591
+ const frameTurn = frameTurnId(frame);
3495
3592
  if (state === "stopped" && this.gate.sawRunning) {
3593
+ if (this.gate.expectedTurnId && !turnIdsMatch(this.gate.expectedTurnId, frameTurn)) {
3594
+ return [false, ""];
3595
+ }
3496
3596
  return this.mark(TURN_END_STOPPED);
3497
3597
  }
3498
- if (state === "idle" && this.gate.allowIdleComplete()) {
3598
+ if (state === "idle" && this.gate.allowIdleComplete(frameTurn)) {
3499
3599
  return this.mark(TURN_END_IDLE);
3500
3600
  }
3501
3601
  return [false, ""];
3502
3602
  }
3503
3603
  if (typ === "event") {
3504
3604
  const mode = String(frame.mode ?? "");
3505
- 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)) {
3506
3608
  return this.mark(TURN_END_STREAM_END);
3507
3609
  }
3508
3610
  }
@@ -3528,20 +3630,26 @@ function normalizeFrame(msg) {
3528
3630
  return inner;
3529
3631
  }
3530
3632
  if (inner && typeof inner === "object" && inner.mode) {
3531
- return {
3633
+ const out = {
3532
3634
  type: "event",
3533
3635
  mode: inner.mode,
3534
3636
  data: inner.data,
3535
3637
  namespace: inner.namespace ?? payload.namespace
3536
3638
  };
3639
+ const tid = inner.turn_id ?? payload.turn_id ?? m.turn_id;
3640
+ if (tid) out.turn_id = tid;
3641
+ return out;
3537
3642
  }
3538
3643
  if (payload.mode) {
3539
- return {
3644
+ const out = {
3540
3645
  type: "event",
3541
3646
  mode: payload.mode,
3542
3647
  data: payload.data,
3543
3648
  namespace: payload.namespace
3544
3649
  };
3650
+ const tid = payload.turn_id ?? m.turn_id;
3651
+ if (tid) out.turn_id = tid;
3652
+ return out;
3545
3653
  }
3546
3654
  return null;
3547
3655
  }
@@ -4236,7 +4344,7 @@ var DaemonSession = class {
4236
4344
  this.lastTurnErrorMessage = null;
4237
4345
  let queryStarted = false;
4238
4346
  let expectedLoopId = this.loopId;
4239
- let streamPayloadSeen = false;
4347
+ let expectedTurnId = null;
4240
4348
  let turnProgressSeen = false;
4241
4349
  this.streaming = true;
4242
4350
  const absoluteDeadline = opts.maxWaitMs !== void 0 && opts.maxWaitMs > 0 ? Date.now() + opts.maxWaitMs : null;
@@ -4270,6 +4378,17 @@ var DaemonSession = class {
4270
4378
  if (expectedLoopId && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== expectedLoopId) {
4271
4379
  continue;
4272
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
+ }
4273
4392
  if (eventType === "error") {
4274
4393
  const errObj = frame.error ?? {};
4275
4394
  throw new Error(String(errObj.message || frame.message || "daemon error"));
@@ -4280,16 +4399,37 @@ var DaemonSession = class {
4280
4399
  this.loopId = loopEv;
4281
4400
  expectedLoopId = loopEv;
4282
4401
  }
4283
- const state = String(frame.state ?? "");
4284
- if (state === "running") {
4402
+ if (statusState === "running") {
4285
4403
  queryStarted = true;
4286
- } else if (queryStarted && state === "stopped") {
4287
- 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;
4288
4419
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4289
4420
  break;
4290
- } else if (queryStarted && state === "idle") {
4291
- if (!streamPayloadSeen && !this.lastTurnCancellationSeen) continue;
4292
- 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;
4293
4433
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4294
4434
  break;
4295
4435
  }
@@ -4311,9 +4451,16 @@ var DaemonSession = class {
4311
4451
  continue;
4312
4452
  }
4313
4453
  if (mode === "custom" && isTurnEndCustomData(data)) {
4314
- 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
+ }
4315
4463
  }
4316
- streamPayloadSeen = true;
4317
4464
  if (isTurnProgressChunk(mode, data)) turnProgressSeen = true;
4318
4465
  yield [namespace, mode, data];
4319
4466
  if (mode === "custom" && isTurnEndCustomData(data)) {
@@ -4514,15 +4661,20 @@ var CardProjection = class {
4514
4661
  fetchLoopHistory,
4515
4662
  fetchLoopMessages,
4516
4663
  fetchSkillsCatalog,
4664
+ formatTurnId,
4665
+ frameSeq,
4666
+ frameTurnId,
4517
4667
  idleTimeoutForTurn,
4518
4668
  inboundNeedsDeliveryAck,
4519
4669
  inputMessageForLoop,
4520
4670
  isCompletionEvent,
4521
4671
  isDaemonLive,
4522
4672
  isDaemonTurnEndEvent,
4673
+ isIdleTerminalAllowed,
4523
4674
  isSubagentProgressEvent,
4524
4675
  isTurnEndCustomData,
4525
4676
  isTurnProgressChunk,
4677
+ isTurnTerminalAllowed,
4526
4678
  isValidVerbosityLevel,
4527
4679
  loadConfigFromEnv,
4528
4680
  newLoopInputMessage,
@@ -4532,6 +4684,7 @@ var CardProjection = class {
4532
4684
  notificationEnvelope,
4533
4685
  parseCardCustomPayload,
4534
4686
  parseNamespace,
4687
+ parseTurnGeneration,
4535
4688
  pingEnvelope,
4536
4689
  pongEnvelope,
4537
4690
  protocol1Rpc,
@@ -4542,6 +4695,7 @@ var CardProjection = class {
4542
4695
  shouldShow,
4543
4696
  splitWirePayload,
4544
4697
  subscribeEnvelope,
4698
+ turnIdsMatch,
4545
4699
  unsubscribeEnvelope,
4546
4700
  validateLoopInputIntentHint,
4547
4701
  waitDaemonReady,