@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.cjs CHANGED
@@ -321,8 +321,7 @@ function newRequestID() {
321
321
  function newLoopInputMessage(loopID, content) {
322
322
  return notificationEnvelope("loop_input", {
323
323
  loop_id: loopID,
324
- content,
325
- autonomous: false
324
+ content
326
325
  });
327
326
  }
328
327
  function newLoopNewMessage(opts) {
@@ -361,51 +360,6 @@ var init_protocol = __esm({
361
360
  }
362
361
  });
363
362
 
364
- // src/intent_hints.ts
365
- function validateLoopInputIntentHint(hint) {
366
- const key = hint.trim().toLowerCase();
367
- if (key in REMOVED_INTENT_HINT_MESSAGES) {
368
- return REMOVED_INTENT_HINT_MESSAGES[key];
369
- }
370
- return null;
371
- }
372
- var INTENT_HINT_TEXT_COMPLETION, INTENT_HINT_IMAGE_TO_TEXT, INTENT_HINT_OCR, INTENT_HINT_EMBED, REMOVED_INTENT_HINTS, REMOVED_INTENT_HINT_MESSAGES, LOOP_ASSISTANT_OUTPUT_PHASES, DEFAULT_DELIVERABLE_PHASES;
373
- var init_intent_hints = __esm({
374
- "src/intent_hints.ts"() {
375
- "use strict";
376
- INTENT_HINT_TEXT_COMPLETION = "text_completion";
377
- INTENT_HINT_IMAGE_TO_TEXT = "image_to_text";
378
- INTENT_HINT_OCR = "ocr";
379
- INTENT_HINT_EMBED = "embed";
380
- REMOVED_INTENT_HINTS = ["direct_llm", "quiz", "direct_model"];
381
- REMOVED_INTENT_HINT_MESSAGES = {
382
- direct_llm: "intent_hint direct_llm is removed; use text_completion (text-only) or image_to_text (with attachments)",
383
- quiz: "intent_hint quiz is removed; omit intent_hint and let intake classify the turn",
384
- direct_model: "intent_hint direct_model is removed; use text_completion, image_to_text, ocr, or embed"
385
- };
386
- LOOP_ASSISTANT_OUTPUT_PHASES = [
387
- "goal_completion",
388
- "quiz",
389
- "autonomous_goal",
390
- "chitchat",
391
- "text_completion",
392
- "image_to_text",
393
- "ocr",
394
- "embed",
395
- "plan_direct"
396
- ];
397
- DEFAULT_DELIVERABLE_PHASES = /* @__PURE__ */ new Set([
398
- "quiz",
399
- "goal_completion",
400
- "chitchat",
401
- "text_completion",
402
- "image_to_text",
403
- "ocr",
404
- "embed"
405
- ]);
406
- }
407
- });
408
-
409
363
  // src/events.ts
410
364
  function parseNamespace(ns) {
411
365
  const parts = splitNamespace(ns);
@@ -887,7 +841,6 @@ var init_client = __esm({
887
841
  init_config();
888
842
  init_errors();
889
843
  init_multiplexer();
890
- init_intent_hints();
891
844
  init_stream_terminal();
892
845
  init_inbound_priority();
893
846
  init_protocol();
@@ -1581,19 +1534,14 @@ var init_client = __esm({
1581
1534
  }
1582
1535
  const params = {
1583
1536
  loop_id: loopId,
1584
- content: text,
1585
- autonomous: options?.autonomous ?? false
1537
+ content: text
1586
1538
  };
1587
- if (options?.maxIterations !== void 0) params.max_iterations = options.maxIterations;
1588
1539
  if (options?.subagent) params.preferred_subagent = options.subagent;
1540
+ if (options?.intakeScope) params.intake_scope = options.intakeScope;
1589
1541
  if (options?.model) params.model = options.model;
1590
1542
  if (options?.modelParams) params.model_params = options.modelParams;
1591
1543
  if (options?.attachments) params.attachments = options.attachments;
1592
1544
  if (options?.intentHint) {
1593
- const hintError = validateLoopInputIntentHint(options.intentHint);
1594
- if (hintError) {
1595
- return Promise.reject(new Error(hintError));
1596
- }
1597
1545
  params.intent_hint = options.intentHint;
1598
1546
  }
1599
1547
  if (options?.responseSchema) params.response_schema = options.responseSchema;
@@ -1914,6 +1862,16 @@ var init_client = __esm({
1914
1862
  timeout ?? 15e3
1915
1863
  );
1916
1864
  }
1865
+ /** Jobs → goals → loops snapshot for CLI top. */
1866
+ autopilotTop(timeoutOrOptions) {
1867
+ const options = typeof timeoutOrOptions === "number" ? { timeout: timeoutOrOptions } : timeoutOrOptions;
1868
+ return this.requestResponse(
1869
+ "autopilot_top",
1870
+ { include_terminal: options?.includeTerminal ?? false },
1871
+ "autopilot_top",
1872
+ options?.timeout ?? 15e3
1873
+ );
1874
+ }
1917
1875
  /** Subscribes to autopilot worker events. */
1918
1876
  autopilotSubscribe(timeout) {
1919
1877
  return this.subscribe("autopilot_events", {}, timeout ?? 15e3);
@@ -2049,7 +2007,6 @@ __export(index_exports, {
2049
2007
  PROTO_VERSION: () => PROTO_VERSION,
2050
2008
  PooledConn: () => PooledConn,
2051
2009
  QueryGate: () => QueryGate,
2052
- REMOVED_INTENT_HINTS: () => REMOVED_INTENT_HINTS,
2053
2010
  ReconnectError: () => ReconnectError,
2054
2011
  SSEBroadcaster: () => SSEBroadcaster,
2055
2012
  STREAM_END: () => STREAM_END,
@@ -2087,15 +2044,20 @@ __export(index_exports, {
2087
2044
  fetchLoopHistory: () => fetchLoopHistory,
2088
2045
  fetchLoopMessages: () => fetchLoopMessages,
2089
2046
  fetchSkillsCatalog: () => fetchSkillsCatalog,
2047
+ formatTurnId: () => formatTurnId,
2048
+ frameSeq: () => frameSeq,
2049
+ frameTurnId: () => frameTurnId,
2090
2050
  idleTimeoutForTurn: () => idleTimeoutForTurn,
2091
2051
  inboundNeedsDeliveryAck: () => inboundNeedsDeliveryAck,
2092
2052
  inputMessageForLoop: () => inputMessageForLoop,
2093
2053
  isCompletionEvent: () => isCompletionEvent,
2094
2054
  isDaemonLive: () => isDaemonLive,
2095
2055
  isDaemonTurnEndEvent: () => isDaemonTurnEndEvent,
2056
+ isIdleTerminalAllowed: () => isIdleTerminalAllowed,
2096
2057
  isSubagentProgressEvent: () => isSubagentProgressEvent,
2097
2058
  isTurnEndCustomData: () => isTurnEndCustomData,
2098
2059
  isTurnProgressChunk: () => isTurnProgressChunk,
2060
+ isTurnTerminalAllowed: () => isTurnTerminalAllowed,
2099
2061
  isValidVerbosityLevel: () => isValidVerbosityLevel,
2100
2062
  loadConfigFromEnv: () => loadConfigFromEnv,
2101
2063
  newLoopInputMessage: () => newLoopInputMessage,
@@ -2105,6 +2067,7 @@ __export(index_exports, {
2105
2067
  notificationEnvelope: () => notificationEnvelope,
2106
2068
  parseCardCustomPayload: () => parseCardCustomPayload,
2107
2069
  parseNamespace: () => parseNamespace,
2070
+ parseTurnGeneration: () => parseTurnGeneration,
2108
2071
  pingEnvelope: () => pingEnvelope,
2109
2072
  pongEnvelope: () => pongEnvelope,
2110
2073
  protocol1Rpc: () => protocol1Rpc,
@@ -2115,8 +2078,8 @@ __export(index_exports, {
2115
2078
  shouldShow: () => shouldShow,
2116
2079
  splitWirePayload: () => splitWirePayload,
2117
2080
  subscribeEnvelope: () => subscribeEnvelope,
2081
+ turnIdsMatch: () => turnIdsMatch,
2118
2082
  unsubscribeEnvelope: () => unsubscribeEnvelope,
2119
- validateLoopInputIntentHint: () => validateLoopInputIntentHint,
2120
2083
  waitDaemonReady: () => waitDaemonReady,
2121
2084
  waitLoopStatusWithID: () => waitLoopStatusWithID,
2122
2085
  waitSubscriptionConfirmed: () => waitSubscriptionConfirmed
@@ -2126,7 +2089,34 @@ init_errors();
2126
2089
  init_verbosity();
2127
2090
  init_config();
2128
2091
  init_protocol();
2129
- init_intent_hints();
2092
+
2093
+ // src/intent_hints.ts
2094
+ var INTENT_HINT_TEXT_COMPLETION = "text_completion";
2095
+ var INTENT_HINT_IMAGE_TO_TEXT = "image_to_text";
2096
+ var INTENT_HINT_OCR = "ocr";
2097
+ var INTENT_HINT_EMBED = "embed";
2098
+ var LOOP_ASSISTANT_OUTPUT_PHASES = [
2099
+ "goal_completion",
2100
+ "quiz",
2101
+ "autonomous_goal",
2102
+ "chitchat",
2103
+ "text_completion",
2104
+ "image_to_text",
2105
+ "ocr",
2106
+ "embed",
2107
+ "plan_direct"
2108
+ ];
2109
+ var DEFAULT_DELIVERABLE_PHASES = /* @__PURE__ */ new Set([
2110
+ "quiz",
2111
+ "goal_completion",
2112
+ "chitchat",
2113
+ "text_completion",
2114
+ "image_to_text",
2115
+ "ocr",
2116
+ "embed"
2117
+ ]);
2118
+
2119
+ // src/index.ts
2130
2120
  init_events();
2131
2121
  init_client();
2132
2122
 
@@ -2321,6 +2311,10 @@ var CommandClient = class {
2321
2311
  async autopilotGetJob(jobId) {
2322
2312
  return this.request("autopilot_get_job", { job_id: jobId });
2323
2313
  }
2314
+ /** Jobs → goals → loops snapshot for CLI top. */
2315
+ async autopilotTop(includeTerminal = false) {
2316
+ return this.request("autopilot_top", { include_terminal: includeTerminal });
2317
+ }
2324
2318
  async cronAdd(text, priority = 0) {
2325
2319
  const params = { text };
2326
2320
  if (priority > 0) params.priority = priority;
@@ -2483,6 +2477,59 @@ async function protocol1Rpc(wsUrl, method, params = null, opts = {}) {
2483
2477
  // src/index.ts
2484
2478
  init_stream_terminal();
2485
2479
 
2480
+ // src/turn_boundary.ts
2481
+ function formatTurnId(loopId, generation) {
2482
+ const lid = String(loopId || "").trim();
2483
+ const gen = Number(generation);
2484
+ if (!lid || !Number.isFinite(gen) || gen <= 0) return "";
2485
+ return `${lid}:${Math.trunc(gen)}`;
2486
+ }
2487
+ function parseTurnGeneration(turnId) {
2488
+ const raw = String(turnId || "").trim();
2489
+ if (!raw || !raw.includes(":")) return null;
2490
+ const suffix = raw.split(":").pop() ?? "";
2491
+ const gen = Number.parseInt(suffix, 10);
2492
+ return Number.isFinite(gen) && gen > 0 ? gen : null;
2493
+ }
2494
+ function frameTurnId(frame) {
2495
+ if (!frame || typeof frame !== "object") return null;
2496
+ const tid = frame.turn_id;
2497
+ if (typeof tid === "string" && tid.trim()) return tid.trim();
2498
+ const data = frame.data;
2499
+ if (data && typeof data === "object") {
2500
+ const inner = data.turn_id;
2501
+ if (typeof inner === "string" && inner.trim()) return inner.trim();
2502
+ }
2503
+ return null;
2504
+ }
2505
+ function frameSeq(frame) {
2506
+ if (!frame || typeof frame !== "object") return null;
2507
+ const raw = frame.seq;
2508
+ if (typeof raw === "boolean") return null;
2509
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0 && Number.isInteger(raw)) {
2510
+ return raw;
2511
+ }
2512
+ return null;
2513
+ }
2514
+ function turnIdsMatch(expected, candidate) {
2515
+ const exp = String(expected || "").trim();
2516
+ const cand = String(candidate || "").trim();
2517
+ return Boolean(exp) && Boolean(cand) && exp === cand;
2518
+ }
2519
+ function isTurnTerminalAllowed(opts) {
2520
+ if (!opts.queryStarted || !opts.turnProgressSeen) return false;
2521
+ return turnIdsMatch(opts.expectedTurnId, opts.frameTurnId);
2522
+ }
2523
+ function isIdleTerminalAllowed(opts) {
2524
+ if (!opts.queryStarted || !String(opts.expectedTurnId || "").trim()) return false;
2525
+ const cand = String(opts.frameTurnId || "").trim();
2526
+ if (cand) {
2527
+ if (!turnIdsMatch(opts.expectedTurnId, cand)) return false;
2528
+ return Boolean(opts.turnProgressSeen || opts.cancellationSeen);
2529
+ }
2530
+ return Boolean(opts.cancellationSeen);
2531
+ }
2532
+
2486
2533
  // src/appkit/broadcaster.ts
2487
2534
  var SUBSCRIBER_QUEUE_CAP = 100;
2488
2535
  var SSEBroadcaster = class {
@@ -3443,9 +3490,6 @@ async function compactAttachments(atts, opts) {
3443
3490
  return out;
3444
3491
  }
3445
3492
 
3446
- // src/appkit/turn_runner.ts
3447
- init_intent_hints();
3448
-
3449
3493
  // src/appkit/turn_boundary.ts
3450
3494
  init_stream_terminal();
3451
3495
  var TURN_END_STREAM_END = STREAM_END;
@@ -3453,8 +3497,9 @@ var TURN_END_IDLE = "status.idle";
3453
3497
  var TURN_END_STOPPED = "status.stopped";
3454
3498
  var TurnLifecycleGate = class {
3455
3499
  sawRunning = false;
3456
- sawStreamPayload = false;
3457
3500
  sawTurnProgress = false;
3501
+ expectedTurnId = null;
3502
+ cancellationSeen = false;
3458
3503
  observe(msg) {
3459
3504
  const frame = normalizeFrame(msg);
3460
3505
  if (!frame) return;
@@ -3462,22 +3507,43 @@ var TurnLifecycleGate = class {
3462
3507
  if (typ === "status") {
3463
3508
  if (String(frame.state ?? "").trim().toLowerCase() === "running") {
3464
3509
  this.sawRunning = true;
3510
+ const statusTurn = frameTurnId(frame);
3511
+ if (statusTurn) {
3512
+ const newGen = parseTurnGeneration(statusTurn);
3513
+ const oldGen = parseTurnGeneration(this.expectedTurnId);
3514
+ if (this.expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
3515
+ if (this.expectedTurnId && statusTurn !== this.expectedTurnId) {
3516
+ this.sawTurnProgress = false;
3517
+ }
3518
+ this.expectedTurnId = statusTurn;
3519
+ }
3520
+ }
3465
3521
  }
3466
3522
  return;
3467
3523
  }
3468
3524
  if (typ === "event") {
3469
- this.sawStreamPayload = true;
3470
3525
  const mode = String(frame.mode ?? "");
3471
3526
  if (isTurnProgressChunk(mode, frame.data)) {
3472
3527
  this.sawTurnProgress = true;
3473
3528
  }
3474
3529
  }
3475
3530
  }
3476
- allowStreamEnd() {
3477
- return this.sawRunning && this.sawTurnProgress;
3531
+ allowStreamEnd(frameTurn) {
3532
+ return isTurnTerminalAllowed({
3533
+ expectedTurnId: this.expectedTurnId,
3534
+ frameTurnId: frameTurn,
3535
+ queryStarted: this.sawRunning,
3536
+ turnProgressSeen: this.sawTurnProgress
3537
+ });
3478
3538
  }
3479
- allowIdleComplete() {
3480
- return this.sawRunning && this.sawStreamPayload;
3539
+ allowIdleComplete(frameTurn) {
3540
+ return isIdleTerminalAllowed({
3541
+ expectedTurnId: this.expectedTurnId,
3542
+ frameTurnId: frameTurn,
3543
+ queryStarted: this.sawRunning,
3544
+ turnProgressSeen: this.sawTurnProgress,
3545
+ cancellationSeen: this.cancellationSeen
3546
+ });
3481
3547
  }
3482
3548
  };
3483
3549
  var TurnBoundary = class {
@@ -3492,17 +3558,23 @@ var TurnBoundary = class {
3492
3558
  const typ = String(frame.type ?? "");
3493
3559
  if (typ === "status") {
3494
3560
  const state = String(frame.state ?? "").trim().toLowerCase();
3561
+ const frameTurn = frameTurnId(frame);
3495
3562
  if (state === "stopped" && this.gate.sawRunning) {
3563
+ if (this.gate.expectedTurnId && !turnIdsMatch(this.gate.expectedTurnId, frameTurn)) {
3564
+ return [false, ""];
3565
+ }
3496
3566
  return this.mark(TURN_END_STOPPED);
3497
3567
  }
3498
- if (state === "idle" && this.gate.allowIdleComplete()) {
3568
+ if (state === "idle" && this.gate.allowIdleComplete(frameTurn)) {
3499
3569
  return this.mark(TURN_END_IDLE);
3500
3570
  }
3501
3571
  return [false, ""];
3502
3572
  }
3503
3573
  if (typ === "event") {
3504
3574
  const mode = String(frame.mode ?? "");
3505
- if (mode === "custom" && isTurnEndCustomData(frame.data) && this.gate.allowStreamEnd()) {
3575
+ const data = frame.data;
3576
+ const dataTurn = frameTurnId(data) || frameTurnId(frame);
3577
+ if (mode === "custom" && isTurnEndCustomData(data) && this.gate.allowStreamEnd(dataTurn)) {
3506
3578
  return this.mark(TURN_END_STREAM_END);
3507
3579
  }
3508
3580
  }
@@ -3528,20 +3600,26 @@ function normalizeFrame(msg) {
3528
3600
  return inner;
3529
3601
  }
3530
3602
  if (inner && typeof inner === "object" && inner.mode) {
3531
- return {
3603
+ const out = {
3532
3604
  type: "event",
3533
3605
  mode: inner.mode,
3534
3606
  data: inner.data,
3535
3607
  namespace: inner.namespace ?? payload.namespace
3536
3608
  };
3609
+ const tid = inner.turn_id ?? payload.turn_id ?? m.turn_id;
3610
+ if (tid) out.turn_id = tid;
3611
+ return out;
3537
3612
  }
3538
3613
  if (payload.mode) {
3539
- return {
3614
+ const out = {
3540
3615
  type: "event",
3541
3616
  mode: payload.mode,
3542
3617
  data: payload.data,
3543
3618
  namespace: payload.namespace
3544
3619
  };
3620
+ const tid = payload.turn_id ?? m.turn_id;
3621
+ if (tid) out.turn_id = tid;
3622
+ return out;
3545
3623
  }
3546
3624
  return null;
3547
3625
  }
@@ -3574,13 +3652,10 @@ function inputMessageForLoop(text, loopID, attachments, opts) {
3574
3652
  if (attachments && attachments.length > 0) msg.attachments = attachments;
3575
3653
  if (opts) {
3576
3654
  if (opts.intentHint?.trim()) {
3577
- const hintError = validateLoopInputIntentHint(opts.intentHint);
3578
- if (hintError) {
3579
- throw new Error(hintError);
3580
- }
3581
3655
  msg.intent_hint = opts.intentHint.trim();
3582
3656
  }
3583
3657
  if (opts.preferredSubagent?.trim()) msg.preferred_subagent = opts.preferredSubagent.trim();
3658
+ if (opts.intakeScope?.trim()) msg.intake_scope = opts.intakeScope.trim();
3584
3659
  if (opts.responseSchema && Object.keys(opts.responseSchema).length > 0) {
3585
3660
  msg.response_schema = opts.responseSchema;
3586
3661
  }
@@ -4109,8 +4184,6 @@ var DaemonSession = class {
4109
4184
  if (!this.loopId) throw new Error("No active loop session");
4110
4185
  await this.client.sendInput(text, {
4111
4186
  loopID: this.loopId,
4112
- autonomous: options?.autonomous,
4113
- maxIterations: options?.maxIterations,
4114
4187
  subagent: options?.preferredSubagent,
4115
4188
  model: options?.model,
4116
4189
  modelParams: options?.modelParams,
@@ -4236,7 +4309,7 @@ var DaemonSession = class {
4236
4309
  this.lastTurnErrorMessage = null;
4237
4310
  let queryStarted = false;
4238
4311
  let expectedLoopId = this.loopId;
4239
- let streamPayloadSeen = false;
4312
+ let expectedTurnId = null;
4240
4313
  let turnProgressSeen = false;
4241
4314
  this.streaming = true;
4242
4315
  const absoluteDeadline = opts.maxWaitMs !== void 0 && opts.maxWaitMs > 0 ? Date.now() + opts.maxWaitMs : null;
@@ -4270,6 +4343,17 @@ var DaemonSession = class {
4270
4343
  if (expectedLoopId && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== expectedLoopId) {
4271
4344
  continue;
4272
4345
  }
4346
+ const evTurnId = frameTurnId(frame);
4347
+ const statusState = eventType === "status" ? String(frame.state ?? "") : "";
4348
+ const isRunningStatus = statusState === "running";
4349
+ const isTerminalStatus = statusState === "idle" || statusState === "stopped";
4350
+ if (expectedTurnId && (eventType === "event" || eventType === "status") && !isRunningStatus) {
4351
+ if (isTerminalStatus) {
4352
+ if (evTurnId && !turnIdsMatch(expectedTurnId, evTurnId)) continue;
4353
+ } else if (!turnIdsMatch(expectedTurnId, evTurnId)) {
4354
+ continue;
4355
+ }
4356
+ }
4273
4357
  if (eventType === "error") {
4274
4358
  const errObj = frame.error ?? {};
4275
4359
  throw new Error(String(errObj.message || frame.message || "daemon error"));
@@ -4280,16 +4364,37 @@ var DaemonSession = class {
4280
4364
  this.loopId = loopEv;
4281
4365
  expectedLoopId = loopEv;
4282
4366
  }
4283
- const state = String(frame.state ?? "");
4284
- if (state === "running") {
4367
+ if (statusState === "running") {
4285
4368
  queryStarted = true;
4286
- } else if (queryStarted && state === "stopped") {
4287
- this.lastTurnEndState = state;
4369
+ const statusTurn = frameTurnId(frame);
4370
+ if (statusTurn) {
4371
+ const newGen = parseTurnGeneration(statusTurn);
4372
+ const oldGen = parseTurnGeneration(expectedTurnId);
4373
+ if (expectedTurnId === null || newGen !== null && (oldGen === null || newGen >= oldGen)) {
4374
+ if (expectedTurnId && statusTurn !== expectedTurnId) {
4375
+ turnProgressSeen = false;
4376
+ }
4377
+ expectedTurnId = statusTurn;
4378
+ }
4379
+ }
4380
+ } else if (queryStarted && statusState === "stopped") {
4381
+ const stopTurn = frameTurnId(frame);
4382
+ if (expectedTurnId && !turnIdsMatch(expectedTurnId, stopTurn)) continue;
4383
+ this.lastTurnEndState = statusState;
4288
4384
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4289
4385
  break;
4290
- } else if (queryStarted && state === "idle") {
4291
- if (!streamPayloadSeen && !this.lastTurnCancellationSeen) continue;
4292
- this.lastTurnEndState = state;
4386
+ } else if (queryStarted && statusState === "idle") {
4387
+ const idleTurn = frameTurnId(frame);
4388
+ if (!isIdleTerminalAllowed({
4389
+ expectedTurnId,
4390
+ frameTurnId: idleTurn,
4391
+ queryStarted,
4392
+ turnProgressSeen,
4393
+ cancellationSeen: this.lastTurnCancellationSeen
4394
+ })) {
4395
+ continue;
4396
+ }
4397
+ this.lastTurnEndState = statusState;
4293
4398
  yield* this.drainStreamEventsAfterIdle(expectedLoopId);
4294
4399
  break;
4295
4400
  }
@@ -4311,9 +4416,16 @@ var DaemonSession = class {
4311
4416
  continue;
4312
4417
  }
4313
4418
  if (mode === "custom" && isTurnEndCustomData(data)) {
4314
- if (!queryStarted || !turnProgressSeen) continue;
4419
+ const dataTurn = frameTurnId(data) || evTurnId;
4420
+ if (!isTurnTerminalAllowed({
4421
+ expectedTurnId,
4422
+ frameTurnId: dataTurn,
4423
+ queryStarted,
4424
+ turnProgressSeen
4425
+ })) {
4426
+ continue;
4427
+ }
4315
4428
  }
4316
- streamPayloadSeen = true;
4317
4429
  if (isTurnProgressChunk(mode, data)) turnProgressSeen = true;
4318
4430
  yield [namespace, mode, data];
4319
4431
  if (mode === "custom" && isTurnEndCustomData(data)) {
@@ -4476,7 +4588,6 @@ var CardProjection = class {
4476
4588
  PROTO_VERSION,
4477
4589
  PooledConn,
4478
4590
  QueryGate,
4479
- REMOVED_INTENT_HINTS,
4480
4591
  ReconnectError,
4481
4592
  SSEBroadcaster,
4482
4593
  STREAM_END,
@@ -4514,15 +4625,20 @@ var CardProjection = class {
4514
4625
  fetchLoopHistory,
4515
4626
  fetchLoopMessages,
4516
4627
  fetchSkillsCatalog,
4628
+ formatTurnId,
4629
+ frameSeq,
4630
+ frameTurnId,
4517
4631
  idleTimeoutForTurn,
4518
4632
  inboundNeedsDeliveryAck,
4519
4633
  inputMessageForLoop,
4520
4634
  isCompletionEvent,
4521
4635
  isDaemonLive,
4522
4636
  isDaemonTurnEndEvent,
4637
+ isIdleTerminalAllowed,
4523
4638
  isSubagentProgressEvent,
4524
4639
  isTurnEndCustomData,
4525
4640
  isTurnProgressChunk,
4641
+ isTurnTerminalAllowed,
4526
4642
  isValidVerbosityLevel,
4527
4643
  loadConfigFromEnv,
4528
4644
  newLoopInputMessage,
@@ -4532,6 +4648,7 @@ var CardProjection = class {
4532
4648
  notificationEnvelope,
4533
4649
  parseCardCustomPayload,
4534
4650
  parseNamespace,
4651
+ parseTurnGeneration,
4535
4652
  pingEnvelope,
4536
4653
  pongEnvelope,
4537
4654
  protocol1Rpc,
@@ -4542,8 +4659,8 @@ var CardProjection = class {
4542
4659
  shouldShow,
4543
4660
  splitWirePayload,
4544
4661
  subscribeEnvelope,
4662
+ turnIdsMatch,
4545
4663
  unsubscribeEnvelope,
4546
- validateLoopInputIntentHint,
4547
4664
  waitDaemonReady,
4548
4665
  waitLoopStatusWithID,
4549
4666
  waitSubscriptionConfirmed