@standardagents/code 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -451,14 +451,6 @@ var ApiClient = class {
451
451
  })
452
452
  );
453
453
  }
454
- async steerInput(threadId, mutation) {
455
- return parseSharedMessagingSnapshot(
456
- await this.json(this.messagingPath(threadId, "/steer"), {
457
- method: "POST",
458
- body: JSON.stringify(mutation)
459
- })
460
- );
461
- }
462
454
  async updatePendingInput(threadId, pendingId, mutation) {
463
455
  return parseSharedMessagingSnapshot(
464
456
  await this.json(this.messagingPath(threadId, `/pending/${encodeURIComponent(pendingId)}`), {
@@ -499,13 +491,23 @@ var ApiClient = class {
499
491
  })
500
492
  );
501
493
  }
502
- async requestSharedStop(threadId, origin2) {
503
- return parseSharedMessagingSnapshot(
504
- await this.json(this.messagingPath(threadId, "/stop"), {
505
- method: "POST",
506
- body: JSON.stringify(origin2)
507
- })
508
- );
494
+ /**
495
+ * Immediately halt a thread: the instance aborts the in-flight LLM
496
+ * request(s), marks dangling tool calls failed, and holds new turns until
497
+ * the next user message. This is Escape's HARD stop — not the old
498
+ * cooperative "stop at the next safe boundary" intent, which deferred the
499
+ * halt until the current step finished.
500
+ */
501
+ async stopThread(threadId) {
502
+ await this.json(`/api/threads/${threadId}/stop`, { method: "POST" });
503
+ }
504
+ /**
505
+ * Resume a stopped thread's execution loop. Used after a hard stop when a
506
+ * queued (steered) message should play immediately instead of waiting for
507
+ * the user's next direct send.
508
+ */
509
+ async continueThread(threadId) {
510
+ await this.json(`/api/threads/${threadId}/continue`, { method: "POST" });
509
511
  }
510
512
  async getMessages(threadId, limit = 50, order) {
511
513
  const orderParam = order ? `&order=${order}` : "";
@@ -3291,8 +3293,16 @@ var BUSY_TAIL_STALE_MS = 30 * 6e4;
3291
3293
  function toMs(raw) {
3292
3294
  return raw > 1e14 ? raw / 1e3 : raw < 1e12 ? raw * 1e3 : raw;
3293
3295
  }
3296
+ var EXECUTION_STOPPED_MARKER = "Execution stopped by user";
3294
3297
  function deriveSessionActivity(messages, nowMs = Date.now()) {
3295
- const visible = messages.filter((message) => message.silent !== true && message.metadata?.silent !== true).sort((a, b) => createdAt(a) - createdAt(b));
3298
+ const all = messages.filter((message) => message.silent !== true && message.metadata?.silent !== true).sort((a, b) => createdAt(a) - createdAt(b));
3299
+ let stopMs = 0;
3300
+ for (const message of all) {
3301
+ if (message.role === "system" && messageText(message.content).startsWith(EXECUTION_STOPPED_MARKER)) {
3302
+ stopMs = Math.max(stopMs, toMs(createdAt(message)));
3303
+ }
3304
+ }
3305
+ const visible = stopMs > 0 ? all.filter((message) => toMs(createdAt(message)) > stopMs) : all;
3296
3306
  if (visible.some((message) => message.status === "pending")) {
3297
3307
  const lastPending = [...visible].reverse().find((m) => m.status === "pending");
3298
3308
  const pendingMs = lastPending ? toMs(createdAt(lastPending)) : 0;
@@ -9316,7 +9326,7 @@ why: ${req.requestPermission}` : ""}`,
9316
9326
  const sessionEnded = new Promise((r) => endSession = r);
9317
9327
  const quit = async () => {
9318
9328
  tui.end();
9319
- const stopped2 = bridge?.isOwner ?? false ? api.requestSharedStop(threadId, messagingOrigin).catch(() => {
9329
+ const stopped2 = bridge?.isOwner ?? false ? api.stopThread(threadId).catch(() => {
9320
9330
  }) : Promise.resolve();
9321
9331
  const procsStopped2 = host ? host.stopAllLocalProcesses().catch(() => 0) : Promise.resolve(0);
9322
9332
  bridge?.close();
@@ -9442,11 +9452,6 @@ why: ${req.requestPermission}` : ""}`,
9442
9452
  attachments: [...refs, ...toSharedAttachments(images)],
9443
9453
  ...messagingOrigin
9444
9454
  }));
9445
- const steerSharedInput = (text, images, refs = []) => applySharedMutation(api.steerInput(threadId, {
9446
- content: text,
9447
- attachments: [...refs, ...toSharedAttachments(images)],
9448
- ...messagingOrigin
9449
- }));
9450
9455
  const editSharedPending = (item, text, images, refs) => applySharedMutation(api.updatePendingInput(threadId, item.id, {
9451
9456
  content: text,
9452
9457
  attachments: [
@@ -9456,7 +9461,21 @@ why: ${req.requestPermission}` : ""}`,
9456
9461
  ...messagingOrigin
9457
9462
  }));
9458
9463
  const dismissSharedPending = (item) => applySharedMutation(api.dismissPendingInput(threadId, item.id, messagingOrigin));
9459
- const promoteSharedPending = (item) => applySharedMutation(api.steerPendingInput(threadId, item.id, messagingOrigin));
9464
+ const promoteSharedPending = async (item) => {
9465
+ if (busy) {
9466
+ await Promise.all([
9467
+ api.stopThread(threadId).catch(() => {
9468
+ }),
9469
+ ...[...activeSubagents.keys()].map((childId) => api.stopThread(childId).catch(() => {
9470
+ }))
9471
+ ]);
9472
+ const promoted = await applySharedMutation(api.steerPendingInput(threadId, item.id, messagingOrigin));
9473
+ if (promoted) await api.continueThread(threadId).catch(() => {
9474
+ });
9475
+ return promoted;
9476
+ }
9477
+ return applySharedMutation(api.steerPendingInput(threadId, item.id, messagingOrigin));
9478
+ };
9460
9479
  const sendNow = async (text, images = [], refs = []) => {
9461
9480
  lastSent = { text, images, refs };
9462
9481
  tui.printUserMessage(text || `\u{1F4CE} ${refs.length + images.length} attachment(s)`);
@@ -9797,7 +9816,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9797
9816
  return;
9798
9817
  }
9799
9818
  const updated = await editSharedPending(item, text, images, draftRefs);
9800
- const promoted = !steer || !updated ? updated : await applySharedMutation(api.steerPendingInput(threadId, pendingId, messagingOrigin));
9819
+ const promoted = !steer || !updated ? updated : await promoteSharedPending({ ...item, id: pendingId });
9801
9820
  if (!promoted) {
9802
9821
  mirroredDraftRefs = item.attachments.filter(isSharedAttachmentRef);
9803
9822
  tui.setExternalAttachmentNames(mirroredDraftRefs.map((attachment) => attachment.name));
@@ -9806,8 +9825,14 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9806
9825
  return;
9807
9826
  }
9808
9827
  if (steer) {
9809
- tui.print(`${c4.yellow}\u21AA steering at the next safe model boundary:${c4.reset} ${text}`);
9810
- if (!await steerSharedInput(text, images, draftRefs)) {
9828
+ tui.print(`${c4.yellow}\u21AA steering now \u2014 stopping the current step${c4.reset}`);
9829
+ await Promise.all([
9830
+ api.stopThread(threadId).catch(() => {
9831
+ }),
9832
+ ...[...activeSubagents.keys()].map((childId) => api.stopThread(childId).catch(() => {
9833
+ }))
9834
+ ]);
9835
+ if (!await sendNow(text, images, draftRefs)) {
9811
9836
  mirroredDraftRefs = draftRefs;
9812
9837
  tui.setExternalAttachmentNames(draftRefs.map((attachment) => attachment.name));
9813
9838
  tui.setInput(text, images);
@@ -9844,13 +9869,15 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9844
9869
  return;
9845
9870
  }
9846
9871
  if (busy) {
9847
- if (!sharedMessagingReady) {
9848
- tui.print(`${c4.dim}Shared messaging is not connected; the session was not stopped.${c4.reset}`);
9849
- return;
9872
+ const queued = sharedMessaging.pending.items.length;
9873
+ tui.print(`${c4.yellow}\u25A0 stopping now${queued > 0 ? ` \u2014 ${queued} queued message${queued === 1 ? "" : "s"} kept` : ""}${c4.reset}`);
9874
+ const stops = [api.stopThread(threadId).catch(() => {
9875
+ })];
9876
+ for (const childId of activeSubagents.keys()) {
9877
+ stops.push(api.stopThread(childId).catch(() => {
9878
+ }));
9850
9879
  }
9851
- const advancing = sharedMessaging.pending.items.length > 0;
9852
- tui.print(`${c4.yellow}${advancing ? "[stopping; next pending message will run]" : "[stopping at the next safe boundary]"}${c4.reset}`);
9853
- void applySharedMutation(api.requestSharedStop(threadId, messagingOrigin));
9880
+ void Promise.all(stops);
9854
9881
  }
9855
9882
  };
9856
9883
  tui.onBgBadge = () => {
@@ -10061,7 +10088,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10061
10088
  clearInterval(pollTimer);
10062
10089
  clearInterval(heartbeatPoll);
10063
10090
  process.off("SIGCONT", onTerminalResume);
10064
- const stopped = bridge?.isOwner ?? false ? api.requestSharedStop(threadId, messagingOrigin).catch(() => {
10091
+ const stopped = bridge?.isOwner ?? false ? api.stopThread(threadId).catch(() => {
10065
10092
  }) : Promise.resolve();
10066
10093
  const procsStopped = host ? host.stopAllLocalProcesses().catch(() => 0) : Promise.resolve(0);
10067
10094
  bridge?.close();
@@ -10328,7 +10355,7 @@ function showKeybindings(tui) {
10328
10355
  tui.print(`${c4.gray}shortcuts:${c4.reset}`);
10329
10356
  tui.print(`${c4.gray} shift-tab${c4.reset} cycle auto-accept level (1\u20135)`);
10330
10357
  tui.print(`${c4.gray} shift-\u23CE${c4.reset} insert a newline (multiline input)`);
10331
- tui.print(`${c4.gray} option-\u23CE${c4.reset} steer the working agent at the next safe boundary`);
10358
+ tui.print(`${c4.gray} option-\u23CE${c4.reset} steer now \u2014 stops the current step and plays your message`);
10332
10359
  tui.print(`${c4.gray} /${c4.reset} open the command palette (type to filter)`);
10333
10360
  tui.print(`${c4.gray} !cmd${c4.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
10334
10361
  tui.print(`${c4.gray} ctrl-v${c4.reset} paste an image from the clipboard ([#Image 1])`);