orion-super-agent-dev 0.1.40 → 0.1.41

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/out/main.js CHANGED
@@ -30114,6 +30114,29 @@ var require_refreshOnRefusal = __commonJS({
30114
30114
  }
30115
30115
  });
30116
30116
 
30117
+ // ../packages/orion-client-core/dist/boundaryEvents.js
30118
+ var require_boundaryEvents = __commonJS({
30119
+ "../packages/orion-client-core/dist/boundaryEvents.js"(exports2) {
30120
+ "use strict";
30121
+ Object.defineProperty(exports2, "__esModule", { value: true });
30122
+ exports2.BOUNDARY_EVENTS = void 0;
30123
+ exports2.resumeTokenOf = resumeTokenOf;
30124
+ exports2.BOUNDARY_EVENTS = /* @__PURE__ */ new Set([
30125
+ "tool_request",
30126
+ "permission_request",
30127
+ "plan_approval_request",
30128
+ "user_input_request",
30129
+ "hook_request",
30130
+ "file_history_rewind_request"
30131
+ ]);
30132
+ function resumeTokenOf(event) {
30133
+ if (!exports2.BOUNDARY_EVENTS.has(event.event))
30134
+ return void 0;
30135
+ return event.data?.resume_token ?? void 0;
30136
+ }
30137
+ }
30138
+ });
30139
+
30117
30140
  // ../packages/orion-client-core/dist/atomicWrite.js
30118
30141
  var require_atomicWrite = __commonJS({
30119
30142
  "../packages/orion-client-core/dist/atomicWrite.js"(exports2) {
@@ -32038,6 +32061,41 @@ var require_interimModels = __commonJS({
32038
32061
  }
32039
32062
  });
32040
32063
 
32064
+ // ../packages/orion-client-core/dist/interruptedTurn.js
32065
+ var require_interruptedTurn = __commonJS({
32066
+ "../packages/orion-client-core/dist/interruptedTurn.js"(exports2) {
32067
+ "use strict";
32068
+ Object.defineProperty(exports2, "__esModule", { value: true });
32069
+ exports2.MOVED_ON_NOTE = void 0;
32070
+ exports2.interruptedTurnMessages = interruptedTurnMessages;
32071
+ exports2.carryInterruptedTurn = carryInterruptedTurn;
32072
+ exports2.interruptedTurnOf = interruptedTurnOf2;
32073
+ var textBlock = (text) => ({ type: "text", text });
32074
+ exports2.MOVED_ON_NOTE = "[Reply cut off here \u2014 the user sent their next message before it finished. That message is what they want now: pick up from this point as it directs, without restarting this reply or remarking on the cut.]";
32075
+ function interruptedTurnMessages(messages, prompt, partialReply, cause = "stopped") {
32076
+ const out2 = [...messages];
32077
+ if (prompt && prompt.trim().length > 0) {
32078
+ out2.push({ role: "user", content: [textBlock(prompt)] });
32079
+ }
32080
+ if (partialReply && partialReply.trim().length > 0) {
32081
+ const content = [textBlock(partialReply)];
32082
+ if (cause === "moved_on")
32083
+ content.push(textBlock(exports2.MOVED_ON_NOTE));
32084
+ out2.push({ role: "assistant", content });
32085
+ }
32086
+ return out2;
32087
+ }
32088
+ var carried = /* @__PURE__ */ new WeakMap();
32089
+ function carryInterruptedTurn(error, turn) {
32090
+ if (typeof error === "object" && error !== null)
32091
+ carried.set(error, turn);
32092
+ }
32093
+ function interruptedTurnOf2(error) {
32094
+ return typeof error === "object" && error !== null ? carried.get(error) : void 0;
32095
+ }
32096
+ }
32097
+ });
32098
+
32041
32099
  // ../packages/orion-client-core/dist/transportError.js
32042
32100
  var require_transportError = __commonJS({
32043
32101
  "../packages/orion-client-core/dist/transportError.js"(exports2) {
@@ -38006,6 +38064,309 @@ var require_toolConcurrency = __commonJS({
38006
38064
  }
38007
38065
  });
38008
38066
 
38067
+ // ../packages/orion-client-core/dist/turnClose.js
38068
+ var require_turnClose = __commonJS({
38069
+ "../packages/orion-client-core/dist/turnClose.js"(exports2) {
38070
+ "use strict";
38071
+ Object.defineProperty(exports2, "__esModule", { value: true });
38072
+ exports2.CLOSE_TURNS_IN_BACKGROUND = exports2.STOPPED_TOOL_GRACE_MS = exports2.TURN_CLOSE_TIMEOUT_MS = exports2.TURN_CLOSE_WAIT_MS = void 0;
38073
+ exports2.closeTurn = closeTurn;
38074
+ var boundaryEvents_js_1 = require_boundaryEvents();
38075
+ var interruptedTurn_js_1 = require_interruptedTurn();
38076
+ exports2.TURN_CLOSE_WAIT_MS = 3e3;
38077
+ exports2.TURN_CLOSE_TIMEOUT_MS = 15e3;
38078
+ exports2.STOPPED_TOOL_GRACE_MS = 500;
38079
+ exports2.CLOSE_TURNS_IN_BACKGROUND = { turnCloseWaitMs: 0, stoppedToolGraceMs: 0 };
38080
+ var RUNS_THE_TURN = /* @__PURE__ */ new Set([
38081
+ "text_delta",
38082
+ "thinking_delta",
38083
+ "tool_use_start",
38084
+ "usage_recorded",
38085
+ ...boundaryEvents_js_1.BOUNDARY_EVENTS,
38086
+ "turn_complete"
38087
+ ]);
38088
+ async function closeTurn(trail, ending, deps2) {
38089
+ if (ending === "failed" && !trail.contentSeen)
38090
+ return void 0;
38091
+ const everything = await everythingTheTurnDid(trail, ending, deps2).catch(() => void 0);
38092
+ if (everything !== void 0)
38093
+ return everything;
38094
+ try {
38095
+ return {
38096
+ kept: "words",
38097
+ messages: (0, interruptedTurn_js_1.interruptedTurnMessages)(deps2.committed(), deps2.prompt, trail.words, deps2.cause)
38098
+ };
38099
+ } catch {
38100
+ return void 0;
38101
+ }
38102
+ }
38103
+ async function everythingTheTurnDid(trail, ending, deps2) {
38104
+ if (trail.resumeToken === void 0)
38105
+ return void 0;
38106
+ const parked = trail.parked;
38107
+ if (parked)
38108
+ await settledWithin(trail.stillRunning, deps2.graceMs);
38109
+ const closing = parked && ending === "stopped" ? "abandoned" : "recovery";
38110
+ const transcript = await engineTranscript(deps2.transport, deps2.closingBody(closing, trail.resumeToken, trail.answered), deps2.waitMs);
38111
+ if (transcript === void 0)
38112
+ return void 0;
38113
+ return {
38114
+ kept: "everything",
38115
+ // Parked, the token holds every word the turn said. Cut mid-stream, the words since
38116
+ // the last boundary are in no token: they follow the transcript as the step that was cut.
38117
+ messages: parked ? transcript : (0, interruptedTurn_js_1.interruptedTurnMessages)(transcript, null, trail.wordsSinceBoundary, deps2.cause)
38118
+ };
38119
+ }
38120
+ async function settledWithin(work, ms) {
38121
+ if (work.length === 0 || ms <= 0)
38122
+ return;
38123
+ let timer;
38124
+ const elapsed = new Promise((resolve5) => {
38125
+ timer = setTimeout(resolve5, ms);
38126
+ timer.unref?.();
38127
+ });
38128
+ try {
38129
+ await Promise.race([Promise.allSettled(work).then(() => void 0), elapsed]);
38130
+ } finally {
38131
+ if (timer !== void 0)
38132
+ clearTimeout(timer);
38133
+ }
38134
+ }
38135
+ function engineTranscript(transport, body2, waitMs) {
38136
+ return new Promise((resolve5) => {
38137
+ const ac = new AbortController();
38138
+ const timers = [];
38139
+ const after = (ms, act) => {
38140
+ const timer = setTimeout(act, ms);
38141
+ timer.unref?.();
38142
+ timers.push(timer);
38143
+ };
38144
+ const onEvent = (event) => {
38145
+ if (RUNS_THE_TURN.has(event.event)) {
38146
+ ac.abort();
38147
+ resolve5(void 0);
38148
+ return;
38149
+ }
38150
+ if (event.event !== "turn_error")
38151
+ return;
38152
+ const messages = event.data?.messages;
38153
+ resolve5(Array.isArray(messages) ? messages : void 0);
38154
+ };
38155
+ after(exports2.TURN_CLOSE_TIMEOUT_MS, () => ac.abort());
38156
+ if (waitMs > 0)
38157
+ after(waitMs, () => resolve5(void 0));
38158
+ else
38159
+ resolve5(void 0);
38160
+ let closing;
38161
+ try {
38162
+ closing = transport.streamSegment(body2, onEvent, ac.signal);
38163
+ } catch {
38164
+ closing = Promise.resolve();
38165
+ }
38166
+ void closing.catch(() => void 0).finally(() => {
38167
+ for (const timer of timers)
38168
+ clearTimeout(timer);
38169
+ resolve5(void 0);
38170
+ });
38171
+ });
38172
+ }
38173
+ }
38174
+ });
38175
+
38176
+ // ../packages/orion-client-core/dist/planState.js
38177
+ var require_planState = __commonJS({
38178
+ "../packages/orion-client-core/dist/planState.js"(exports2) {
38179
+ "use strict";
38180
+ Object.defineProperty(exports2, "__esModule", { value: true });
38181
+ exports2.planOfEvent = planOfEvent2;
38182
+ function planOfEvent2(data) {
38183
+ if (typeof data !== "object" || data === null)
38184
+ return null;
38185
+ const { steps: rawSteps, current_step: pointer } = data;
38186
+ const steps = Array.isArray(rawSteps) ? rawSteps.filter((step) => typeof step === "string") : [];
38187
+ if (steps.length === 0)
38188
+ return null;
38189
+ return {
38190
+ steps,
38191
+ current_step: typeof pointer === "number" && Number.isFinite(pointer) ? pointer : 0
38192
+ };
38193
+ }
38194
+ }
38195
+ });
38196
+
38197
+ // ../packages/orion-client-core/dist/turnTrail.js
38198
+ var require_turnTrail = __commonJS({
38199
+ "../packages/orion-client-core/dist/turnTrail.js"(exports2) {
38200
+ "use strict";
38201
+ Object.defineProperty(exports2, "__esModule", { value: true });
38202
+ exports2.TurnTrail = void 0;
38203
+ var boundaryEvents_js_1 = require_boundaryEvents();
38204
+ var planState_js_1 = require_planState();
38205
+ var StreamedWords = class {
38206
+ text = "";
38207
+ stepEnded = false;
38208
+ add(delta) {
38209
+ if (delta === "")
38210
+ return;
38211
+ if (this.stepEnded && this.text !== "")
38212
+ this.text = `${this.text.replace(/\n+$/, "")}
38213
+
38214
+ `;
38215
+ this.stepEnded = false;
38216
+ this.text += delta;
38217
+ }
38218
+ /** What follows belongs to the next step. */
38219
+ endStep() {
38220
+ this.stepEnded = true;
38221
+ }
38222
+ read() {
38223
+ return this.text;
38224
+ }
38225
+ };
38226
+ var TurnTrail = class {
38227
+ /** The `.orion` snapshot the turn's segments carry — a closing carries the same one. */
38228
+ orionResources;
38229
+ /** Whether anything of the turn reached the host. A turn that never did changed nothing. */
38230
+ contentSeen = false;
38231
+ /** The plan the engine last announced this turn; `undefined` = it announced none. */
38232
+ plan;
38233
+ token;
38234
+ isParked = false;
38235
+ resumedOn;
38236
+ carried = [];
38237
+ answer;
38238
+ batch = [];
38239
+ settled = /* @__PURE__ */ new Map();
38240
+ running = /* @__PURE__ */ new Set();
38241
+ turnWords = new StreamedWords();
38242
+ stepWords = new StreamedWords();
38243
+ // ── what happens to a turn ────────────────────────────────────────────────────────────
38244
+ /** A segment goes out, resumed on `result` (nothing, for the turn's first). The engine
38245
+ * owns the turn until it ends or parks. */
38246
+ segmentStarts(result) {
38247
+ this.isParked = false;
38248
+ this.resumedOn = result;
38249
+ this.carried = [];
38250
+ this.answer = void 0;
38251
+ this.batch = [];
38252
+ this.settled.clear();
38253
+ this.running.clear();
38254
+ this.stepWords = new StreamedWords();
38255
+ this.turnWords.endStep();
38256
+ }
38257
+ /** That segment carries `steering`: what the host had queued for the turn since the last
38258
+ * one — the user's words, a sub-agent's report. The engine folds it as the segment starts. */
38259
+ steeredWith(steering) {
38260
+ this.carried = steering;
38261
+ }
38262
+ /** The engine parked the turn at a boundary: it waits on this machine, and `token` holds
38263
+ * everything it did up to here — the words of the segment that parked it included, and
38264
+ * what that segment was steered with. Heard once per segment; the trail hears it from the
38265
+ * boundary event itself as it ARRIVES ({@link observe}), because a stop or a cut can land
38266
+ * before that segment's stream ends. */
38267
+ parkedAt(token) {
38268
+ if (this.isParked)
38269
+ return;
38270
+ this.token = token;
38271
+ this.isParked = true;
38272
+ this.resumedOn = void 0;
38273
+ this.carried = [];
38274
+ this.stepWords = new StreamedWords();
38275
+ this.turnWords.endStep();
38276
+ }
38277
+ /** The client answered the boundary in full; the turn stays parked until the next segment
38278
+ * goes out with the answer. */
38279
+ answeredWith(result) {
38280
+ this.answer = result;
38281
+ }
38282
+ /** One event of the turn's stream, as the host is about to see it. */
38283
+ observe(event) {
38284
+ this.contentSeen = true;
38285
+ const token = (0, boundaryEvents_js_1.resumeTokenOf)(event);
38286
+ if (token !== void 0)
38287
+ this.parkedAt(token);
38288
+ if (event.agent_id)
38289
+ return;
38290
+ const data = event.data;
38291
+ switch (event.event) {
38292
+ case "text_delta": {
38293
+ const delta = typeof data?.delta === "string" ? data.delta : "";
38294
+ this.turnWords.add(delta);
38295
+ this.stepWords.add(delta);
38296
+ break;
38297
+ }
38298
+ case "tool_use_start":
38299
+ this.turnWords.endStep();
38300
+ this.stepWords.endStep();
38301
+ break;
38302
+ case "plan_updated":
38303
+ this.plan = (0, planState_js_1.planOfEvent)(data);
38304
+ break;
38305
+ default:
38306
+ break;
38307
+ }
38308
+ }
38309
+ /** The parked boundary is a batch of client tools. */
38310
+ runsTools(batch) {
38311
+ this.batch = batch;
38312
+ }
38313
+ /** One tool of that batch, running: its result is kept the moment it settles, whether or
38314
+ * not the batch ever finishes. A tool that fails to settle leaves no result. */
38315
+ watch(request, run3) {
38316
+ const watched = run3.then((result) => {
38317
+ this.settled.set(request.request_id, result);
38318
+ }, () => void 0);
38319
+ this.running.add(watched);
38320
+ void watched.finally(() => this.running.delete(watched));
38321
+ return run3;
38322
+ }
38323
+ // ── what a closing reads ──────────────────────────────────────────────────────────────
38324
+ /** The last boundary's resume token; `undefined` before the turn reached one. */
38325
+ get resumeToken() {
38326
+ return this.token;
38327
+ }
38328
+ /** Whether the turn is waiting at that boundary (no segment is streaming). */
38329
+ get parked() {
38330
+ return this.isParked;
38331
+ }
38332
+ /** What the client has answered the last boundary with. While a segment STREAMS, the whole
38333
+ * answer it was resumed on. While the turn is PARKED, the whole answer once there is one;
38334
+ * until then, of a batch of tools, the results of those that have settled so far, in the
38335
+ * order they were asked for — and of a card, nothing. */
38336
+ get answered() {
38337
+ if (!this.isParked)
38338
+ return this.resumedOn;
38339
+ if (this.answer !== void 0)
38340
+ return this.answer;
38341
+ const results = this.batch.flatMap((request) => {
38342
+ const result = this.settled.get(request.request_id);
38343
+ return result === void 0 ? [] : [result];
38344
+ });
38345
+ return results.length === 0 ? void 0 : { kind: "tool_result", results };
38346
+ }
38347
+ /** What the segment in flight was steered with — in no token until that segment parks, so
38348
+ * a closing carries it again. Nothing while the turn is parked: the token holds what rode
38349
+ * the segment that parked it, and what was queued since is still the host's. */
38350
+ get steering() {
38351
+ return this.carried;
38352
+ }
38353
+ /** The tools of the parked batch that have not settled yet. */
38354
+ get stillRunning() {
38355
+ return [...this.running];
38356
+ }
38357
+ /** Every word the turn's main agent streamed. */
38358
+ get words() {
38359
+ return this.turnWords.read();
38360
+ }
38361
+ /** The words streamed since the last boundary — the ones no token holds. */
38362
+ get wordsSinceBoundary() {
38363
+ return this.stepWords.read();
38364
+ }
38365
+ };
38366
+ exports2.TurnTrail = TurnTrail;
38367
+ }
38368
+ });
38369
+
38009
38370
  // ../packages/orion-client-core/dist/segmentDriver.js
38010
38371
  var require_segmentDriver = __commonJS({
38011
38372
  "../packages/orion-client-core/dist/segmentDriver.js"(exports2) {
@@ -38062,12 +38423,14 @@ var require_segmentDriver = __commonJS({
38062
38423
  exports2.runToolBatch = runToolBatch;
38063
38424
  var node_events_1 = __require("node:events");
38064
38425
  var os9 = __importStar(__require("node:os"));
38426
+ var boundaryEvents_js_1 = require_boundaryEvents();
38065
38427
  var compactionTrigger_js_1 = require_compactionTrigger();
38066
38428
  var duetEntry_js_1 = require_duetEntry();
38067
38429
  var duetRouterModel_js_1 = require_duetRouterModel();
38068
38430
  var duetMode_js_1 = require_duetMode();
38069
38431
  var duetPlan_js_1 = require_duetPlan();
38070
38432
  var interimModels_js_1 = require_interimModels();
38433
+ var interruptedTurn_js_1 = require_interruptedTurn();
38071
38434
  var retrievalModels_js_1 = require_retrievalModels();
38072
38435
  var retrievalSteps_js_1 = require_retrievalSteps();
38073
38436
  var repoIdentity_js_1 = require_repoIdentity();
@@ -38081,6 +38444,8 @@ var require_segmentDriver = __commonJS({
38081
38444
  var wellFormed_js_1 = require_wellFormed();
38082
38445
  var orionSnapshot_js_1 = require_orionSnapshot();
38083
38446
  var toolConcurrency_js_1 = require_toolConcurrency();
38447
+ var turnClose_js_1 = require_turnClose();
38448
+ var turnTrail_js_1 = require_turnTrail();
38084
38449
  function turnAbortController() {
38085
38450
  const ac = new AbortController();
38086
38451
  (0, node_events_1.setMaxListeners)(0, ac.signal);
@@ -38126,14 +38491,6 @@ var require_segmentDriver = __commonJS({
38126
38491
  return error instanceof transportError_js_2.TransportError && (error.status === void 0 || error.status >= 500);
38127
38492
  }
38128
38493
  exports2.TERMINAL_EVENTS = /* @__PURE__ */ new Set(["turn_complete", "turn_error"]);
38129
- var BOUNDARY_EVENTS = /* @__PURE__ */ new Set([
38130
- "tool_request",
38131
- "permission_request",
38132
- "plan_approval_request",
38133
- "user_input_request",
38134
- "hook_request",
38135
- "file_history_rewind_request"
38136
- ]);
38137
38494
  function effectiveWirePermissionMode2(turbo, configured2) {
38138
38495
  return turbo ? "full_access" : configured2;
38139
38496
  }
@@ -38220,20 +38577,57 @@ var require_segmentDriver = __commonJS({
38220
38577
  }
38221
38578
  async function driveTurn3(transport, start2, handlers, opts) {
38222
38579
  const work = (0, keepAwake_js_1.beginAgentTurn)();
38580
+ const trail = new turnTrail_js_1.TurnTrail();
38581
+ const closing = closingFor(transport, start2, handlers, trail, opts);
38223
38582
  try {
38224
- return await driveTurnToOutcome(transport, start2, cardsWaitOnPerson(handlers, work), opts);
38583
+ return await driveTurnToOutcome(transport, start2, cardsWaitOnPerson(handlers, work), opts, trail, closing);
38225
38584
  } catch (error) {
38226
- if ((0, transportError_js_2.isAbortError)(error)) {
38585
+ const stopped = (0, transportError_js_2.isAbortError)(error) || stopRequested(handlers);
38586
+ if (stopped) {
38227
38587
  try {
38228
38588
  opts?.mirror?.(stoppedTerminal(start2.sessionId));
38229
38589
  } catch {
38230
38590
  }
38231
38591
  }
38592
+ const left = await closing(stopped ? "stopped" : "failed");
38593
+ if (left !== void 0)
38594
+ (0, interruptedTurn_js_1.carryInterruptedTurn)(error, left);
38232
38595
  throw error;
38233
38596
  } finally {
38234
38597
  work.end();
38235
38598
  }
38236
38599
  }
38600
+ function closingFor(transport, start2, handlers, trail, opts) {
38601
+ let closed;
38602
+ return (ending) => closed ??= (0, turnClose_js_1.closeTurn)(trail, ending, {
38603
+ transport,
38604
+ closingBody: (closing, token, answered) => buildSegmentBody(start2, { resumeToken: token, boundaryResult: answered, closing }, trail),
38605
+ committed: () => start2.getMessages(),
38606
+ prompt: promptOf2(start2),
38607
+ cause: ending === "stopped" ? stopCauseOf(handlers) : "stopped",
38608
+ waitMs: opts?.turnCloseWaitMs ?? turnClose_js_1.TURN_CLOSE_WAIT_MS,
38609
+ graceMs: opts?.stoppedToolGraceMs ?? turnClose_js_1.STOPPED_TOOL_GRACE_MS
38610
+ });
38611
+ }
38612
+ function stopRequested(handlers) {
38613
+ try {
38614
+ return handlers.signal?.aborted === true || handlers.isCancelRequested?.() === true;
38615
+ } catch {
38616
+ return false;
38617
+ }
38618
+ }
38619
+ function stopCauseOf(handlers) {
38620
+ try {
38621
+ return handlers.stopCause?.() ?? "stopped";
38622
+ } catch {
38623
+ return "stopped";
38624
+ }
38625
+ }
38626
+ function promptOf2(start2) {
38627
+ if (start2.skill)
38628
+ return `/${start2.skill.name}${start2.skill.args ? ` ${start2.skill.args}` : ""}`;
38629
+ return start2.text ?? null;
38630
+ }
38237
38631
  function cardsWaitOnPerson(handlers, work) {
38238
38632
  const waitingOnPerson = (card) => async (...args2) => {
38239
38633
  const resume = work.waitOnPerson();
@@ -38250,8 +38644,8 @@ var require_segmentDriver = __commonJS({
38250
38644
  onAskUser: waitingOnPerson(handlers.onAskUser)
38251
38645
  };
38252
38646
  }
38253
- async function driveTurnToOutcome(transport, start2, handlers, opts) {
38254
- const orionResources = start2.getOrionResources ? await start2.getOrionResources() : void 0;
38647
+ async function driveTurnToOutcome(transport, start2, handlers, opts, trail, closing) {
38648
+ trail.orionResources = start2.getOrionResources ? await start2.getOrionResources() : void 0;
38255
38649
  void (0, scratchpad_js_1.ensureSessionScratchDir)({
38256
38650
  sessionId: start2.sessionId,
38257
38651
  workspaceRoot: (0, pathResolution_js_1.effectiveWorkspaceRoot)(start2.routing?.workspaceRoot ?? null)
@@ -38262,7 +38656,6 @@ var require_segmentDriver = __commonJS({
38262
38656
  let transportRetried = false;
38263
38657
  let busyAttempts = 0;
38264
38658
  let busyWaitedMs = 0;
38265
- const stoppedDuringWait = () => handlers.signal?.aborted === true || handlers.isCancelRequested?.() === true;
38266
38659
  for (; ; ) {
38267
38660
  let sawEvent = false;
38268
38661
  let drained = [];
@@ -38275,7 +38668,7 @@ var require_segmentDriver = __commonJS({
38275
38668
  }
38276
38669
  };
38277
38670
  try {
38278
- return await driveTurnAttempt(transport, attemptStart, handlers, orionResources, () => {
38671
+ return await driveTurnAttempt(transport, attemptStart, handlers, trail, closing, () => {
38279
38672
  sawEvent = true;
38280
38673
  }, {
38281
38674
  silenceMs: opts?.streamSilenceMs ?? exports2.STREAM_SILENCE_TIMEOUT_MS,
@@ -38283,7 +38676,7 @@ var require_segmentDriver = __commonJS({
38283
38676
  }, busy !== null, opts?.mirror);
38284
38677
  } catch (error) {
38285
38678
  if (error instanceof SessionBusyRefusal) {
38286
- if (busy !== null && busyWaitedMs < busy.maxWaitMs && !stoppedDuringWait()) {
38679
+ if (busy !== null && busyWaitedMs < busy.maxWaitMs && !stopRequested(handlers)) {
38287
38680
  requeue = drained;
38288
38681
  const delayMs = sessionBusyDelayMs(busyAttempts, busy);
38289
38682
  busyAttempts += 1;
@@ -38295,7 +38688,7 @@ var require_segmentDriver = __commonJS({
38295
38688
  setTimeout(resolve5, delayMs);
38296
38689
  });
38297
38690
  busyWaitedMs += delayMs;
38298
- if (stoppedDuringWait())
38691
+ if (stopRequested(handlers))
38299
38692
  throw new DOMException("turn aborted", "AbortError");
38300
38693
  continue;
38301
38694
  }
@@ -38442,6 +38835,19 @@ var require_segmentDriver = __commonJS({
38442
38835
  };
38443
38836
  return { event: "turn_error", session_id: sessionId, timestamp: Date.now() / 1e3, data };
38444
38837
  }
38838
+ function endsWithNothing(ev, trail) {
38839
+ if (ev.event !== "turn_error")
38840
+ return false;
38841
+ const data = ev.data;
38842
+ if (Array.isArray(data.messages))
38843
+ return false;
38844
+ return data.error === exports2.TURN_CANCELLED_ERROR || trail.contentSeen;
38845
+ }
38846
+ async function withWhatTheTurnLeft(terminal, closing) {
38847
+ const stopped = terminal.data.error === exports2.TURN_CANCELLED_ERROR;
38848
+ const left = await closing(stopped ? "stopped" : "failed");
38849
+ return left === void 0 ? terminal : { ...terminal, data: { ...terminal.data, messages: left.messages } };
38850
+ }
38445
38851
  function stoppedTerminal(sessionId) {
38446
38852
  return {
38447
38853
  event: "turn_error",
@@ -38460,11 +38866,10 @@ var require_segmentDriver = __commonJS({
38460
38866
  const plan = file ? (0, planFile_js_1.readPlanFileSync)(file.path) : null;
38461
38867
  return plan === null ? ev : { ...ev, data: { ...ev.data, plan } };
38462
38868
  }
38463
- async function driveTurnAttempt(transport, start2, handlers, orionResources, markEvent, caps, busyRetry, mirror) {
38869
+ async function driveTurnAttempt(transport, start2, handlers, trail, closing, markEvent, caps, busyRetry, mirror) {
38464
38870
  let resumeToken;
38465
38871
  let boundaryResult;
38466
38872
  let first = true;
38467
- let contentSeen = false;
38468
38873
  const workspaceRoot = start2.routing?.workspaceRoot;
38469
38874
  let applyChain = Promise.resolve();
38470
38875
  const agentTouchedRoots = /* @__PURE__ */ new Set();
@@ -38483,7 +38888,7 @@ var require_segmentDriver = __commonJS({
38483
38888
  handlers.onEvent(ev);
38484
38889
  return;
38485
38890
  }
38486
- contentSeen = true;
38891
+ trail.observe(ev);
38487
38892
  markEvent();
38488
38893
  if (ev.event === "client_write") {
38489
38894
  const d2 = ev.data;
@@ -38528,10 +38933,12 @@ var require_segmentDriver = __commonJS({
38528
38933
  throw new DOMException("turn aborted", "AbortError");
38529
38934
  }
38530
38935
  const openingSegment = first;
38531
- const body2 = buildSegmentBody(start2, first, resumeToken, boundaryResult, orionResources);
38936
+ trail.segmentStarts(boundaryResult);
38937
+ const body2 = buildSegmentBody(start2, first ? "opening" : { resumeToken, boundaryResult }, trail);
38532
38938
  first = false;
38533
38939
  handlers.onPhaseChange?.("stream");
38534
38940
  let terminal;
38941
+ let held;
38535
38942
  let markTerminal;
38536
38943
  const terminalSeen = new Promise((resolve5) => {
38537
38944
  markTerminal = resolve5;
@@ -38540,11 +38947,19 @@ var require_segmentDriver = __commonJS({
38540
38947
  let busyRefusal;
38541
38948
  const segmentOnEvent = (ev) => {
38542
38949
  watchdog.feed();
38543
- if (busyRetry && openingSegment && !contentSeen && busyRefusal === void 0 && ev.event === "turn_error" && isSessionBusyRefusal(ev.data)) {
38950
+ if (busyRetry && openingSegment && !trail.contentSeen && busyRefusal === void 0 && ev.event === "turn_error" && isSessionBusyRefusal(ev.data)) {
38544
38951
  busyRefusal = ev;
38545
38952
  markTerminal();
38546
38953
  return;
38547
38954
  }
38955
+ if (!terminal && held === void 0 && endsWithNothing(ev, trail)) {
38956
+ held = ev;
38957
+ markTerminal();
38958
+ return;
38959
+ }
38960
+ deliver(ev);
38961
+ };
38962
+ const deliver = (ev) => {
38548
38963
  onEvent(ev);
38549
38964
  if (!terminal && (ev.event === "turn_complete" || ev.event === "turn_error")) {
38550
38965
  terminal = ev;
@@ -38571,6 +38986,14 @@ var require_segmentDriver = __commonJS({
38571
38986
  });
38572
38987
  throw new SessionBusyRefusal(busyRefusal);
38573
38988
  }
38989
+ if (held !== void 0 && !handlers.signal?.aborted) {
38990
+ watchdog.settle();
38991
+ void streamDone.catch(() => {
38992
+ });
38993
+ const whole = await withWhatTheTurnLeft(held, closing);
38994
+ deliver(whole);
38995
+ return { kind: "turn_error", event: whole };
38996
+ }
38574
38997
  if (terminal) {
38575
38998
  watchdog.settle();
38576
38999
  void streamDone.catch(() => {
@@ -38582,8 +39005,8 @@ var require_segmentDriver = __commonJS({
38582
39005
  watchdog.settle();
38583
39006
  void streamDone.catch(() => {
38584
39007
  });
38585
- const synthetic = killTerminal(start2.sessionId, kill, watchdog.step(), (0, keepAwake_js_1.sleptDuring)(watchdog.armedAt()));
38586
- segmentOnEvent(synthetic);
39008
+ const synthetic = await withWhatTheTurnLeft(killTerminal(start2.sessionId, kill, watchdog.step(), (0, keepAwake_js_1.sleptDuring)(watchdog.armedAt())), closing);
39009
+ deliver(synthetic);
38587
39010
  return { kind: "turn_error", event: synthetic };
38588
39011
  }
38589
39012
  if (handlers.signal?.aborted) {
@@ -38603,17 +39026,19 @@ var require_segmentDriver = __commonJS({
38603
39026
  handlers.onPhaseChange?.("boundary");
38604
39027
  resumeToken = readResumeToken(events);
38605
39028
  if (resumeToken === void 0) {
38606
- if (!events.some((e) => BOUNDARY_EVENTS.has(e.event))) {
39029
+ if (!events.some((e) => boundaryEvents_js_1.BOUNDARY_EVENTS.has(e.event))) {
38607
39030
  throw new transportError_js_2.TransportError("segment stream ended without a terminal \u2014 the connection was cut mid-turn");
38608
39031
  }
38609
39032
  throw new Error("boundary segment carried no resume_token \u2014 cannot resume the turn");
38610
39033
  }
39034
+ trail.parkedAt(resumeToken);
38611
39035
  const tokenSaving = start2.routing?.tokenSaving;
38612
39036
  const toolContext = tokenSaving === void 0 && handlers.signal === void 0 ? void 0 : {
38613
39037
  ...tokenSaving !== void 0 ? { tokenSaving } : {},
38614
39038
  ...handlers.signal !== void 0 ? { signal: handlers.signal } : {}
38615
39039
  };
38616
- boundaryResult = await untilStopped(resolveBoundary(events, handlers, start2.sessionId, toolContext), handlers.signal);
39040
+ boundaryResult = await untilStopped(resolveBoundary(events, handlers, start2.sessionId, toolContext, trail), handlers.signal);
39041
+ trail.answeredWith(boundaryResult);
38617
39042
  }
38618
39043
  }
38619
39044
  function untilStopped(work, signal) {
@@ -38634,7 +39059,8 @@ var require_segmentDriver = __commonJS({
38634
39059
  });
38635
39060
  });
38636
39061
  }
38637
- function buildSegmentBody(start2, first, resumeToken, boundaryResult, orionResources) {
39062
+ function buildSegmentBody(start2, segment, trail) {
39063
+ const orionResources = trail.orionResources;
38638
39064
  const body2 = {
38639
39065
  // Host-supplied extras ride first so the canonical fields below always win
38640
39066
  // a key collision (rule: the driver owns the wire shape, extras only add).
@@ -38683,9 +39109,9 @@ var require_segmentDriver = __commonJS({
38683
39109
  const repoIdentity = (0, repoIdentity_js_1.repoIdentityForSegment)(start2.routing?.workspaceRoot);
38684
39110
  if (repoIdentity !== void 0)
38685
39111
  Object.assign(body2, repoIdentity);
38686
- const cs = start2.getClientState?.();
38687
- if (cs !== void 0)
38688
- body2.client_state = cs;
39112
+ const clientState = clientStateForSegment(start2.getClientState?.(), trail.plan);
39113
+ if (clientState !== void 0)
39114
+ body2.client_state = clientState;
38689
39115
  const fileHistory = start2.getFileHistoryManifest?.();
38690
39116
  if (fileHistory && fileHistory.snapshots.length > 0)
38691
39117
  body2.file_history = fileHistory;
@@ -38701,7 +39127,8 @@ var require_segmentDriver = __commonJS({
38701
39127
  if (attention && (attention.active || attention.open.length > 0 || attention.touched.length > 0)) {
38702
39128
  body2.ovault_attention = attention;
38703
39129
  }
38704
- if (first) {
39130
+ const closing = segment === "opening" ? void 0 : segment.closing;
39131
+ if (segment === "opening") {
38705
39132
  if (start2.skill) {
38706
39133
  body2.skill = start2.skill;
38707
39134
  } else {
@@ -38710,12 +39137,19 @@ var require_segmentDriver = __commonJS({
38710
39137
  if (start2.attachments && start2.attachments.length > 0)
38711
39138
  body2.attachments = start2.attachments;
38712
39139
  } else {
38713
- body2.resume_token = resumeToken;
38714
- if (boundaryResult !== void 0)
38715
- body2.boundary_result = boundaryResult;
38716
- }
38717
- const queued = start2.drainQueuedInput?.();
38718
- if (queued && queued.length > 0)
39140
+ body2.resume_token = segment.resumeToken;
39141
+ if (segment.boundaryResult !== void 0)
39142
+ body2.boundary_result = segment.boundaryResult;
39143
+ if (closing !== void 0) {
39144
+ body2.abandoned = true;
39145
+ if (closing === "recovery")
39146
+ body2.recovery = true;
39147
+ }
39148
+ }
39149
+ const queued = closing !== void 0 ? trail.steering : start2.drainQueuedInput?.() ?? [];
39150
+ if (closing === void 0)
39151
+ trail.steeredWith(queued);
39152
+ if (queued.length > 0)
38719
39153
  body2.queued_input = queued.map(queuedInput_js_1.queuedInputWire);
38720
39154
  const relayDepth = start2.peerRelayDepth?.();
38721
39155
  if (relayDepth !== void 0)
@@ -38728,6 +39162,11 @@ var require_segmentDriver = __commonJS({
38728
39162
  }
38729
39163
  return (0, wellFormed_js_1.toWellFormedJsonDeep)(body2);
38730
39164
  }
39165
+ function clientStateForSegment(state, announced) {
39166
+ if (announced === void 0)
39167
+ return state;
39168
+ return state === void 0 ? { plan: announced } : { ...state, plan: announced };
39169
+ }
38731
39170
  function applyRouting(body2, routing) {
38732
39171
  if (!routing)
38733
39172
  return;
@@ -38772,15 +39211,13 @@ var require_segmentDriver = __commonJS({
38772
39211
  }
38773
39212
  function readResumeToken(events) {
38774
39213
  for (const e of events) {
38775
- if (BOUNDARY_EVENTS.has(e.event)) {
38776
- const t = e.data.resume_token;
38777
- if (t !== void 0 && t !== null)
38778
- return t;
38779
- }
39214
+ const token = (0, boundaryEvents_js_1.resumeTokenOf)(e);
39215
+ if (token !== void 0)
39216
+ return token;
38780
39217
  }
38781
39218
  return void 0;
38782
39219
  }
38783
- async function resolveBoundary(events, handlers, sessionId, toolContext) {
39220
+ async function resolveBoundary(events, handlers, sessionId, toolContext, trail) {
38784
39221
  const hookReqs = events.filter((e) => e.event === "hook_request");
38785
39222
  if (hookReqs.length > 0) {
38786
39223
  const results = {};
@@ -38827,7 +39264,9 @@ var require_segmentDriver = __commonJS({
38827
39264
  }
38828
39265
  const toolReqs = events.filter((e) => e.event === "tool_request");
38829
39266
  if (toolReqs.length > 0) {
38830
- const results = await runToolBatch(toolReqs.map((e) => e.data), (req) => handlers.runTool(req, sessionId, toolContext), handlers.signal);
39267
+ const batch = toolReqs.map((e) => e.data);
39268
+ trail.runsTools(batch);
39269
+ const results = await runToolBatch(batch, (req) => trail.watch(req, handlers.runTool(req, sessionId, toolContext)), handlers.signal);
38831
39270
  if (handlers.signal?.aborted)
38832
39271
  throw new DOMException("turn aborted", "AbortError");
38833
39272
  for (const result of results)
@@ -50954,6 +51393,7 @@ var require_agentManager = __commonJS({
50954
51393
  } });
50955
51394
  var agentNarration_2 = require_agentNarration();
50956
51395
  var askAnswers_js_1 = require_askAnswers();
51396
+ var interruptedTurn_js_1 = require_interruptedTurn();
50957
51397
  var awaitWake_js_1 = require_awaitWake();
50958
51398
  var agentDefs_js_1 = require_agentDefs();
50959
51399
  var dosePolicy_js_1 = require_dosePolicy();
@@ -51606,6 +52046,7 @@ ${(0, agentNarration_2.lastChars)(entry.partialText.trim(), 4e3)}`;
51606
52046
  async runChild(entry, opts) {
51607
52047
  let outcome;
51608
52048
  let failure;
52049
+ let left;
51609
52050
  try {
51610
52051
  const start2 = this.childSegmentStart(entry, opts);
51611
52052
  const handlers = this.childHandlers(entry);
@@ -51613,9 +52054,10 @@ ${(0, agentNarration_2.lastChars)(entry.partialText.trim(), 4e3)}`;
51613
52054
  transportRetry: {}
51614
52055
  });
51615
52056
  } catch (e) {
52057
+ left = (0, interruptedTurn_js_1.interruptedTurnOf)(e);
51616
52058
  failure = entry.status === "stopped" || entry.status === "lost" ? void 0 : e instanceof Error ? e.message : String(e);
51617
52059
  }
51618
- await this.settleChild(entry, outcome, failure);
52060
+ await this.settleChild(entry, outcome, failure, left);
51619
52061
  }
51620
52062
  childSegmentStart(entry, opts) {
51621
52063
  const { def, routing } = opts;
@@ -51765,7 +52207,7 @@ When done, reply with your findings \u2014 your FINAL MESSAGE is your report and
51765
52207
  * the worktree decision and `reportText` are final — a consumer that
51766
52208
  * observes a terminal status (agent_check, dashboards, harnesses polling
51767
52209
  * the registry) must never see half-settled terminal state. */
51768
- async settleChild(entry, outcome, failure) {
52210
+ async settleChild(entry, outcome, failure, left) {
51769
52211
  const wasStopped = entry.status === "stopped" || entry.status === "lost";
51770
52212
  const terminal = wasStopped ? entry.status : outcome?.kind === "turn_complete" ? "finished" : "failed";
51771
52213
  let summary;
@@ -51776,18 +52218,22 @@ When done, reply with your findings \u2014 your FINAL MESSAGE is your report and
51776
52218
  entry.costUsd += typeof data.cost_usd === "number" ? data.cost_usd : 0;
51777
52219
  summary = typeof data.assistant_message === "string" && data.assistant_message.trim() || entry.partialText.trim() || "(Sub-agent completed but returned no output.)";
51778
52220
  success = !wasStopped;
51779
- } else if (wasStopped) {
51780
- summary = entry.partialText.trim() ? `Stopped before finishing. Partial result:
52221
+ } else {
52222
+ const transcript = outcome ? outcome.event.data.messages : left?.messages;
52223
+ if (Array.isArray(transcript))
52224
+ entry.transcript = transcript;
52225
+ if (wasStopped) {
52226
+ summary = entry.partialText.trim() ? `Stopped before finishing. Partial result:
51781
52227
 
51782
52228
  ${entry.partialText.trim()}` : "Stopped before producing any output.";
51783
- success = false;
51784
- } else {
51785
- const error = failure ?? String(outcome?.event.data?.error ?? "turn failed");
51786
- summary = entry.partialText.trim() ? `${error}
52229
+ } else {
52230
+ const error = failure ?? String(outcome?.event.data?.error ?? "turn failed");
52231
+ summary = entry.partialText.trim() ? `${error}
51787
52232
 
51788
52233
  Partial result before the failure:
51789
52234
 
51790
52235
  ${entry.partialText.trim()}` : error;
52236
+ }
51791
52237
  success = false;
51792
52238
  }
51793
52239
  const worktreeNote = await this.cleanupWorktree(entry);
@@ -62379,20 +62825,13 @@ var require_flushGate = __commonJS({
62379
62825
  peek() {
62380
62826
  return this.queue;
62381
62827
  }
62382
- /** Close the gate and hand back the queued items to drain IN ORDER after the flush. */
62828
+ /** Close the gate and hand back the queued items IN ORDER. */
62383
62829
  open() {
62384
62830
  this.armed = false;
62385
62831
  const items = this.queue;
62386
62832
  this.queue = [];
62387
62833
  return items;
62388
62834
  }
62389
- /** Permanent close: discard and report how many items were lost. */
62390
- drop() {
62391
- this.armed = false;
62392
- const lost = this.queue.length;
62393
- this.queue = [];
62394
- return lost;
62395
- }
62396
62835
  };
62397
62836
  exports2.FlushGate = FlushGate;
62398
62837
  }
@@ -62519,6 +62958,7 @@ var require_mirror = __commonJS({
62519
62958
  // observes every event before this check.
62520
62959
  "tool_input_delta"
62521
62960
  ]);
62961
+ var ACK_KINDS = /* @__PURE__ */ new Set(["rc_ack", "rc_permission_resolved"]);
62522
62962
  function coalesceInto(batch, event, n) {
62523
62963
  const last = batch[batch.length - 1];
62524
62964
  if (event.event === "thinking_delta") {
@@ -62615,19 +63055,27 @@ var require_mirror = __commonJS({
62615
63055
  * and its ack must land regardless — the buffered channel replays it on attach. */
62616
63056
  emitControl(event) {
62617
63057
  if (this.phase === "dormant") {
62618
- this.chain = this.chain.then(() => this.deps.publish([event]).catch((error) => {
62619
- this.deps.reportOutcome({
62620
- kind: "rc_control_publish_failed",
62621
- session_id: this.sessionId,
62622
- error: String(error)
62623
- });
62624
- }));
63058
+ this.publishDirect([event]);
62625
63059
  return;
62626
63060
  }
62627
63061
  if (this.gate.enqueue(event))
62628
63062
  return;
62629
63063
  this.enqueueLive(event);
62630
63064
  }
63065
+ /** Publish outside the batch — sequenced through the SAME chain as backfills and flushes,
63066
+ * deliberately NOT generation-fenced (an ack must land even across a mirror reset). A
63067
+ * detached fire-and-forget POST here races a backfill that starts a beat later, and the
63068
+ * gateway stamps order by ARRIVAL: the losing interleaving renders a user's prompt AFTER
63069
+ * the answer it caused. */
63070
+ publishDirect(events) {
63071
+ this.chain = this.chain.then(() => this.deps.publish(events).catch((error) => {
63072
+ this.deps.reportOutcome({
63073
+ kind: "rc_control_publish_failed",
63074
+ session_id: this.sessionId,
63075
+ error: String(error)
63076
+ });
63077
+ }));
63078
+ }
62631
63079
  /** A viewer attached (the instant signal). `backfill` is the viewer's word on what it
62632
63080
  * needs: `true` (every phone before the gateway-held transcript, and any signal that
62633
63081
  * does not say) earns a fresh backfill — while LIVE too, a re-attaching phone's state
@@ -62815,17 +63263,9 @@ var require_mirror = __commonJS({
62815
63263
  clearTimeout(this.timer);
62816
63264
  this.timer = null;
62817
63265
  }
62818
- const lostGate = this.gate.drop();
62819
- const lostBatch = this.batch.length;
63266
+ const held = [...this.gate.open(), ...this.batch];
62820
63267
  this.batch = [];
62821
- if (lostGate + lostBatch > 0) {
62822
- this.deps.reportOutcome({
62823
- kind: "rc_mirror_dropped",
62824
- session_id: this.sessionId,
62825
- reason,
62826
- events: lostGate + lostBatch
62827
- });
62828
- }
63268
+ this.salvageAcks(held, reason);
62829
63269
  }
62830
63270
  startBackfill() {
62831
63271
  this.generation += 1;
@@ -62934,12 +63374,7 @@ var require_mirror = __commonJS({
62934
63374
  const generation = this.generation;
62935
63375
  this.chain = this.chain.then(async () => {
62936
63376
  if (generation !== this.generation) {
62937
- this.deps.reportOutcome({
62938
- kind: "rc_mirror_dropped",
62939
- session_id: this.sessionId,
62940
- reason: "superseded_in_flight",
62941
- events: events.length
62942
- });
63377
+ this.salvageAcks(events, "superseded_in_flight");
62943
63378
  return;
62944
63379
  }
62945
63380
  try {
@@ -62953,10 +63388,28 @@ var require_mirror = __commonJS({
62953
63388
  events: events.length,
62954
63389
  error: String(error)
62955
63390
  });
63391
+ this.salvageAcks(events);
62956
63392
  this.goDormant("publish_failed");
62957
63393
  }
62958
63394
  });
62959
63395
  }
63396
+ /** Events that will not reach the wire the way they were meant to: the acks among them are
63397
+ * published on their own (`ACK_KINDS`), the rest is counted as loss under `reason` — or
63398
+ * left to the caller's own accounting when no reason is given. */
63399
+ salvageAcks(events, reason) {
63400
+ const acks = events.filter((event) => ACK_KINDS.has(event.event));
63401
+ if (acks.length > 0)
63402
+ this.publishDirect(acks);
63403
+ const lost = events.length - acks.length;
63404
+ if (reason !== void 0 && lost > 0) {
63405
+ this.deps.reportOutcome({
63406
+ kind: "rc_mirror_dropped",
63407
+ session_id: this.sessionId,
63408
+ reason,
63409
+ events: lost
63410
+ });
63411
+ }
63412
+ }
62960
63413
  };
62961
63414
  exports2.SessionMirror = SessionMirror;
62962
63415
  function chunk(items, size) {
@@ -63106,6 +63559,17 @@ var require_hostBridge = __commonJS({
63106
63559
  var mirror_js_1 = require_mirror();
63107
63560
  var rcSettings_js_1 = require_rcSettings();
63108
63561
  var types_js_1 = require_types();
63562
+ function mailboxStampOf(event) {
63563
+ const data = event.data;
63564
+ const stamp = data?.mailbox;
63565
+ if (typeof stamp !== "object" || stamp === null)
63566
+ return void 0;
63567
+ const { origin, seq } = stamp;
63568
+ if (typeof origin !== "string" || origin === "" || typeof seq !== "number")
63569
+ return void 0;
63570
+ return { origin, seq };
63571
+ }
63572
+ var MAILBOX_ORIGINS_MAX = 32;
63109
63573
  function isFenceRefusal(message) {
63110
63574
  const normalized = message.toLowerCase().replace(/[_-]/g, " ");
63111
63575
  return normalized.includes("stale epoch") || normalized.includes("not registered");
@@ -63204,6 +63668,9 @@ var require_hostBridge = __commonJS({
63204
63668
  const announced = /* @__PURE__ */ new Set();
63205
63669
  let beatWaiters = [];
63206
63670
  let mailboxCursor;
63671
+ let streamGeneration;
63672
+ const mailboxOrigins = /* @__PURE__ */ new Map();
63673
+ let streamBlockedSince = null;
63207
63674
  let heartbeatTimer = null;
63208
63675
  let debounceTimer = null;
63209
63676
  let lastTickAt = null;
@@ -63506,12 +63973,47 @@ var require_hostBridge = __commonJS({
63506
63973
  if (!watchFramed && epoch !== null) {
63507
63974
  watchFramed = true;
63508
63975
  linkState = "seen";
63976
+ if (streamBlockedSince !== null) {
63977
+ report({ kind: "rc_stream_restored", blocked_ms: clock() - streamBlockedSince });
63978
+ streamBlockedSince = null;
63979
+ }
63509
63980
  setStatus({ phase: "connected" });
63510
63981
  }
63511
63982
  const event = raw;
63983
+ if (event.event === "stream_status") {
63984
+ const generation2 = event.data?.generation;
63985
+ if (typeof generation2 === "string" && generation2 !== "" && generation2 !== streamGeneration) {
63986
+ if (streamGeneration !== void 0)
63987
+ mailboxCursor = void 0;
63988
+ streamGeneration = generation2;
63989
+ }
63990
+ }
63512
63991
  if (typeof event.seq === "number" && (mailboxCursor === void 0 || event.seq > mailboxCursor)) {
63513
63992
  mailboxCursor = event.seq;
63514
63993
  }
63994
+ applyControlFrame(event);
63995
+ }
63996
+ function applyControlFrame(event) {
63997
+ const stamp = mailboxStampOf(event);
63998
+ if (stamp !== void 0) {
63999
+ const known = mailboxOrigins.get(stamp.origin);
64000
+ mailboxOrigins.set(stamp.origin, {
64001
+ seq: Math.max(stamp.seq, known?.seq ?? 0),
64002
+ seenAtMs: clock()
64003
+ });
64004
+ if (mailboxOrigins.size > MAILBOX_ORIGINS_MAX) {
64005
+ let oldest;
64006
+ let oldestAt = Number.POSITIVE_INFINITY;
64007
+ for (const [origin, { seenAtMs }] of mailboxOrigins) {
64008
+ if (seenAtMs < oldestAt) {
64009
+ oldest = origin;
64010
+ oldestAt = seenAtMs;
64011
+ }
64012
+ }
64013
+ if (oldest !== void 0)
64014
+ mailboxOrigins.delete(oldest);
64015
+ }
64016
+ }
63515
64017
  if (event.event === "rc_command") {
63516
64018
  const stamped = event.data.epoch;
63517
64019
  if (typeof stamped === "number" && epoch !== null && stamped !== epoch) {
@@ -63536,6 +64038,22 @@ var require_hostBridge = __commonJS({
63536
64038
  }
63537
64039
  }
63538
64040
  }
64041
+ function mailboxCursorsForBeat() {
64042
+ const cursors = {};
64043
+ for (const [origin, { seq }] of mailboxOrigins)
64044
+ cursors[origin] = seq;
64045
+ return cursors;
64046
+ }
64047
+ function applyMailbox(mailbox) {
64048
+ if (!Array.isArray(mailbox) || mailbox.length === 0)
64049
+ return;
64050
+ const frames = mailbox.filter((frame) => typeof frame === "object" && frame !== null && typeof frame.event === "string");
64051
+ if (frames.length === 0)
64052
+ return;
64053
+ report({ kind: "rc_mailbox_read", frames: frames.length });
64054
+ for (const frame of frames)
64055
+ applyControlFrame(frame);
64056
+ }
63539
64057
  function teardownChannel() {
63540
64058
  controlWatch?.dispose();
63541
64059
  controlWatch = null;
@@ -63546,9 +64064,23 @@ var require_hostBridge = __commonJS({
63546
64064
  watchFramed = false;
63547
64065
  watchOpenedAtMs = clock();
63548
64066
  controlWatch = deps2.bridge.watchSession(channel, onControlEvent, {
63549
- since: () => mailboxCursor
64067
+ since: () => mailboxCursor,
64068
+ generation: () => streamGeneration
63550
64069
  });
63551
64070
  }
64071
+ function judgeStream(linked) {
64072
+ if (watchFramed || linked === void 0 || controlWatch === null) {
64073
+ setStatus({ phase: "connected" });
64074
+ return;
64075
+ }
64076
+ if (watchAttaching())
64077
+ return;
64078
+ if (streamBlockedSince === null) {
64079
+ streamBlockedSince = clock();
64080
+ report({ kind: "rc_stream_blocked" });
64081
+ }
64082
+ setStatus({ phase: "stream_blocked", sinceMs: streamBlockedSince });
64083
+ }
63552
64084
  function watchAttaching() {
63553
64085
  return !watchFramed && clock() - watchOpenedAtMs < tuning.heartbeatMs / 2;
63554
64086
  }
@@ -63863,7 +64395,11 @@ var require_hostBridge = __commonJS({
63863
64395
  const result = await beatRpc("rc_heartbeat", {
63864
64396
  host_id: hostId,
63865
64397
  epoch: myEpoch,
63866
- sessions: upserts
64398
+ sessions: upserts,
64399
+ // The heartbeat lane's two words: what this host has received off its mailbox, and
64400
+ // whether its stream delivers at all (what the phones are told beside this machine).
64401
+ mailbox_cursors: mailboxCursorsForBeat(),
64402
+ push_blocked: streamBlockedSince !== null
63867
64403
  });
63868
64404
  if (superseded())
63869
64405
  return;
@@ -63907,8 +64443,9 @@ var require_hostBridge = __commonJS({
63907
64443
  relinked = false;
63908
64444
  linkState = "seen";
63909
64445
  resetLinkFailures();
63910
- setStatus({ phase: "connected" });
64446
+ judgeStream(result.linked);
63911
64447
  }
64448
+ applyMailbox(result.mailbox);
63912
64449
  applyWatching(result.watching);
63913
64450
  } catch (error) {
63914
64451
  if (superseded())
@@ -64228,7 +64765,7 @@ var require_hostBridge = __commonJS({
64228
64765
  tick();
64229
64766
  },
64230
64767
  canAnswerPermissions(sessionId) {
64231
- return status.phase === "connected" && settings.enabled && sessions.has(sessionId) && !hidden(sessionId);
64768
+ return (status.phase === "connected" || status.phase === "stream_blocked") && settings.enabled && sessions.has(sessionId) && !hidden(sessionId);
64232
64769
  },
64233
64770
  status: () => status,
64234
64771
  onStatusChange(listener) {
@@ -64541,6 +65078,13 @@ var require_statusText = __commonJS({
64541
65078
  return { label: "connecting", detail: null, action: null, severity: "ok" };
64542
65079
  case "connected":
64543
65080
  return { label: "connected", detail: null, action: null, severity: "ok" };
65081
+ case "stream_blocked":
65082
+ return {
65083
+ label: "connected by heartbeat",
65084
+ detail: null,
65085
+ action: "Orion's live stream isn't reaching this machine \u2014 a proxy or VPN on this network is holding it \u2014 so your phone's messages arrive with its heartbeat, up to about twenty seconds later.",
65086
+ severity: "transport"
65087
+ };
64544
65088
  case "reconnecting":
64545
65089
  return { label: "reconnecting", detail: status.reason, action: null, severity: "transport" };
64546
65090
  case "failed":
@@ -79398,7 +79942,7 @@ var require_toolExecutor = __commonJS({
79398
79942
  };
79399
79943
  }();
79400
79944
  Object.defineProperty(exports2, "__esModule", { value: true });
79401
- exports2.ToolExecutor = exports2.STOPPED_COMMAND_STATUS = exports2.DETACHED_PROCESS_NOTE = exports2.DETACHED_OUTPUT_DRAIN_MS = exports2.SUBPROCESS_DEFAULT_TIMEOUT_S = exports2.UNBOUNDED_COMMAND_TIMEOUTS = exports2.DESKTOP_COMMAND_TIMEOUTS = void 0;
79945
+ exports2.ToolExecutor = exports2.STOPPED_WITH_TURN_NOTE = exports2.STOPPED_COMMAND_STATUS = exports2.DETACHED_PROCESS_NOTE = exports2.DETACHED_OUTPUT_DRAIN_MS = exports2.SUBPROCESS_DEFAULT_TIMEOUT_S = exports2.UNBOUNDED_COMMAND_TIMEOUTS = exports2.DESKTOP_COMMAND_TIMEOUTS = void 0;
79402
79946
  exports2.enhancedSearchStamp = enhancedSearchStamp;
79403
79947
  exports2.judgeSearchStamp = judgeSearchStamp;
79404
79948
  exports2.formatErrorDiagnostics = formatErrorDiagnostics;
@@ -79780,16 +80324,50 @@ ${exports2.DETACHED_PROCESS_NOTE}`;
79780
80324
  onChunk?.(note, "status");
79781
80325
  return note;
79782
80326
  }
79783
- function endWithTurn(proc, signal) {
80327
+ function endWithTurn(proc, signal, onEnded) {
79784
80328
  if (signal === void 0)
79785
80329
  return;
79786
- const stop2 = () => void (0, processHardening_1.terminateChildTree)(proc);
80330
+ const stop2 = () => {
80331
+ onEnded?.();
80332
+ void (0, processHardening_1.terminateChildTree)(proc);
80333
+ };
79787
80334
  signal.addEventListener("abort", stop2, { once: true });
79788
80335
  const release = () => signal.removeEventListener("abort", stop2);
79789
80336
  proc.once("exit", release);
79790
80337
  proc.once("error", release);
79791
80338
  }
79792
80339
  exports2.STOPPED_COMMAND_STATUS = "\n(exit null)";
80340
+ exports2.STOPPED_WITH_TURN_NOTE = "(the turn was stopped while this command was running \u2014 it did not finish)";
80341
+ var CommandStop = class {
80342
+ signal;
80343
+ ended = false;
80344
+ constructor(signal) {
80345
+ this.signal = signal;
80346
+ }
80347
+ /** End `proc` with the turn ({@link endWithTurn}). */
80348
+ watch(proc) {
80349
+ endWithTurn(proc, this.signal, () => {
80350
+ this.ended = true;
80351
+ });
80352
+ }
80353
+ /** Whether the stop landed while the command's process was alive. */
80354
+ get endedCommand() {
80355
+ return this.ended;
80356
+ }
80357
+ };
80358
+ var EXIT_TRAILER = /\n?(\(exit (?:null|-?\d+)\)\s*)$/;
80359
+ function stoppedWithTurn(result, stop2) {
80360
+ if (!stop2.endedCommand)
80361
+ return result;
80362
+ const trailer = EXIT_TRAILER.exec(result);
80363
+ if (trailer === null)
80364
+ return result;
80365
+ const printed = result.slice(0, trailer.index);
80366
+ const lead = printed === "" ? "" : `${printed}
80367
+ `;
80368
+ return `${lead}${exports2.STOPPED_WITH_TURN_NOTE}
80369
+ ${trailer[1]}`;
80370
+ }
79793
80371
  function openCommandCard(sink, sessionId, toolKey, command, signal) {
79794
80372
  if (sink === null || signal?.aborted)
79795
80373
  return { feed: void 0, close: () => void 0 };
@@ -81304,18 +81882,19 @@ exit $__orion_rc`;
81304
81882
  const sandbox = this.sandboxForParams(params, sessionId);
81305
81883
  const card = openCommandCard(params._agent_id ? null : this.termSink, sessionId, reqId, command, context.signal);
81306
81884
  try {
81307
- const out2 = await this.runBash(wrapped, resolveCommandTimeoutS(params.timeout_s, this.commandTimeouts), effectiveCwd, card.feed, false, sandbox, command, context.signal);
81885
+ const stop2 = new CommandStop(context.signal);
81886
+ const out2 = await this.runBash(wrapped, resolveCommandTimeoutS(params.timeout_s, this.commandTimeouts), effectiveCwd, card.feed, false, sandbox, command, stop2);
81308
81887
  if (baseline) {
81309
81888
  const changes = await (0, bashFileChanges_1.diffAgainstBaseline)(baseline);
81310
81889
  if (changes.length > 0)
81311
81890
  this.pendingBashChanges.set(reqId, changes);
81312
81891
  }
81313
- const compressed = (0, terminalOutputCompression_1.compressTerminalOutput)({
81892
+ const compressed = stoppedWithTurn((0, terminalOutputCompression_1.compressTerminalOutput)({
81314
81893
  toolName: req.name,
81315
81894
  command,
81316
81895
  output: out2,
81317
81896
  tokenSaving: context.tokenSaving
81318
- });
81897
+ }), stop2);
81319
81898
  if (sandbox && /\(exit \d+\)\s*$/.test(out2) && (0, bashSandbox_1.looksLikeSandboxDenial)(out2)) {
81320
81899
  return compressed + bashSandbox_1.SANDBOX_RETRY_HINT;
81321
81900
  }
@@ -81376,18 +81955,19 @@ exit $__orion_rc`;
81376
81955
  const baseline = detectRoot && effectiveCwd && !inScratch ? await (0, bashFileChanges_1.captureBaseline)(effectiveCwd, detectRoot) : null;
81377
81956
  const card = openCommandCard(params._agent_id ? null : this.termSink, sessionId, reqId, command, context.signal);
81378
81957
  try {
81379
- const out2 = await this.runPowershell(wrapped, resolveCommandTimeoutS(params.timeout_s, this.commandTimeouts), effectiveCwd, card.feed, context.signal);
81958
+ const stop2 = new CommandStop(context.signal);
81959
+ const out2 = await this.runPowershell(wrapped, resolveCommandTimeoutS(params.timeout_s, this.commandTimeouts), effectiveCwd, card.feed, stop2);
81380
81960
  if (baseline) {
81381
81961
  const changes = await (0, bashFileChanges_1.diffAgainstBaseline)(baseline);
81382
81962
  if (changes.length > 0)
81383
81963
  this.pendingBashChanges.set(reqId, changes);
81384
81964
  }
81385
- return (0, terminalOutputCompression_1.compressTerminalOutput)({
81965
+ return stoppedWithTurn((0, terminalOutputCompression_1.compressTerminalOutput)({
81386
81966
  toolName: req.name,
81387
81967
  command,
81388
81968
  output: out2,
81389
81969
  tokenSaving: context.tokenSaving
81390
- });
81970
+ }), stop2);
81391
81971
  } finally {
81392
81972
  card.close();
81393
81973
  if (cwdFile) {
@@ -81815,8 +82395,8 @@ exit $__orion_rc`;
81815
82395
  /** Spawn one PowerShell command. Shares `runBash`'s streaming, truncation,
81816
82396
  * exit-marker and turn-stop conventions so the model reads the same shape of
81817
82397
  * result from either shell; the interpreter and its flags are the only difference. */
81818
- async runPowershell(command, timeoutS, cwd, onChunk, signal) {
81819
- if (signal?.aborted)
82398
+ async runPowershell(command, timeoutS, cwd, onChunk, stop2) {
82399
+ if (stop2?.signal?.aborted)
81820
82400
  throw new DOMException("turn aborted", "AbortError");
81821
82401
  const inv = (0, powershellResolver_1.powershellInvocation)(command);
81822
82402
  return await new Promise((resolve5, reject) => {
@@ -81840,7 +82420,7 @@ exit $__orion_rc`;
81840
82420
  proc.stdout?.on("data", take);
81841
82421
  proc.stderr?.on("data", take);
81842
82422
  (0, processHardening_1.trackChild)(proc);
81843
- endWithTurn(proc, signal);
82423
+ stop2?.watch(proc);
81844
82424
  let timedOut = false;
81845
82425
  const timer = timeoutS === null ? null : setTimeout(() => {
81846
82426
  timedOut = true;
@@ -81873,8 +82453,8 @@ exit $__orion_rc`;
81873
82453
  });
81874
82454
  });
81875
82455
  }
81876
- async runBash(command, timeoutS, cwd, onChunk, partialOnTimeout = false, sandbox, label = command, signal) {
81877
- if (signal?.aborted)
82456
+ async runBash(command, timeoutS, cwd, onChunk, partialOnTimeout = false, sandbox, label = command, stop2) {
82457
+ if (stop2?.signal?.aborted)
81878
82458
  throw new DOMException("turn aborted", "AbortError");
81879
82459
  const inv = sandbox && sandbox.capability ? (0, bashSandbox_1.buildSandboxedInvocation)(command, {
81880
82460
  capability: sandbox.capability,
@@ -81918,7 +82498,7 @@ exit $__orion_rc`;
81918
82498
  this.shellChildren.set(proc, label);
81919
82499
  proc.once("exit", () => this.shellChildren.delete(proc));
81920
82500
  proc.once("error", () => this.shellChildren.delete(proc));
81921
- endWithTurn(proc, signal);
82501
+ stop2?.watch(proc);
81922
82502
  let timedOut = false;
81923
82503
  const timer = timeoutS === null ? null : setTimeout(() => {
81924
82504
  timedOut = true;
@@ -87230,6 +87810,7 @@ var require_loopRunner = __commonJS({
87230
87810
  var askAnswers_js_1 = require_askAnswers();
87231
87811
  var anchorDrift_js_1 = require_anchorDrift();
87232
87812
  var segmentDriver_js_1 = require_segmentDriver();
87813
+ var turnClose_js_1 = require_turnClose();
87233
87814
  var common_js_1 = require_common();
87234
87815
  var loopEnvelope_js_1 = require_loopEnvelope();
87235
87816
  var loopRuns_js_1 = require_loopRuns();
@@ -87412,27 +87993,34 @@ var require_loopRunner = __commonJS({
87412
87993
  let text = "";
87413
87994
  let failure = "";
87414
87995
  try {
87415
- const outcome = await (0, segmentDriver_js_1.driveTurn)(this.deps.transport, {
87416
- sessionId,
87417
- ...decl.skill ? { skill: { name: decl.skill, args: "" } } : { text: decl.prompt },
87418
- getMessages: () => [],
87419
- routing,
87420
- // No fan-out, no scheduling, no work graphs: a standing loop is one agent.
87421
- clientCapabilities: []
87422
- }, this.handlers(decl, run3, budget, sessionId, (question) => {
87423
- parkedQuestion = question;
87424
- void this.deps.store.save({
87425
- ...base,
87426
- status: "parked",
87427
- session_id: sessionId,
87428
- cost_usd: budget.spendUsd,
87429
- parked: question,
87430
- summary: `waiting on you: ${(0, common_js_1.clipTail)(question.question, 160)}`
87431
- });
87432
- if (decl.notify.on.includes("parked")) {
87433
- this.deps.notify?.(`[loop ${decl.name}] waiting on you: ${question.question}`);
87434
- }
87435
- }));
87996
+ const outcome = await (0, segmentDriver_js_1.driveTurn)(
87997
+ this.deps.transport,
87998
+ {
87999
+ sessionId,
88000
+ ...decl.skill ? { skill: { name: decl.skill, args: "" } } : { text: decl.prompt },
88001
+ getMessages: () => [],
88002
+ routing,
88003
+ // No fan-out, no scheduling, no work graphs: a standing loop is one agent.
88004
+ clientCapabilities: []
88005
+ },
88006
+ this.handlers(decl, run3, budget, sessionId, (question) => {
88007
+ parkedQuestion = question;
88008
+ void this.deps.store.save({
88009
+ ...base,
88010
+ status: "parked",
88011
+ session_id: sessionId,
88012
+ cost_usd: budget.spendUsd,
88013
+ parked: question,
88014
+ summary: `waiting on you: ${(0, common_js_1.clipTail)(question.question, 160)}`
88015
+ });
88016
+ if (decl.notify.on.includes("parked")) {
88017
+ this.deps.notify?.(`[loop ${decl.name}] waiting on you: ${question.question}`);
88018
+ }
88019
+ }),
88020
+ // A run starts from nothing and its session is closed behind it: nothing reads what a
88021
+ // run that was stopped left.
88022
+ turnClose_js_1.CLOSE_TURNS_IN_BACKGROUND
88023
+ );
87436
88024
  if (outcome.kind === "turn_complete") {
87437
88025
  const data = outcome.event.data;
87438
88026
  text = typeof data.assistant_message === "string" ? data.assistant_message : "";
@@ -88833,7 +89421,7 @@ var require_dist = __commonJS({
88833
89421
  for (var p in m2) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m2, p);
88834
89422
  };
88835
89423
  Object.defineProperty(exports2, "__esModule", { value: true });
88836
- exports2.safeSpawnCwd = exports2.gitFrom = exports2.reconcileMemoryIndex = exports2.memoryRootsFor = exports2.memoryRootOf = exports2.isAbsolutePath = exports2.frontmatterField = exports2.MEMORY_INDEX_FILENAME = exports2.watchUserSettingsFile = void 0;
89424
+ exports2.safeSpawnCwd = exports2.gitFrom = exports2.reconcileMemoryIndex = exports2.memoryRootsFor = exports2.memoryRootOf = exports2.isAbsolutePath = exports2.frontmatterField = exports2.MEMORY_INDEX_FILENAME = exports2.watchUserSettingsFile = exports2.TURN_CLOSE_WAIT_MS = exports2.TURN_CLOSE_TIMEOUT_MS = exports2.STOPPED_TOOL_GRACE_MS = exports2.CLOSE_TURNS_IN_BACKGROUND = void 0;
88837
89425
  __exportStar(require_shims(), exports2);
88838
89426
  __exportStar(require_protocol(), exports2);
88839
89427
  __exportStar(require_askAnswers(), exports2);
@@ -88863,6 +89451,21 @@ var require_dist = __commonJS({
88863
89451
  __exportStar(require_migrateChat(), exports2);
88864
89452
  __exportStar(require_streamHealth(), exports2);
88865
89453
  __exportStar(require_segmentDriver(), exports2);
89454
+ __exportStar(require_interruptedTurn(), exports2);
89455
+ var turnClose_1 = require_turnClose();
89456
+ Object.defineProperty(exports2, "CLOSE_TURNS_IN_BACKGROUND", { enumerable: true, get: function() {
89457
+ return turnClose_1.CLOSE_TURNS_IN_BACKGROUND;
89458
+ } });
89459
+ Object.defineProperty(exports2, "STOPPED_TOOL_GRACE_MS", { enumerable: true, get: function() {
89460
+ return turnClose_1.STOPPED_TOOL_GRACE_MS;
89461
+ } });
89462
+ Object.defineProperty(exports2, "TURN_CLOSE_TIMEOUT_MS", { enumerable: true, get: function() {
89463
+ return turnClose_1.TURN_CLOSE_TIMEOUT_MS;
89464
+ } });
89465
+ Object.defineProperty(exports2, "TURN_CLOSE_WAIT_MS", { enumerable: true, get: function() {
89466
+ return turnClose_1.TURN_CLOSE_WAIT_MS;
89467
+ } });
89468
+ __exportStar(require_planState(), exports2);
88866
89469
  __exportStar(require_queuedInput(), exports2);
88867
89470
  __exportStar(require_heldInputPen(), exports2);
88868
89471
  __exportStar(require_customProviders(), exports2);
@@ -89172,31 +89775,6 @@ var require_clientState = __commonJS({
89172
89775
  }
89173
89776
  });
89174
89777
 
89175
- // ../packages/orion-client-ui-core/dist/interruptedTurn.js
89176
- var require_interruptedTurn = __commonJS({
89177
- "../packages/orion-client-ui-core/dist/interruptedTurn.js"(exports2) {
89178
- "use strict";
89179
- Object.defineProperty(exports2, "__esModule", { value: true });
89180
- exports2.MOVED_ON_NOTE = void 0;
89181
- exports2.interruptedTurnMessages = interruptedTurnMessages2;
89182
- var textBlock = (text) => ({ type: "text", text });
89183
- exports2.MOVED_ON_NOTE = "[Reply cut off here \u2014 the user sent their next message before it finished. That message is what they want now: pick up from this point as it directs, without restarting this reply or remarking on the cut.]";
89184
- function interruptedTurnMessages2(messages, prompt, partialReply, cause = "stopped") {
89185
- const out2 = [...messages];
89186
- if (prompt && prompt.trim().length > 0) {
89187
- out2.push({ role: "user", content: [textBlock(prompt)] });
89188
- }
89189
- if (partialReply && partialReply.trim().length > 0) {
89190
- const content = [textBlock(partialReply)];
89191
- if (cause === "moved_on")
89192
- content.push(textBlock(exports2.MOVED_ON_NOTE));
89193
- out2.push({ role: "assistant", content });
89194
- }
89195
- return out2;
89196
- }
89197
- }
89198
- });
89199
-
89200
89778
  // ../packages/orion-client-ui-core/dist/chatFork.js
89201
89779
  var require_chatFork = __commonJS({
89202
89780
  "../packages/orion-client-ui-core/dist/chatFork.js"(exports2) {
@@ -93094,7 +93672,7 @@ var require_progress = __commonJS({
93094
93672
  "../packages/orion-client-ui-core/dist/progress.js"(exports2) {
93095
93673
  "use strict";
93096
93674
  Object.defineProperty(exports2, "__esModule", { value: true });
93097
- exports2.STALLED_LABEL = exports2.STALL_AFTER_MS = exports2.THINKING_STAGES_MS = exports2.WAITING_FOR_PREVIOUS_TURN = void 0;
93675
+ exports2.STALLED_LABEL = exports2.STALL_AFTER_MS = exports2.THINKING_STAGES_MS = exports2.STOPPING_TURN = exports2.WAITING_FOR_PREVIOUS_TURN = void 0;
93098
93676
  exports2.formatMeterElapsed = formatMeterElapsed3;
93099
93677
  exports2.formatMeterTokens = formatMeterTokens;
93100
93678
  exports2.progressMeter = progressMeter;
@@ -93102,6 +93680,7 @@ var require_progress = __commonJS({
93102
93680
  exports2.thinkingLabel = thinkingLabel;
93103
93681
  exports2.isStalled = isStalled;
93104
93682
  exports2.WAITING_FOR_PREVIOUS_TURN = "Waiting for the previous turn to finish\u2026";
93683
+ exports2.STOPPING_TURN = "Stopping\u2026";
93105
93684
  function formatMeterElapsed3(ms) {
93106
93685
  const total = Math.max(0, Math.floor(ms / 1e3));
93107
93686
  const h = Math.floor(total / 3600);
@@ -93549,6 +94128,107 @@ var require_pasteRefs = __commonJS({
93549
94128
  }
93550
94129
  });
93551
94130
 
94131
+ // ../packages/orion-client-ui-core/dist/providerFailure.js
94132
+ var require_providerFailure = __commonJS({
94133
+ "../packages/orion-client-ui-core/dist/providerFailure.js"(exports2) {
94134
+ "use strict";
94135
+ Object.defineProperty(exports2, "__esModule", { value: true });
94136
+ exports2.providerWords = providerWords;
94137
+ exports2.plainLine = plainLine;
94138
+ exports2.readEndpointFailure = readEndpointFailure;
94139
+ var toolLog_1 = require_toolLog();
94140
+ var ADAPTER_PREFIX = /provider request failed:\s*/i;
94141
+ var RELAY_WORDS = /^gateway (auth refused|rate limit|unavailable|stream)\b/i;
94142
+ function providerWords(text) {
94143
+ const m2 = ADAPTER_PREFIX.exec(text);
94144
+ if (!m2)
94145
+ return null;
94146
+ const words = text.slice(m2.index + m2[0].length);
94147
+ return RELAY_WORDS.test(words) ? null : words;
94148
+ }
94149
+ var HIDDEN = /[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2060-\u206f\ufeff]/g;
94150
+ function plainLine(text, max) {
94151
+ return (0, toolLog_1.clip)(text.replace(HIDDEN, " "), max);
94152
+ }
94153
+ var DETAIL_MAX = 200;
94154
+ function messageIn(value, depth) {
94155
+ if (typeof value === "string")
94156
+ return value.trim() || null;
94157
+ if (depth > 3 || value === null || typeof value !== "object" || Array.isArray(value))
94158
+ return null;
94159
+ const record = value;
94160
+ for (const key of ["message", "detail", "error"]) {
94161
+ const found = messageIn(record[key], depth + 1);
94162
+ if (found !== null)
94163
+ return found;
94164
+ }
94165
+ return null;
94166
+ }
94167
+ var TITLE_SCAN = 4096;
94168
+ function bodyMessage(body2, status) {
94169
+ const raw = body2.trim();
94170
+ if (!raw)
94171
+ return "";
94172
+ let found = null;
94173
+ try {
94174
+ found = messageIn(JSON.parse(raw), 0);
94175
+ } catch {
94176
+ }
94177
+ if (found === null) {
94178
+ const title = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(raw.slice(0, TITLE_SCAN));
94179
+ found = title ? title[1] : raw.startsWith("<") ? "" : raw;
94180
+ }
94181
+ return plainLine(found.replace(new RegExp(`^\\s*${status}\\b[\\s:-]*`), ""), DETAIL_MAX);
94182
+ }
94183
+ var HTTP_ANSWER = /^(\d{3})(?:[ \t]+([^:\r\n]*))?:[ \t]*([\s\S]*)$/;
94184
+ var STREAMED_ERROR = /^(?:openai responses|openai|anthropic) error \(([^)]*)\):\s*([\s\S]*)$/i;
94185
+ function answerDetail(status, words) {
94186
+ if (status === null)
94187
+ return words || null;
94188
+ return words ? `HTTP ${status}: ${words}` : `HTTP ${status}`;
94189
+ }
94190
+ var CONNECT_FAILURE = /^error sending request for url \(([^)\s]*)\)([\s\S]*)$/i;
94191
+ var RELAY_ROUTE = /\/llm\/v1\/stream(?:[?#]|$)/i;
94192
+ var TRANSPORT_FRAMING = /^(client error \(\w+\)|tcp connect error|dns error|error trying to connect|connection error)$/i;
94193
+ function reachDetail(url, chain) {
94194
+ let where = "";
94195
+ try {
94196
+ const origin = new URL(url).origin;
94197
+ where = origin === "null" ? "" : origin;
94198
+ } catch {
94199
+ }
94200
+ const cause = chain.split(/:\s+/).map((part) => part.trim()).find((part) => part.length > 0 && !TRANSPORT_FRAMING.test(part));
94201
+ const why = cause ? plainLine(cause.replace(/\s*\(os error \d+\)$/i, "").replace(/[.]$/, ""), 120) : "";
94202
+ if (why && where)
94203
+ return `${why} at ${where}`;
94204
+ return why || where || null;
94205
+ }
94206
+ var RELAYED = /^provider request failed:/i;
94207
+ function readEndpointFailure(said) {
94208
+ if (RELAYED.test(said))
94209
+ return null;
94210
+ const reach = CONNECT_FAILURE.exec(said);
94211
+ if (reach) {
94212
+ return RELAY_ROUTE.test(reach[1]) ? null : { shape: "unreachable", detail: reachDetail(reach[1], reach[2]) };
94213
+ }
94214
+ const http = HTTP_ANSWER.exec(said);
94215
+ if (http) {
94216
+ const status2 = Number(http[1]);
94217
+ const message2 = bodyMessage(http[3], http[1]);
94218
+ return { shape: "answer", status: status2, kind: "", message: message2, detail: answerDetail(status2, message2 || plainLine(http[2] ?? "", 80)) };
94219
+ }
94220
+ const streamed = STREAMED_ERROR.exec(said);
94221
+ if (!streamed)
94222
+ return null;
94223
+ const code = streamed[1].trim();
94224
+ const status = /^\d{3}$/.test(code) ? Number(code) : null;
94225
+ const kind = status === null ? code : "";
94226
+ const message = plainLine(streamed[2], DETAIL_MAX);
94227
+ return { shape: "answer", status, kind, message, detail: answerDetail(status, message || plainLine(kind, 80)) };
94228
+ }
94229
+ }
94230
+ });
94231
+
93552
94232
  // ../packages/orion-client-ui-core/dist/turnErrors.js
93553
94233
  var require_turnErrors = __commonJS({
93554
94234
  "../packages/orion-client-ui-core/dist/turnErrors.js"(exports2) {
@@ -93556,6 +94236,7 @@ var require_turnErrors = __commonJS({
93556
94236
  Object.defineProperty(exports2, "__esModule", { value: true });
93557
94237
  exports2.describeTurnError = describeTurnError3;
93558
94238
  var client_core_1 = require_dist();
94239
+ var providerFailure_1 = require_providerFailure();
93559
94240
  var HINTS = {
93560
94241
  retry: "Send it again to retry.",
93561
94242
  pick_model: "Pick another model.",
@@ -93565,6 +94246,8 @@ var require_turnErrors = __commonJS({
93565
94246
  compact: "Compact the conversation, or start a new chat.",
93566
94247
  none: null
93567
94248
  };
94249
+ var CONTEXT_TOO_LONG = /context (window )?(too large|exceeded)|prompt is too long|too many tokens|exceeds the model's context/i;
94250
+ var BUSY = /rate.?limit|too many requests|\b429\b|overloaded|\b529\b/i;
93568
94251
  function providerDetail(text) {
93569
94252
  const m2 = /provider reported an error:\s*(.+)$/i.exec(text);
93570
94253
  return m2 ? m2[1].trim() : null;
@@ -93576,6 +94259,51 @@ var require_turnErrors = __commonJS({
93576
94259
  function capitalise(text) {
93577
94260
  return text.length === 0 ? text : text[0].toUpperCase() + text.slice(1);
93578
94261
  }
94262
+ function endpointOf(ctx) {
94263
+ const name2 = (0, client_core_1.parseCustomProviderRef)(ctx.provider?.trim() ?? "");
94264
+ if (name2 === null)
94265
+ return null;
94266
+ return ctx.mode === "duet" ? "" : name2;
94267
+ }
94268
+ var ENDPOINT_OUTCOMES = {
94269
+ refused: {
94270
+ headline: (who) => `${capitalise(who)} refused the request.`,
94271
+ hint: "Check the endpoint's API key and that its server is healthy, then send it again."
94272
+ },
94273
+ unreachable: {
94274
+ headline: (who) => `Orion couldn't reach ${who}.`,
94275
+ hint: "Check that the endpoint is running and its address is right, then send it again."
94276
+ },
94277
+ busy: {
94278
+ headline: (who) => `${capitalise(who)} is busy right now.`,
94279
+ hint: HINTS.retry
94280
+ },
94281
+ failed: {
94282
+ headline: (who) => `${capitalise(who)} returned an error.`,
94283
+ hint: "Check the endpoint's server, then send it again."
94284
+ }
94285
+ };
94286
+ var REFUSAL_KIND = /auth|permission|forbidden|unauthori[sz]ed|api.?key/i;
94287
+ function outcomeOf(reading) {
94288
+ if (reading.shape === "unreachable")
94289
+ return "unreachable";
94290
+ const { status, kind, message } = reading;
94291
+ if (status === 401 || status === 403 || status === null && REFUSAL_KIND.test(kind))
94292
+ return "refused";
94293
+ if (status === 429 || status === 529 || BUSY.test(kind) || BUSY.test(message))
94294
+ return "busy";
94295
+ return "failed";
94296
+ }
94297
+ function endpointFailure(said, name2) {
94298
+ if (CONTEXT_TOO_LONG.test(said))
94299
+ return null;
94300
+ const reading = (0, providerFailure_1.readEndpointFailure)(said);
94301
+ if (reading === null)
94302
+ return null;
94303
+ const who = name2 ? `your endpoint "${(0, providerFailure_1.plainLine)(name2, 60)}"` : "your endpoint";
94304
+ const { headline, hint } = ENDPOINT_OUTCOMES[outcomeOf(reading)];
94305
+ return { headline: headline(who), detail: reading.detail, action: "retry", hint, retryable: true };
94306
+ }
93579
94307
  function describeTurnError3(data, ctx = {}) {
93580
94308
  const text = typeof data?.error === "string" ? data.error.trim() : "";
93581
94309
  const code = typeof data?.code === "string" ? data.code : "";
@@ -93594,6 +94322,13 @@ var require_turnErrors = __commonJS({
93594
94322
  if ((0, client_core_1.describeNetworkFailure)(new Error(text)).kind === "tls-untrusted") {
93595
94323
  return view((0, client_core_1.networkFailureText)(new Error(text)), "network_settings", null, true);
93596
94324
  }
94325
+ const said = (0, providerFailure_1.providerWords)(text);
94326
+ const endpoint = endpointOf(ctx);
94327
+ if (said !== null && endpoint !== null) {
94328
+ const failure = endpointFailure(said, endpoint);
94329
+ if (failure !== null)
94330
+ return failure;
94331
+ }
93597
94332
  if (/no provider adapter configured/i.test(text) || /provider .* not (configured|available)/i.test(text)) {
93598
94333
  return view(`${model} can't run on this server.`, "pick_model");
93599
94334
  }
@@ -93616,13 +94351,13 @@ var require_turnErrors = __commonJS({
93616
94351
  if (/fetch failed|ECONNREFUSED|ENOTFOUND|ECONNRESET|network|unreachable|socket hang up|error sending request|client error \(Connect\)/i.test(text)) {
93617
94352
  return view("Orion couldn't reach the backend.", "check_network", null, true);
93618
94353
  }
93619
- if (/gateway auth refused|\b401\b|unauthori[sz]ed|not signed in|sign-in|signin|token (expired|revoked|refused)|auth(entication)? (failed|error)/i.test(text)) {
94354
+ if (said === null && /gateway auth refused|\b401\b|unauthori[sz]ed|not signed in|sign-in|signin|token (expired|revoked|refused)|auth(entication)? (failed|error)/i.test(text)) {
93620
94355
  return view("Your sign-in has expired.", "sign_in");
93621
94356
  }
93622
- if (/context (window )?(too large|exceeded)|prompt is too long|too many tokens|exceeds the model's context/i.test(text)) {
94357
+ if (CONTEXT_TOO_LONG.test(text)) {
93623
94358
  return view("The conversation is too long for the model's window.", "compact");
93624
94359
  }
93625
- if (/rate.?limit|too many requests|\b429\b|overloaded|\b529\b/i.test(text)) {
94360
+ if (BUSY.test(text)) {
93626
94361
  return view("The model provider is busy right now.", "retry");
93627
94362
  }
93628
94363
  if (text.length === 0) {
@@ -94048,6 +94783,8 @@ var require_placeholder = __commonJS({
94048
94783
  const waiting = n === 1 ? "1 message waiting for the next step" : `${n} messages waiting for the next step`;
94049
94784
  return host.queueEditKey ? `${waiting} \u2014 ${host.queueEditKey} edits, Enter adds another` : `${waiting} \u2014 Enter adds another`;
94050
94785
  }
94786
+ case "stopping":
94787
+ return "Stopping the turn \u2014 what you send now goes next";
94051
94788
  case "parked":
94052
94789
  return situation.on === "permission" ? parkedComposer_1.PARKED_COMPOSER_WORDING.permissionPlaceholder : situation.on === "plan" ? parkedComposer_1.PARKED_COMPOSER_WORDING.plan : parkedComposer_1.PARKED_COMPOSER_WORDING.ask;
94053
94790
  case "after-error":
@@ -94757,7 +95494,6 @@ var require_dist2 = __commonJS({
94757
95494
  Object.defineProperty(exports2, "__esModule", { value: true });
94758
95495
  exports2.normalizeCapabilities = exports2.isRenderableGuard = exports2.isGuardEntitled = exports2.entitledGuards = exports2.productToggles = exports2.UNAVAILABLE_CAPABILITIES = exports2.GUARD_IDS = exports2.GUARD_FIELDS = exports2.parseUserCompaction = exports2.parseInputGuardEvent = exports2.parseTrust = void 0;
94759
95496
  __exportStar(require_clientState(), exports2);
94760
- __exportStar(require_interruptedTurn(), exports2);
94761
95497
  __exportStar(require_chatFork(), exports2);
94762
95498
  __exportStar(require_permissionCard(), exports2);
94763
95499
  __exportStar(require_askCard(), exports2);
@@ -110384,7 +111120,7 @@ var ScrollBox2 = ScrollBox_default;
110384
111120
 
110385
111121
  // src/main.tsx
110386
111122
  var import_client_core61 = __toESM(require_dist(), 1);
110387
- var import_client_ui_core54 = __toESM(require_dist2(), 1);
111123
+ var import_client_ui_core55 = __toESM(require_dist2(), 1);
110388
111124
  var import_client_core62 = __toESM(require_dist(), 1);
110389
111125
  import { fileURLToPath as fileURLToPath3 } from "url";
110390
111126
  import * as os8 from "os";
@@ -112594,6 +113330,9 @@ function fileModified(s, d2) {
112594
113330
  const live = settlesBlock && f.live ? { ...f.live, liveCode: null } : f.live;
112595
113331
  return { ...f, live, settled: [...f.settled, item] };
112596
113332
  }
113333
+ function applyPlanUpdated(s, plan) {
113334
+ return { ...s, clientState: { ...s.clientState, plan } };
113335
+ }
112597
113336
  function applyTurnComplete(s, data) {
112598
113337
  const flushed = flushLive(s);
112599
113338
  const messages = Array.isArray(data.messages) ? data.messages : flushed.messages;
@@ -112608,24 +113347,22 @@ function applyTurnComplete(s, data) {
112608
113347
  live: null
112609
113348
  });
112610
113349
  }
112611
- function isUnrepairedStop(data) {
112612
- return data.error === "cancelled" && !Array.isArray(data.messages);
112613
- }
112614
- function applyTurnError(s, data) {
112615
- const flushed = flushLive(s);
112616
- const messages = Array.isArray(data.messages) ? data.messages : flushed.messages;
112617
- const settledTurn = { ...flushed, messages, committedCount: flushed.settled.length, live: null };
112618
- return isUnrepairedStop(data) ? settledTurn : markTurnBoundary(settledTurn);
112619
- }
112620
- function applyTurnInterrupted(s, prompt, partialReply = "") {
112621
- const partial = partialReply || s.live?.assistantText || "";
113350
+ function settleEndedTurn(s, left, forkable) {
112622
113351
  const flushed = flushLive(s);
112623
- return markTurnBoundary({
113352
+ const settledTurn = {
112624
113353
  ...flushed,
112625
- messages: (0, import_client_ui_core8.interruptedTurnMessages)(flushed.messages, prompt, partial),
113354
+ messages: left ?? flushed.messages,
112626
113355
  committedCount: flushed.settled.length,
112627
113356
  live: null
112628
- });
113357
+ };
113358
+ return forkable ? markTurnBoundary(settledTurn) : settledTurn;
113359
+ }
113360
+ function applyTurnError(s, data) {
113361
+ const left = Array.isArray(data.messages) ? data.messages : void 0;
113362
+ return settleEndedTurn(s, left, left !== void 0 || data.error !== "cancelled");
113363
+ }
113364
+ function applyTurnInterrupted(s, left) {
113365
+ return settleEndedTurn(s, left?.messages, left !== void 0);
112629
113366
  }
112630
113367
  function hydrate(sessionId, messages, clientState, transcript, contextGauge, turnMarks) {
112631
113368
  const msgs = Array.isArray(messages) ? messages : [];
@@ -113273,6 +114010,7 @@ function sessionSwapReset(s) {
113273
114010
  turnStartedAt: 0,
113274
114011
  // a stale anchor from a mid-swap turn must never stamp an Orbited line
113275
114012
  waitingOnPreviousTurn: false,
114013
+ stopping: false,
113276
114014
  turnFacts: import_client_ui_core12.EMPTY_TURN_FACTS,
113277
114015
  followUp: null,
113278
114016
  lastTurnMs: 0,
@@ -113324,6 +114062,7 @@ function initialAppState(partial) {
113324
114062
  turnStartedAt: 0,
113325
114063
  turnModel: "",
113326
114064
  waitingOnPreviousTurn: false,
114065
+ stopping: false,
113327
114066
  turnFacts: import_client_ui_core12.EMPTY_TURN_FACTS,
113328
114067
  followUp: null,
113329
114068
  lastTurnMs: 0,
@@ -119063,6 +119802,20 @@ function applyIndexStatus(state, key, value) {
119063
119802
  return state;
119064
119803
  }
119065
119804
 
119805
+ // src/state/stoppedTurn.ts
119806
+ function showInterrupted(transcript, turn) {
119807
+ if (turn.settled) return;
119808
+ transcript.setState(
119809
+ (s) => s.sessionId !== turn.sessionId || s.live === null ? s : appendNote(applyTurnInterrupted(s, void 0), INTERRUPT_NOTE)
119810
+ );
119811
+ }
119812
+ function settleInterrupted(transcript, turn, left) {
119813
+ if (turn.settled) return;
119814
+ showInterrupted(transcript, turn);
119815
+ turn.settled = true;
119816
+ transcript.setState((s) => s.sessionId === turn.sessionId ? applyTurnInterrupted(s, left) : s);
119817
+ }
119818
+
119066
119819
  // src/events/renderLane.ts
119067
119820
  var import_client_core31 = __toESM(require_dist(), 1);
119068
119821
  var import_client_ui_core21 = __toESM(require_dist2(), 1);
@@ -119079,8 +119832,8 @@ var TURN_ERROR_GESTURES = {
119079
119832
  compact: "/compact shortens it; /new starts fresh.",
119080
119833
  none: null
119081
119834
  };
119082
- function turnErrorNote(data, modelLabel) {
119083
- const view = (0, import_client_ui_core20.describeTurnError)(data, { modelLabel });
119835
+ function turnErrorNote(data, modelLabel, route = {}) {
119836
+ const view = (0, import_client_ui_core20.describeTurnError)(data, { modelLabel, ...route });
119084
119837
  const parts2 = [view.headline];
119085
119838
  if (view.detail) parts2.push(`(${view.detail})`);
119086
119839
  if (view.hint) parts2.push(view.hint);
@@ -119285,6 +120038,7 @@ function renderLane(transcript, app, ev) {
119285
120038
  current_step: typeof d2.current_step === "number" ? d2.current_step : 0
119286
120039
  }
119287
120040
  }));
120041
+ transcript.setState((s) => applyPlanUpdated(s, (0, import_client_core32.planOfEvent)(d2)));
119288
120042
  break;
119289
120043
  case "agent_lifecycle": {
119290
120044
  const agentId = String(d2.agent_id ?? ev.agent_id ?? "");
@@ -119425,8 +120179,8 @@ function renderLane(transcript, app, ev) {
119425
120179
  }));
119426
120180
  } else {
119427
120181
  const quota = (0, import_client_core32.parseQuotaRefusal)(d2);
119428
- const { modelLabel, model } = app.getState();
119429
- const failure = turnErrorNote(data, modelLabel || displayModel(model));
120182
+ const { modelLabel, model, provider, mode } = app.getState();
120183
+ const failure = turnErrorNote(data, modelLabel || displayModel(model), { provider, mode });
119430
120184
  transcript.setState(
119431
120185
  (s) => quota ? appendQuota(applyTurnError(s, data), quota) : appendNote(applyTurnError(s, data), failure.text, { tone: "error" })
119432
120186
  );
@@ -119636,7 +120390,7 @@ function startupFailureMessage(err2, apiUrl) {
119636
120390
  }
119637
120391
 
119638
120392
  // src/main.tsx
119639
- var import_client_ui_core55 = __toESM(require_dist2(), 1);
120393
+ var import_client_ui_core56 = __toESM(require_dist2(), 1);
119640
120394
 
119641
120395
  // src/services/goalTurn.ts
119642
120396
  var GOAL_SEND_MAX_ATTEMPTS = 20;
@@ -120483,7 +121237,7 @@ var WorkPlanApprovalBridge = class {
120483
121237
  };
120484
121238
 
120485
121239
  // src/main.tsx
120486
- var import_client_ui_core56 = __toESM(require_dist2(), 1);
121240
+ var import_client_ui_core57 = __toESM(require_dist2(), 1);
120487
121241
 
120488
121242
  // src/components/layout/Transcript.tsx
120489
121243
  var import_react30 = __toESM(require_react(), 1);
@@ -137771,7 +138525,7 @@ function detectTerminal2(env3 = process.env, platform2 = process.platform) {
137771
138525
 
137772
138526
  // src/services/version.ts
137773
138527
  function tuiVersion() {
137774
- return "0.1.40".length > 0 ? "0.1.40" : null;
138528
+ return "0.1.41".length > 0 ? "0.1.41" : null;
137775
138529
  }
137776
138530
 
137777
138531
  // src/components/layout/WelcomeCard.tsx
@@ -142640,15 +143394,33 @@ function WorkingStatus({
142640
143394
  ] });
142641
143395
  }
142642
143396
 
142643
- // src/state/inputQueue.ts
143397
+ // src/components/layout/footerWords.ts
142644
143398
  var import_client_ui_core46 = __toESM(require_dist2(), 1);
143399
+ function stopUnderWay(moment) {
143400
+ return moment.stopping && moment.status === "streaming";
143401
+ }
143402
+ function workingLabel(moment) {
143403
+ if (stopUnderWay(moment)) return import_client_ui_core46.STOPPING_TURN;
143404
+ return moment.waitingOnPreviousTurn ? import_client_ui_core46.WAITING_FOR_PREVIOUS_TURN : void 0;
143405
+ }
143406
+ function composerSituation(moment) {
143407
+ if (moment.status === "error") return { kind: "after-error" };
143408
+ if (moment.status === "streaming") {
143409
+ if (moment.stopping) return { kind: "stopping" };
143410
+ return moment.queuedCount > 0 ? { kind: "queued", count: moment.queuedCount } : { kind: "busy" };
143411
+ }
143412
+ return moment.conversationStarted ? { kind: "idle" } : { kind: "first" };
143413
+ }
143414
+
143415
+ // src/state/inputQueue.ts
143416
+ var import_client_ui_core47 = __toESM(require_dist2(), 1);
142645
143417
  function isEditable(entry) {
142646
- return (0, import_client_ui_core46.isEditableSteer)(entry);
143418
+ return (0, import_client_ui_core47.isEditableSteer)(entry);
142647
143419
  }
142648
143420
  function popEditableQueue(app, draft) {
142649
143421
  const own = app.getState().queuedMessages.filter(isEditable);
142650
143422
  const attachments = own.flatMap((entry) => entry.attachments ?? []);
142651
- const merged = (0, import_client_ui_core46.mergeIntoDraft)(
143423
+ const merged = (0, import_client_ui_core47.mergeIntoDraft)(
142652
143424
  own.map((entry) => entry.text),
142653
143425
  draft
142654
143426
  );
@@ -142658,7 +143430,7 @@ function popEditableQueue(app, draft) {
142658
143430
  return merged ?? draft;
142659
143431
  }
142660
143432
  function queueRows(queued) {
142661
- const { shown, hidden } = (0, import_client_ui_core46.capMachineRows)(queued);
143433
+ const { shown, hidden } = (0, import_client_ui_core47.capMachineRows)(queued);
142662
143434
  const rows = shown.map((entry) => ({ kind: "entry", entry }));
142663
143435
  if (hidden > 0) rows.push({ kind: "overflow", hidden });
142664
143436
  return rows;
@@ -142711,7 +143483,7 @@ function PendingAttachments({ items: all }) {
142711
143483
  }
142712
143484
 
142713
143485
  // src/components/layout/ReplFooter.tsx
142714
- var import_client_ui_core47 = __toESM(require_dist2(), 1);
143486
+ var import_client_ui_core48 = __toESM(require_dist2(), 1);
142715
143487
  var import_jsx_runtime77 = __toESM(require_jsx_runtime(), 1);
142716
143488
  function ReplFooter({
142717
143489
  appStore,
@@ -142761,11 +143533,12 @@ function ReplFooter({
142761
143533
  const cloudJobStatus = useStoreSelector(appStore, (s) => s.cloudJobStatus);
142762
143534
  const cloudWorking = cloudJobStatus !== null && agentWorking(cloudJobStatus);
142763
143535
  const queuedCount = useStoreSelector(appStore, (s) => s.queuedMessages.length);
143536
+ const stopping = useStoreSelector(appStore, stopUnderWay);
142764
143537
  const followUp = useStoreSelector(appStore, (s) => s.followUp);
142765
143538
  const screenReader = useStoreSelector(appStore, (s) => s.screenReader);
142766
143539
  const ghost = !cloud && !screenReader && status === "idle" && followUp ? followUp : void 0;
142767
- const placeholder = cloud ? cloudJob ? (0, import_client_ui_core47.cloudInputPlaceholder)((0, import_client_ui_core47.inputModeFor)(cloudJobStatus ?? "queued", true), null) : (0, import_client_ui_core47.inputPlaceholder)("cloud") : (0, import_client_ui_core47.composerPlaceholder)(
142768
- status === "error" ? { kind: "after-error" } : status === "streaming" ? queuedCount > 0 ? { kind: "queued", count: queuedCount } : { kind: "busy" } : conversationStarted ? { kind: "idle" } : { kind: "first" },
143540
+ const placeholder = cloud ? cloudJob ? (0, import_client_ui_core48.cloudInputPlaceholder)((0, import_client_ui_core48.inputModeFor)(cloudJobStatus ?? "queued", true), null) : (0, import_client_ui_core48.inputPlaceholder)("cloud") : (0, import_client_ui_core48.composerPlaceholder)(
143541
+ composerSituation({ status, stopping, queuedCount, conversationStarted }),
142769
143542
  // No queue key here: the strip under the turn carries that hint itself.
142770
143543
  { shellPrefix: true, queueEditKey: null, helpKey: "?" }
142771
143544
  );
@@ -142794,7 +143567,14 @@ function ReplFooter({
142794
143567
  );
142795
143568
  const [menuOpen, setMenuOpen] = import_react51.default.useState(false);
142796
143569
  return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(import_jsx_runtime77.Fragment, { children: [
142797
- streaming && !parked ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(WorkingStatus, { store: appStore, label: waitingOnPreviousTurn ? import_client_ui_core47.WAITING_FOR_PREVIOUS_TURN : void 0, stopKey: vimMode ? "Ctrl+C" : "esc" }) : cloudWorking ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(WorkingStatus, { store: appStore, tokens: 0, stopKey: null }) : null,
143570
+ streaming && !parked ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
143571
+ WorkingStatus,
143572
+ {
143573
+ store: appStore,
143574
+ label: workingLabel({ status, stopping, waitingOnPreviousTurn }),
143575
+ stopKey: stopping ? null : vimMode ? "Ctrl+C" : "esc"
143576
+ }
143577
+ ) : cloudWorking ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(WorkingStatus, { store: appStore, tokens: 0, stopKey: null }) : null,
142798
143578
  plan && /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(PlanCard, { plan }),
142799
143579
  pendingWorkPlan ? /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
142800
143580
  WorkPlanApprovalPrompt,
@@ -142987,13 +143767,13 @@ function FullscreenLayout({
142987
143767
  // src/components/dialogs/AgentsDashboard.tsx
142988
143768
  var import_react54 = __toESM(require_react(), 1);
142989
143769
  var import_client_core51 = __toESM(require_dist(), 1);
142990
- var import_client_ui_core49 = __toESM(require_dist2(), 1);
143770
+ var import_client_ui_core50 = __toESM(require_dist2(), 1);
142991
143771
  var import_strip_ansi4 = __toESM(require_strip_ansi(), 1);
142992
143772
 
142993
143773
  // src/components/dialogs/agentsHandback.tsx
142994
143774
  var import_react53 = __toESM(require_react(), 1);
142995
143775
  var import_client_core50 = __toESM(require_dist(), 1);
142996
- var import_client_ui_core48 = __toESM(require_dist2(), 1);
143776
+ var import_client_ui_core49 = __toESM(require_dist2(), 1);
142997
143777
  var import_jsx_runtime79 = __toESM(require_jsx_runtime(), 1);
142998
143778
  function useHandback(host) {
142999
143779
  const [mismatch, setMismatch] = (0, import_react53.useState)(null);
@@ -143006,7 +143786,7 @@ function useHandback(host) {
143006
143786
  const hb = host.handback;
143007
143787
  if (!hb) return;
143008
143788
  const outcome = await hb.checkout(root2, branch, strategy);
143009
- say((0, import_client_ui_core48.checkedOutNote)(outcome), outcome.ok ? "success" : "warning");
143789
+ say((0, import_client_ui_core49.checkedOutNote)(outcome), outcome.ok ? "success" : "warning");
143010
143790
  if (outcome.ok && then === "continue") {
143011
143791
  await hb.continueLocally(job);
143012
143792
  host.onClose();
@@ -143054,7 +143834,7 @@ function useHandback(host) {
143054
143834
  const diff3 = await host.getDiff(job);
143055
143835
  if (diff3.files.length === 0) return say("The agent has not pushed any changes yet.", "warning");
143056
143836
  const outcome = await hb.apply(root2, diff3);
143057
- say((0, import_client_ui_core48.appliedNote)(outcome, { nameFirstConflict: true }), outcome.conflicted.length ? "warning" : "success");
143837
+ say((0, import_client_ui_core49.appliedNote)(outcome, { nameFirstConflict: true }), outcome.conflicted.length ? "warning" : "success");
143058
143838
  });
143059
143839
  };
143060
143840
  const handleCardKey = (job, input, escape) => {
@@ -143062,7 +143842,7 @@ function useHandback(host) {
143062
143842
  if (escape) setMismatch(null);
143063
143843
  else if (input === "!") setMismatch({ ...mismatch, remember: !mismatch.remember });
143064
143844
  else {
143065
- const choice = import_client_ui_core48.MISMATCH_CHOICES[Number(input) - 1];
143845
+ const choice = import_client_ui_core49.MISMATCH_CHOICES[Number(input) - 1];
143066
143846
  if (choice) answerMismatch(job, choice.choice);
143067
143847
  }
143068
143848
  return true;
@@ -143071,14 +143851,14 @@ function useHandback(host) {
143071
143851
  }
143072
143852
  function MismatchCard({ job, card }) {
143073
143853
  return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box2, { flexDirection: "column", children: [
143074
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: (0, import_client_ui_core48.jobTitle)(job) }),
143854
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: (0, import_client_ui_core49.jobTitle)(job) }),
143075
143855
  /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box2, { flexDirection: "column", marginTop: 1, children: [
143076
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: "warning", bold: true, wrap: "truncate-end", children: (0, import_client_ui_core48.mismatchCardTitle)(card.currentBranch, card.files) }),
143077
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: import_client_ui_core48.MISMATCH_QUESTION }),
143856
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: "warning", bold: true, wrap: "truncate-end", children: (0, import_client_ui_core49.mismatchCardTitle)(card.currentBranch, card.files) }),
143857
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: import_client_ui_core49.MISMATCH_QUESTION }),
143078
143858
  card.files.slice(0, 6).map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: ` ${f}` }, f)),
143079
143859
  card.files.length > 6 ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { dim: true, children: ` \u2026and ${card.files.length - 6} more` }) : null
143080
143860
  ] }),
143081
- /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Box2, { flexDirection: "column", marginTop: 1, children: import_client_ui_core48.MISMATCH_CHOICES.map((c3, i2) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: c3.choice === "discard" ? "error" : void 0, wrap: "truncate-end", children: ` ${i2 + 1} ${c3.label} \u2014 ${c3.detail}` }, c3.choice)) }),
143861
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Box2, { flexDirection: "column", marginTop: 1, children: import_client_ui_core49.MISMATCH_CHOICES.map((c3, i2) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { colorKey: c3.choice === "discard" ? "error" : void 0, wrap: "truncate-end", children: ` ${i2 + 1} ${c3.label} \u2014 ${c3.detail}` }, c3.choice)) }),
143082
143862
  /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Box2, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: ` ! ${card.remember ? "[x]" : "[ ]"} don't ask again for this repository (stash or commit) \xB7 esc keeps the tree` }) })
143083
143863
  ] });
143084
143864
  }
@@ -143129,7 +143909,7 @@ function AgentsDashboard({
143129
143909
  const [board, setBoard] = (0, import_react54.useState)(panel.jobs);
143130
143910
  const boardRef = (0, import_react54.useRef)(panel.jobs);
143131
143911
  const feedRef = (0, import_react54.useRef)(null);
143132
- const [transcript, setTranscript] = (0, import_react54.useState)(() => (0, import_client_ui_core49.emptyTranscript)());
143912
+ const [transcript, setTranscript] = (0, import_react54.useState)(() => (0, import_client_ui_core50.emptyTranscript)());
143133
143913
  const [diff3, setDiff] = (0, import_react54.useState)(null);
143134
143914
  const diffFetchedFor = (0, import_react54.useRef)(null);
143135
143915
  const [liveTerm, setLiveTerm] = (0, import_react54.useState)("");
@@ -143159,12 +143939,12 @@ function AgentsDashboard({
143159
143939
  const launchRepos = panel.launchRepos ?? [];
143160
143940
  const now2 = Date.now();
143161
143941
  const byId = new Map(board.map((row) => [row.id, row]));
143162
- const views = (0, import_client_ui_core49.sortJobRows)(board.map((row) => (0, import_client_ui_core49.jobRowView)(row, now2)));
143942
+ const views = (0, import_client_ui_core50.sortJobRows)(board.map((row) => (0, import_client_ui_core50.jobRowView)(row, now2)));
143163
143943
  const jobs = views.map((v2) => byId.get(v2.id));
143164
143944
  const safeCursor = Math.min(cursor, Math.max(0, jobs.length - 1));
143165
143945
  const detailId = screen.kind === "detail" ? screen.id : null;
143166
143946
  const detailJob = detailId ? byId.get(detailId) ?? null : null;
143167
- const inputMode = detailJob && (0, import_client_core51.isTerminal)(detailJob.status) ? (0, import_client_ui_core49.inputModeFor)(detailJob.status, true) : transcript.inputMode;
143947
+ const inputMode = detailJob && (0, import_client_core51.isTerminal)(detailJob.status) ? (0, import_client_ui_core50.inputModeFor)(detailJob.status, true) : transcript.inputMode;
143168
143948
  const panelRef = (0, import_react54.useRef)(panel);
143169
143949
  panelRef.current = panel;
143170
143950
  const screenRef = (0, import_react54.useRef)(screen);
@@ -143184,10 +143964,10 @@ function AgentsDashboard({
143184
143964
  listJobs: (options) => panelRef.current.listJobs(options)
143185
143965
  });
143186
143966
  feedRef.current = feed;
143187
- const deduper = new import_client_ui_core49.NotificationDeduper();
143967
+ const deduper = new import_client_ui_core50.NotificationDeduper();
143188
143968
  const sub = feed.subscribe((change) => {
143189
143969
  if (change.kind === "patch") {
143190
- const decision = (0, import_client_ui_core49.notificationFor)(change.previous, change.job, { windowFocused: false, enabled: true });
143970
+ const decision = (0, import_client_ui_core50.notificationFor)(change.previous, change.job, { windowFocused: false, enabled: true });
143191
143971
  if (decision && deduper.first(decision.dedupeKey)) panelRef.current.bell();
143192
143972
  }
143193
143973
  if (change.kind !== "health") {
@@ -143210,7 +143990,7 @@ function AgentsDashboard({
143210
143990
  (0, import_react54.useEffect)(() => {
143211
143991
  if (!detailId) return;
143212
143992
  const seed = boardRef.current.find((row) => row.id === detailId);
143213
- let current2 = seed ? (0, import_client_ui_core49.applyJobRow)((0, import_client_ui_core49.emptyTranscript)(detailId), seed) : (0, import_client_ui_core49.emptyTranscript)(detailId);
143993
+ let current2 = seed ? (0, import_client_ui_core50.applyJobRow)((0, import_client_ui_core50.emptyTranscript)(detailId), seed) : (0, import_client_ui_core50.emptyTranscript)(detailId);
143214
143994
  setTranscript(current2);
143215
143995
  setDiff(null);
143216
143996
  setLiveTerm("");
@@ -143254,7 +144034,7 @@ function AgentsDashboard({
143254
144034
  }
143255
144035
  }
143256
144036
  if (message.kind === "job") feedRef.current?.applyRow(message.job.unread ? { ...message.job, unread: false } : message.job);
143257
- current2 = (0, import_client_ui_core49.applyStreamMessage)(current2, message);
144037
+ current2 = (0, import_client_ui_core50.applyStreamMessage)(current2, message);
143258
144038
  schedule();
143259
144039
  });
143260
144040
  stream.start();
@@ -143842,13 +144622,13 @@ function footerHint(screen, job, messaging, conn, width, launch) {
143842
144622
  }
143843
144623
  function WatchLine({ status, accountsUrl }) {
143844
144624
  if (!status) return null;
143845
- const page = (0, import_client_ui_core49.watcherPageUrl)(accountsUrl);
144625
+ const page = (0, import_client_ui_core50.watcherPageUrl)(accountsUrl);
143846
144626
  return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Box2, { flexDirection: "column", children: [
143847
144627
  /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(ThemedText, { children: [
143848
- /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: status.connections.length > 0 ? "success" : void 0, dim: status.connections.length === 0, children: `${status.connections.length > 0 ? "\u2691 " : ""}${(0, import_client_ui_core49.watchSummaryLine)(status.connections)}` }),
144628
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: status.connections.length > 0 ? "success" : void 0, dim: status.connections.length === 0, children: `${status.connections.length > 0 ? "\u2691 " : ""}${(0, import_client_ui_core50.watchSummaryLine)(status.connections)}` }),
143849
144629
  /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, children: page ? ` \xB7 manage at ${page}` : " \xB7 set up on the website" })
143850
144630
  ] }),
143851
- status.connections.slice(0, 6).map((c3) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: ` ${(0, import_client_ui_core49.watchConnectionLine)(c3)} \xB7 ${(0, import_client_ui_core49.watchHealthChip)(c3).label}` }, c3.id))
144631
+ status.connections.slice(0, 6).map((c3) => /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, wrap: "truncate-end", children: ` ${(0, import_client_ui_core50.watchConnectionLine)(c3)} \xB7 ${(0, import_client_ui_core50.watchHealthChip)(c3).label}` }, c3.id))
143852
144632
  ] });
143853
144633
  }
143854
144634
  function ConnectionsView({
@@ -144005,9 +144785,9 @@ function JobDetailView({
144005
144785
  const terminal = (0, import_client_core51.isTerminal)(status);
144006
144786
  const pending = queue.filter((item) => !item.sent_at);
144007
144787
  const live = composer.mode !== "none" && composer.mode !== "launch";
144008
- const row = job ? (0, import_client_ui_core49.jobRowView)(job, Date.now()) : null;
144009
- const pill = row ? (0, import_client_ui_core49.jobStatusPill)(status, row.failure) : null;
144010
- const recording = job && (job.screen_state === "ready" || job.screen_state === "truncated") ? (0, import_client_ui_core49.recordingPageUrl)(accountsUrl, job.id) : null;
144788
+ const row = job ? (0, import_client_ui_core50.jobRowView)(job, Date.now()) : null;
144789
+ const pill = row ? (0, import_client_ui_core50.jobStatusPill)(status, row.failure) : null;
144790
+ const recording = job && (job.screen_state === "ready" || job.screen_state === "truncated") ? (0, import_client_ui_core50.recordingPageUrl)(accountsUrl, job.id) : null;
144011
144791
  const errorLine = job?.error && !transcript.failure && terminal ? job.error : null;
144012
144792
  (0, import_react54.useEffect)(() => {
144013
144793
  const measure = () => {
@@ -144022,10 +144802,10 @@ function JobDetailView({
144022
144802
  });
144023
144803
  if (!job) return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, children: "Job not found." });
144024
144804
  if (handback?.mismatch) return /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(MismatchCard, { job, card: handback.mismatch });
144025
- const facts = [pill?.label ?? status, row?.sourceChip ?? "", repoName((0, import_client_ui_core49.repoSlug)(job.repo_url)), job.branch_name ?? (job.target_branch ? `\u2192 ${job.target_branch}` : ""), row?.elapsed ?? "", job.model ?? ""].filter(Boolean);
144805
+ const facts = [pill?.label ?? status, row?.sourceChip ?? "", repoName((0, import_client_ui_core50.repoSlug)(job.repo_url)), job.branch_name ?? (job.target_branch ? `\u2192 ${job.target_branch}` : ""), row?.elapsed ?? "", job.model ?? ""].filter(Boolean);
144026
144806
  return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Box2, { flexDirection: "column", flexGrow: 1, children: [
144027
144807
  /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Box2, { flexDirection: "column", flexShrink: 0, children: [
144028
- /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: fitCell((0, import_client_ui_core49.jobTitle)(job), width).trimEnd() }),
144808
+ /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "listSelection", bold: true, children: fitCell((0, import_client_ui_core50.jobTitle)(job), width).trimEnd() }),
144029
144809
  /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Box2, { children: [
144030
144810
  /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: statusColorKey(status), bold: true, children: facts[0] }),
144031
144811
  facts.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, children: ` \xB7 ${fitCell(facts.slice(1).join(" \xB7 "), Math.max(0, width - facts[0].length - 5)).trimEnd()}` }) : null
@@ -144033,7 +144813,7 @@ function JobDetailView({
144033
144813
  job.pr_url ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "info", children: fitCell(`PR ${job.pr_url}${row?.prChip ? ` \xB7 ${row.prChip.replace(/^PR\s*/, "")}` : ""}`, width).trimEnd() }) : null,
144034
144814
  recording ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "info", children: fitCell(`Recording ${recording}${job.screen_state === "truncated" ? " \xB7 cut at the cap" : ""}`, width).trimEnd() }) : null,
144035
144815
  errorLine ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "error", children: fitCell(errorLine, width).trimEnd() }) : null,
144036
- transcript.health === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "error", children: fitCell((0, import_client_ui_core49.streamFailureLine)(transcript), width).trimEnd() }) : transcript.health === "reconnecting" ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, children: "reconnecting\u2026" }) : null
144816
+ transcript.health === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { colorKey: "error", children: fitCell((0, import_client_ui_core50.streamFailureLine)(transcript), width).trimEnd() }) : transcript.health === "reconnecting" ? /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(ThemedText, { dim: true, children: "reconnecting\u2026" }) : null
144037
144817
  ] }),
144038
144818
  /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("ink-box", { ref: viewportRef, style: { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 3, overflow: "hidden", marginTop: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("ink-box", { ref: bodyRef, style: { flexDirection: "column", flexShrink: 0, marginTop: -scroll }, children: [
144039
144819
  /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
@@ -144872,6 +145652,17 @@ var REMOTE_DENIED = /* @__PURE__ */ new Set([
144872
145652
  "agent-messages"
144873
145653
  ]);
144874
145654
  var REMOTE_ARG_REQUIRED = /* @__PURE__ */ new Set(["provider", "persona", "reasoning", "tokensave"]);
145655
+ function sendVerdict(outcome) {
145656
+ switch (outcome) {
145657
+ case "started":
145658
+ case "queued":
145659
+ return import_client_core54.RC_OK;
145660
+ case "declined":
145661
+ return (0, import_client_core54.rcRefusePhotosMidTurn)();
145662
+ case "blocked":
145663
+ return (0, import_client_core54.rcRefuse)("signed_out", "sign in on this machine and choose a model first");
145664
+ }
145665
+ }
144875
145666
  function flattenInfoView(view) {
144876
145667
  const lines = [view.title];
144877
145668
  if (view.subtitle !== void 0 && view.subtitle !== "") lines.push(view.subtitle);
@@ -144922,8 +145713,8 @@ function startTuiRemoteControl(opts) {
144922
145713
  opts.verbs.steer(text);
144923
145714
  return import_client_core54.RC_OK;
144924
145715
  }
144925
- const sent = attachments !== void 0 && attachments.length > 0 ? opts.verbs.send(text, attachments) : opts.verbs.send(text);
144926
- return sent ? import_client_core54.RC_OK : (0, import_client_core54.rcRefuse)("signed_out", "sign in on this machine first");
145716
+ const outcome = attachments !== void 0 && attachments.length > 0 ? opts.verbs.send(text, attachments) : opts.verbs.send(text);
145717
+ return sendVerdict(outcome);
144927
145718
  };
144928
145719
  const commandCatalog = () => opts.commands === void 0 ? [] : opts.commands.registry.all().filter((command) => !REMOTE_DENIED.has(command.name)).map((command) => ({
144929
145720
  name: command.name,
@@ -145188,7 +145979,7 @@ function startTuiRemoteControl(opts) {
145188
145979
 
145189
145980
  // src/services/machineWake.ts
145190
145981
  var import_client_core55 = __toESM(require_dist(), 1);
145191
- var import_client_ui_core50 = __toESM(require_dist2(), 1);
145982
+ var import_client_ui_core51 = __toESM(require_dist2(), 1);
145192
145983
  function createWakeIfIdle(deps2) {
145193
145984
  return (prompt) => {
145194
145985
  if (!deps2.isIdle()) return;
@@ -145210,7 +146001,7 @@ function createPeerDelivery(deps2) {
145210
146001
  return (message) => {
145211
146002
  const hold = (0, import_client_core55.inboundNoteToHold)(message, (userId) => deps2.trusted?.(userId) === true);
145212
146003
  deps2.pen.hold(hold.item, hold.relayDepth, hold.turnStart);
145213
- deps2.appendRow((0, import_client_ui_core50.peerDeliveredRow)(message));
146004
+ deps2.appendRow((0, import_client_ui_core51.peerDeliveredRow)(message));
145214
146005
  queueMicrotask(() => wakeIfIdle((0, import_client_core55.peerMessageWakePrompt)(message)));
145215
146006
  };
145216
146007
  }
@@ -145230,7 +146021,7 @@ function createTuiIdleWatcher(deps2) {
145230
146021
 
145231
146022
  // src/services/friendsSurface.ts
145232
146023
  var import_client_core56 = __toESM(require_dist(), 1);
145233
- var import_client_ui_core51 = __toESM(require_dist2(), 1);
146024
+ var import_client_ui_core52 = __toESM(require_dist2(), 1);
145234
146025
  function createTuiFriends(deps2) {
145235
146026
  const store = new import_client_core56.FriendsStore({
145236
146027
  api: {
@@ -145255,14 +146046,14 @@ function createTuiFriends(deps2) {
145255
146046
  senderClass: () => deps2.senderClass(),
145256
146047
  onSettled: (sessionId, review, outcome, detail) => {
145257
146048
  if (sessionId !== deps2.currentSessionId()) return;
145258
- deps2.appendRow((0, import_client_ui_core51.friendSendSettledRow)(review, outcome, detail));
145259
- deps2.pen.hold((0, import_client_core56.harnessNoticeInput)((0, import_client_ui_core51.friendSendNoticeForModel)(review, outcome, detail)));
146049
+ deps2.appendRow((0, import_client_ui_core52.friendSendSettledRow)(review, outcome, detail));
146050
+ deps2.pen.hold((0, import_client_core56.harnessNoticeInput)((0, import_client_ui_core52.friendSendNoticeForModel)(review, outcome, detail)));
145260
146051
  },
145261
146052
  onChanged: (sessionId) => {
145262
146053
  if (sessionId !== deps2.currentSessionId()) return;
145263
146054
  const pending = reviews.pendingFor(sessionId).length;
145264
146055
  if (pendingReviews.arrived(sessionId, pending)) {
145265
- deps2.appendRow(`${(0, import_client_ui_core51.friendSendPendingSummary)(pending)} \u2014 /messages to review`);
146056
+ deps2.appendRow(`${(0, import_client_ui_core52.friendSendPendingSummary)(pending)} \u2014 /messages to review`);
145266
146057
  }
145267
146058
  }
145268
146059
  });
@@ -145544,12 +146335,12 @@ function shouldAutoApprove(fullyAutomatic, configAutoApprove) {
145544
146335
 
145545
146336
  // src/headless/runHeadless.ts
145546
146337
  var import_client_core59 = __toESM(require_dist(), 1);
145547
- var import_client_ui_core52 = __toESM(require_dist2(), 1);
146338
+ var import_client_ui_core53 = __toESM(require_dist2(), 1);
145548
146339
  import * as os7 from "os";
145549
146340
  import { randomUUID } from "crypto";
145550
146341
  function adoptTurnComplete(carry, data) {
145551
146342
  if (Array.isArray(data.messages)) carry.messages = data.messages;
145552
- (0, import_client_ui_core52.mergeTurnComplete)(carry.clientState, data);
146343
+ (0, import_client_ui_core53.mergeTurnComplete)(carry.clientState, data);
145553
146344
  }
145554
146345
  var HEADLESS_RESULT_PREFIX = "ORION_HEADLESS_RESULT ";
145555
146346
  var HEADLESS_DEFAULT_MAX_TURNS = 100;
@@ -145790,7 +146581,7 @@ async function runHeadless(flags2) {
145790
146581
  };
145791
146582
  };
145792
146583
  const sessionId = randomUUID();
145793
- const carry = { messages: [], clientState: (0, import_client_ui_core52.emptyClientState)() };
146584
+ const carry = { messages: [], clientState: (0, import_client_ui_core53.emptyClientState)() };
145794
146585
  const trace = process.env.ORION_HEADLESS_TRACE === "1";
145795
146586
  const runTurn = async (prompt) => {
145796
146587
  const ac = new AbortController();
@@ -145819,7 +146610,9 @@ async function runHeadless(flags2) {
145819
146610
  ...boundary.makeHandlers(sessionId, ac.signal, onEvent),
145820
146611
  isCancelRequested: () => false,
145821
146612
  onPhaseChange: () => void 0
145822
- }
146613
+ },
146614
+ // A turn that fails ends the run (below): nothing reads what it left.
146615
+ import_client_core59.CLOSE_TURNS_IN_BACKGROUND
145823
146616
  );
145824
146617
  const data = outcome.event.data ?? {};
145825
146618
  if (outcome.kind === "turn_error") {
@@ -145869,7 +146662,7 @@ async function runHeadless(flags2) {
145869
146662
 
145870
146663
  // src/services/feedbackGate.ts
145871
146664
  var import_client_core60 = __toESM(require_dist(), 1);
145872
- var import_client_ui_core53 = __toESM(require_dist2(), 1);
146665
+ var import_client_ui_core54 = __toESM(require_dist2(), 1);
145873
146666
  import { randomUUID as randomUUID2 } from "crypto";
145874
146667
  var TuiFeedbackGate = class {
145875
146668
  constructor(deps2) {
@@ -145889,13 +146682,13 @@ var TuiFeedbackGate = class {
145889
146682
  maybeOffer(sessionId, turnId) {
145890
146683
  const now2 = this.now();
145891
146684
  const config = this.deps.loadConfig();
145892
- let state = (0, import_client_ui_core53.feedbackStateFromScalars)(config, now2);
146685
+ let state = (0, import_client_ui_core54.feedbackStateFromScalars)(config, now2);
145893
146686
  if (config.feedbackFirstSeenAt <= 0 || config.feedbackSeed === 0) {
145894
- state = { ...state, seed: (0, import_client_ui_core53.mintFeedbackSeed)() };
145895
- this.safeSave((0, import_client_ui_core53.feedbackScalarsFromState)(state));
146687
+ state = { ...state, seed: (0, import_client_ui_core54.mintFeedbackSeed)() };
146688
+ this.safeSave((0, import_client_ui_core54.feedbackScalarsFromState)(state));
145896
146689
  }
145897
146690
  const pendings = this.deps.getPendings();
145898
- const offer = (0, import_client_ui_core53.shouldOfferFeedback)(state, {
146691
+ const offer = (0, import_client_ui_core54.shouldOfferFeedback)(state, {
145899
146692
  now: now2,
145900
146693
  completedTurnsThisSession: this.turnsBySession.get(sessionId) ?? 0,
145901
146694
  turnCompletedCleanly: true,
@@ -145906,8 +146699,8 @@ var TuiFeedbackGate = class {
145906
146699
  forced: this.deps.forced
145907
146700
  });
145908
146701
  if (!offer) return;
145909
- state = (0, import_client_ui_core53.applyFeedbackShown)(state, now2);
145910
- this.safeSave((0, import_client_ui_core53.feedbackScalarsFromState)(state));
146702
+ state = (0, import_client_ui_core54.applyFeedbackShown)(state, now2);
146703
+ this.safeSave((0, import_client_ui_core54.feedbackScalarsFromState)(state));
145911
146704
  const routing = this.deps.getRouting();
145912
146705
  const requestId = randomUUID2();
145913
146706
  this.pendingId = requestId;
@@ -145929,10 +146722,10 @@ var TuiFeedbackGate = class {
145929
146722
  const snapshot = this.deps.getMessages().slice();
145930
146723
  const now2 = this.now();
145931
146724
  const config = this.deps.loadConfig();
145932
- const state = (0, import_client_ui_core53.applyFeedbackSubmitted)((0, import_client_ui_core53.feedbackStateFromScalars)(config, now2), now2);
145933
- this.safeSave((0, import_client_ui_core53.feedbackScalarsFromState)(state));
145934
- const card = (0, import_client_ui_core53.feedbackSetWhy)((0, import_client_ui_core53.feedbackSelectScore)((0, import_client_ui_core53.createFeedbackCard)(), score), why);
145935
- const body2 = (0, import_client_ui_core53.buildFeedbackSubmission)(card, {
146725
+ const state = (0, import_client_ui_core54.applyFeedbackSubmitted)((0, import_client_ui_core54.feedbackStateFromScalars)(config, now2), now2);
146726
+ this.safeSave((0, import_client_ui_core54.feedbackScalarsFromState)(state));
146727
+ const card = (0, import_client_ui_core54.feedbackSetWhy)((0, import_client_ui_core54.feedbackSelectScore)((0, import_client_ui_core54.createFeedbackCard)(), score), why);
146728
+ const body2 = (0, import_client_ui_core54.buildFeedbackSubmission)(card, {
145936
146729
  submissionId: randomUUID2(),
145937
146730
  sessionId: meta.sessionId,
145938
146731
  turnId: meta.turnId,
@@ -145947,7 +146740,7 @@ var TuiFeedbackGate = class {
145947
146740
  conversation,
145948
146741
  conversation_truncated: truncated
145949
146742
  });
145950
- this.deps.appendNote(import_client_ui_core53.FEEDBACK_WORDING.transcriptNote);
146743
+ this.deps.appendNote(import_client_ui_core54.FEEDBACK_WORDING.transcriptNote);
145951
146744
  return true;
145952
146745
  }
145953
146746
  /** The session/transcript under a pending card is going away (new chat, resume) —
@@ -145966,11 +146759,11 @@ var TuiFeedbackGate = class {
145966
146759
  this.deps.setPendingFeedback(null);
145967
146760
  if (explicit) {
145968
146761
  const now2 = this.now();
145969
- const state = (0, import_client_ui_core53.applyFeedbackDismissed)(
145970
- (0, import_client_ui_core53.feedbackStateFromScalars)(this.deps.loadConfig(), now2),
146762
+ const state = (0, import_client_ui_core54.applyFeedbackDismissed)(
146763
+ (0, import_client_ui_core54.feedbackStateFromScalars)(this.deps.loadConfig(), now2),
145971
146764
  now2
145972
146765
  );
145973
- this.safeSave((0, import_client_ui_core53.feedbackScalarsFromState)(state));
146766
+ this.safeSave((0, import_client_ui_core54.feedbackScalarsFromState)(state));
145974
146767
  void (0, import_client_core60.sendFeedbackImpression)(this.deps.submit, "dismissed", "tui");
145975
146768
  }
145976
146769
  return true;
@@ -146045,10 +146838,10 @@ async function main() {
146045
146838
  const flags2 = parseLaunchFlags(process.argv.slice(2));
146046
146839
  const latestSaved = () => [...loadIndex()].sort((a, b2) => b2.updatedAt - a.updatedAt)[0]?.sessionId ?? null;
146047
146840
  const resumeId = flags2.resumeId ?? (flags2.continueLast ? latestSaved() : null);
146048
- const commandUsage = new import_client_ui_core55.CommandUsage(fileUsageStorage());
146841
+ const commandUsage = new import_client_ui_core56.CommandUsage(fileUsageStorage());
146049
146842
  const sessionNumber = config.sessionsStarted + 1;
146050
146843
  saveConfig({ sessionsStarted: sessionNumber });
146051
- const tips = new import_client_ui_core55.TipService({ host: "tui", state: config.tips, save: (state) => saveConfig({ tips: state }), session: sessionNumber });
146844
+ const tips = new import_client_ui_core56.TipService({ host: "tui", state: config.tips, save: (state) => saveConfig({ tips: state }), session: sessionNumber });
146052
146845
  let turnsCompleted = 0;
146053
146846
  let steeredThisSession = 0;
146054
146847
  let changedFilesSession = 0;
@@ -146275,7 +147068,7 @@ async function main() {
146275
147068
  // the store's referential stability: unchanged state returns the same object.
146276
147069
  (relPath) => transcriptStore.setState((s) => {
146277
147070
  const clientState = { ...s.clientState };
146278
- if (!(0, import_client_ui_core56.dropSeenPath)(clientState, relPath)) return s;
147071
+ if (!(0, import_client_ui_core57.dropSeenPath)(clientState, relPath)) return s;
146279
147072
  return { ...s, clientState };
146280
147073
  }),
146281
147074
  { rgPath: ripgrep.locate(), mode: config.indexMode }
@@ -146383,15 +147176,6 @@ async function main() {
146383
147176
  send: (text, attachments) => void sendTurn(text, void 0, attachments),
146384
147177
  runCommand: (text) => routeInput(text)
146385
147178
  });
146386
- const settleInterrupted = (turn) => {
146387
- if (turn.settled) return;
146388
- turn.settled = true;
146389
- transcriptStore.setState((s) => {
146390
- const hadLive = s.live !== null;
146391
- const next = applyTurnInterrupted(s, turn.promptText, turn.partialText);
146392
- return hadLive ? appendNote(next, INTERRUPT_NOTE) : next;
146393
- });
146394
- };
146395
147179
  let lastTurnComplete = null;
146396
147180
  let lastDuetExecutorStatus = null;
146397
147181
  const onRenderEvent = (rawEvent) => {
@@ -146698,19 +147482,19 @@ async function main() {
146698
147482
  controller: turn.abort,
146699
147483
  sendCancel: () => turnTransport.cancelTurn(turn.sessionId)
146700
147484
  });
146701
- appStore.setState((s) => ({ ...s, statusText: "stopping\u2026", ...clearParkedForStop(s) }));
147485
+ appStore.setState((s) => ({ ...s, stopping: true, statusText: "stopping\u2026", ...clearParkedForStop(s) }));
146702
147486
  rejectPendingWorkPlan();
146703
147487
  return;
146704
147488
  }
146705
- if (turn) settleInterrupted(turn);
147489
+ const closing = turn !== null && !turn.settled;
147490
+ if (turn) showInterrupted(transcriptStore, turn);
146706
147491
  turn?.abort.abort();
146707
147492
  rejectPendingWorkPlan();
146708
147493
  appStore.setState((s) => ({
146709
147494
  ...s,
146710
147495
  ...clearParkedForStop(s),
146711
147496
  // same one definition as the graceful branch
146712
- status: "idle",
146713
- statusText: "",
147497
+ ...closing ? { stopping: true } : { status: "idle", statusText: "", stopping: false },
146714
147498
  turnStartedAt: 0,
146715
147499
  // an interrupted turn stamps no Orbited line
146716
147500
  waitingOnPreviousTurn: false
@@ -146760,7 +147544,7 @@ async function main() {
146760
147544
  const pending = appStore.getState().pendingPlanApproval;
146761
147545
  boundary.resolvePlanApproval(requestId, decision, sid, feedback);
146762
147546
  if (pending?.requestId === requestId) {
146763
- const record = (0, import_client_ui_core56.planDecisionRecord)({ decision, feedback }, { planPath: pending.planPath });
147547
+ const record = (0, import_client_ui_core57.planDecisionRecord)({ decision, feedback }, { planPath: pending.planPath });
146764
147548
  if (record) {
146765
147549
  const note = planDecisionNote(record);
146766
147550
  transcriptStore.setState((s) => appendNote(s, note.text, note.tone ? { tone: note.tone } : void 0));
@@ -146807,7 +147591,7 @@ async function main() {
146807
147591
  notify: (sid, message, status) => {
146808
147592
  rc?.host.reportAgentNotice(sid, message, status);
146809
147593
  if (status === "expired" && sid === sessionId) {
146810
- transcriptStore.setState((s) => appendNote(s, (0, import_client_ui_core56.peerSettledRow)(message, "expired")));
147594
+ transcriptStore.setState((s) => appendNote(s, (0, import_client_ui_core57.peerSettledRow)(message, "expired")));
146811
147595
  }
146812
147596
  },
146813
147597
  onHeldChanged: (sid) => {
@@ -146816,14 +147600,14 @@ async function main() {
146816
147600
  appStore.setState((s) => ({ ...s, heldPeerMessages: waiting }));
146817
147601
  if (waiting > before) {
146818
147602
  transcriptStore.setState(
146819
- (s) => appendNote(s, `${(0, import_client_ui_core56.peerHeldSummary)(waiting)} \u2014 /messages to review`)
147603
+ (s) => appendNote(s, `${(0, import_client_ui_core57.peerHeldSummary)(waiting)} \u2014 /messages to review`)
146820
147604
  );
146821
147605
  }
146822
147606
  },
146823
147607
  onWaitingChanged: (sid) => {
146824
147608
  const arrived = waitingFriendNotes.arrived(sid, peerInbox.waitingFor(sid).length);
146825
147609
  if (arrived && sid === sessionId) {
146826
- transcriptStore.setState((s) => appendNote(s, import_client_ui_core54.FRIEND_NOTE_WAITING_ROW));
147610
+ transcriptStore.setState((s) => appendNote(s, import_client_ui_core55.FRIEND_NOTE_WAITING_ROW));
146827
147611
  }
146828
147612
  }
146829
147613
  });
@@ -147002,7 +147786,7 @@ async function main() {
147002
147786
  event: "file_modified",
147003
147787
  session_id: sid,
147004
147788
  timestamp: Date.now(),
147005
- data: { ...data, agent_label: (0, import_client_ui_core56.subagentLabel)(agent) }
147789
+ data: { ...data, agent_label: (0, import_client_ui_core57.subagentLabel)(agent) }
147006
147790
  }),
147007
147791
  getOrionResources: () => (0, import_client_core61.buildOrionSnapshot)(config.workspaceRoot, void 0, sid),
147008
147792
  // Full-dose source for review-persona children (lesson dosing) — the same
@@ -147129,11 +147913,6 @@ async function main() {
147129
147913
  disposeFallback: null,
147130
147914
  phase: "stream",
147131
147915
  hadEdits: false,
147132
- // The stop-repair inputs (see settleInterrupted): the OUTGOING prompt (a
147133
- // skill turn is identified by its typed form); the partial reply
147134
- // accumulates from this turn's own deltas in onEvent below.
147135
- promptText: start2.text ?? (start2.skill ? `/${start2.skill.name}${start2.skill.args ? ` ${start2.skill.args}` : ""}` : null),
147136
- partialText: "",
147137
147916
  settled: false
147138
147917
  };
147139
147918
  currentTurn = turn;
@@ -147169,7 +147948,7 @@ async function main() {
147169
147948
  )
147170
147949
  );
147171
147950
  if (currentTurn === turn) currentTurn = null;
147172
- appStore.setState((s) => ({ ...s, status: "idle", statusText: "" }));
147951
+ appStore.setState((s) => ({ ...s, status: "idle", statusText: "", stopping: false }));
147173
147952
  endSettle = { kind: "error", message: `custom endpoint '${name2}' is not configured` };
147174
147953
  fireTurnEnd();
147175
147954
  return;
@@ -147227,10 +148006,6 @@ async function main() {
147227
148006
  let sawEvent = false;
147228
148007
  const onEvent = (ev) => {
147229
148008
  sawEvent = true;
147230
- if (ev.event === "text_delta" && !ev.agent_id) {
147231
- const delta = ev.data?.delta;
147232
- if (typeof delta === "string") turn.partialText += delta;
147233
- }
147234
148009
  onRenderEvent(ev);
147235
148010
  };
147236
148011
  try {
@@ -147362,30 +148137,30 @@ async function main() {
147362
148137
  kind: "error",
147363
148138
  message: typeof data.error === "string" ? data.error : "turn failed"
147364
148139
  };
147365
- if (isUnrepairedStop(data)) {
147366
- settleInterrupted(turn);
147367
- } else {
147368
- turn.settled = true;
147369
- }
148140
+ turn.settled = true;
147370
148141
  }
147371
148142
  } catch (e) {
147372
148143
  if (ac.signal.aborted || turn.cancelRequested) {
147373
148144
  settleOutcome = "cancelled";
147374
148145
  endSettle = { kind: "cancelled" };
147375
- settleInterrupted(turn);
148146
+ settleInterrupted(transcriptStore, turn, (0, import_client_core62.interruptedTurnOf)(e));
147376
148147
  if (currentTurn === turn) {
147377
148148
  appStore.setState((s) => ({ ...s, status: "idle", statusText: "", error: null }));
147378
148149
  }
147379
148150
  return;
147380
148151
  }
147381
- turn.settled = true;
147382
148152
  settleOutcome = "error";
147383
148153
  const msg = e instanceof Error ? e.message : String(e);
147384
148154
  endSettle = { kind: "error", message: msg };
148155
+ const left = (0, import_client_core62.interruptedTurnOf)(e);
148156
+ if (left !== void 0 && transcriptStore.getState().sessionId === sid) {
148157
+ transcriptStore.setState((t) => applyTurnError(t, { error: msg, messages: left.messages }));
148158
+ }
148159
+ turn.settled = true;
147385
148160
  if (currentTurn === turn) {
147386
148161
  if (!sawEvent) transcriptStore.setState(() => snapshot);
147387
- const { modelLabel, model } = appStore.getState();
147388
- const failure = turnErrorNote({ error: msg }, modelLabel || displayModel(model));
148162
+ const { modelLabel, model, provider, mode } = appStore.getState();
148163
+ const failure = turnErrorNote({ error: msg }, modelLabel || displayModel(model), { provider, mode });
147389
148164
  transcriptStore.setState((t) => appendNote(t, failure.text, { tone: "error" }));
147390
148165
  appStore.setState((s) => ({ ...s, status: "error", statusText: failure.headline, error: msg }));
147391
148166
  }
@@ -147423,7 +148198,10 @@ async function main() {
147423
148198
  assistantText: endSettle.kind === "complete" ? endSettle.finalText : ""
147424
148199
  });
147425
148200
  }
147426
- if (currentTurn === turn) currentTurn = null;
148201
+ if (currentTurn === turn) {
148202
+ currentTurn = null;
148203
+ appStore.setState((s) => s.stopping ? { ...s, stopping: false } : s);
148204
+ }
147427
148205
  fireTurnEnd();
147428
148206
  }
147429
148207
  };
@@ -147688,18 +148466,23 @@ async function main() {
147688
148466
  // is defined below; the closure reads it lazily, after startup has built it.
147689
148467
  commands: { registry, contextFor: () => cmdCtx },
147690
148468
  verbs: {
147691
- send: (text, attachments) => {
147692
- if (!ensureSignedInToChat()) return false;
147693
- const outcome = sendTurn(text, void 0, attachments, "system", { from: "phone" });
147694
- return outcome === "started" || outcome === "queued";
147695
- },
148469
+ // A phone-driven turn is UNATTENDED at the terminal — origin "system" so the
148470
+ // feedback card never takes over a composer nobody is watching (the PA relies
148471
+ // on its isAttendedActive gate for the same path; the TUI has no attach state,
148472
+ // so origin is the equivalent signal). A send landing on a LIVE turn folds
148473
+ // into the steer queue (the gate — a phone can't race the terminal into two
148474
+ // concurrent turns); only a photo-bearing send declines then (the phone lane's
148475
+ // own rule, see `sendTurn`). The outcome itself goes back, so the phone hears
148476
+ // WHY a send did not start — signed out, no model chosen, photos mid-turn —
148477
+ // in the remote host's words for each.
148478
+ send: (text, attachments) => sendTurn(text, void 0, attachments, "system", { from: "phone" }),
147696
148479
  steer: (text) => enqueueMachineSteer(text),
147697
148480
  // Every note meets the receive policy first: delivered, held for the person at this
147698
148481
  // terminal, or refused — and the sender is told which (`peerInbox.ts`).
147699
148482
  deliverAgentMessage: (message) => void peerInbox.receive(sessionId, message),
147700
148483
  deliverAgentNotice: (notice) => {
147701
- transcriptStore.setState((s) => appendNote(s, (0, import_client_ui_core56.peerNoticeRow)(notice)));
147702
- heldInputs.hold((0, import_client_core62.harnessNoticeInput)((0, import_client_ui_core56.peerNoticeForModel)(notice)));
148484
+ transcriptStore.setState((s) => appendNote(s, (0, import_client_ui_core57.peerNoticeRow)(notice)));
148485
+ heldInputs.hold((0, import_client_core62.harnessNoticeInput)((0, import_client_ui_core57.peerNoticeForModel)(notice)));
147703
148486
  },
147704
148487
  agentMessagingEnabled: () => (0, import_client_core62.agentMessagesFor)(config.workspaceRoot).choice !== "off",
147705
148488
  agentMailWithoutPhone: () => (0, import_client_core62.wantsMailWithoutPhone)((0, import_client_core62.agentMessagesFor)(config.workspaceRoot)),
@@ -147815,15 +148598,15 @@ async function main() {
147815
148598
  };
147816
148599
  const showTip = (force) => {
147817
148600
  const tip = tips.next(currentTipMoment(), force);
147818
- if (tip) transcriptStore.setState((t) => appendNote(t, (0, import_client_ui_core55.tipLine)(tip), { ephemeral: true }));
148601
+ if (tip) transcriptStore.setState((t) => appendNote(t, (0, import_client_ui_core56.tipLine)(tip), { ephemeral: true }));
147819
148602
  return tip !== null;
147820
148603
  };
147821
148604
  if (sessionNumber === 1) showTip(true);
147822
148605
  {
147823
148606
  const version = tuiVersion();
147824
- if ((0, import_client_ui_core54.shouldOfferWhatsNew)(version, config.lastSeenVersion || null)) {
147825
- const url = (0, import_client_ui_core54.changelogUrl)(config.accountsUrl);
147826
- transcriptStore.setState((t) => appendNote(t, `${(0, import_client_ui_core54.whatsNewLine)(version)}${url ? ` \u2014 what changed: ${url}` : ""}`, { ephemeral: true }));
148607
+ if ((0, import_client_ui_core55.shouldOfferWhatsNew)(version, config.lastSeenVersion || null)) {
148608
+ const url = (0, import_client_ui_core55.changelogUrl)(config.accountsUrl);
148609
+ transcriptStore.setState((t) => appendNote(t, `${(0, import_client_ui_core55.whatsNewLine)(version)}${url ? ` \u2014 what changed: ${url}` : ""}`, { ephemeral: true }));
147827
148610
  }
147828
148611
  if (version && version !== config.lastSeenVersion) saveConfig({ lastSeenVersion: version });
147829
148612
  }
@@ -148012,7 +148795,7 @@ async function main() {
148012
148795
  void (0, import_client_core62.loadCustomProviders)(opts).then(panel);
148013
148796
  }
148014
148797
  };
148015
- const commandTypoGuard = new import_client_ui_core55.CommandTypoGuard(() => registry.all());
148798
+ const commandTypoGuard = new import_client_ui_core56.CommandTypoGuard(() => registry.all());
148016
148799
  const routeInput = (text, source2 = "input") => {
148017
148800
  appStore.setState((s) => s.status === "error" ? { ...s, status: "idle", statusText: "" } : s);
148018
148801
  return routeChatInput(text, {