replicas-engine 0.1.986 → 0.1.987

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.
@@ -5,12 +5,12 @@ import {
5
5
  getCodexAspHost,
6
6
  restartCodexAspHost,
7
7
  restartCodexAspHostIfRunning
8
- } from "./chunk-O2ZKAC2G.js";
9
- import "./chunk-Z3D5SVUI.js";
10
- import "./chunk-SAJ6ERGR.js";
11
- import "./chunk-4WBOMDAN.js";
8
+ } from "./chunk-D5B72SUV.js";
9
+ import "./chunk-TYH3IEXZ.js";
10
+ import "./chunk-KAJYMTYA.js";
11
+ import "./chunk-2PMHC6IL.js";
12
12
  import "./chunk-UZSNFLDQ.js";
13
- import "./chunk-IIANP4GI.js";
13
+ import "./chunk-QU4FXL4X.js";
14
14
  import "./chunk-VEQXQN22.js";
15
15
  export {
16
16
  getCodexAspHost,
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  HOOK_EXEC_MAX_BUFFER_BYTES
6
- } from "./chunk-IIANP4GI.js";
6
+ } from "./chunk-QU4FXL4X.js";
7
7
 
8
8
  // src/utils/exec.ts
9
9
  import { exec, execFile } from "child_process";
@@ -5,7 +5,7 @@ import {
5
5
  extractCommandProtectionCommandText,
6
6
  isRecord,
7
7
  parseCodeHostPrUrl
8
- } from "./chunk-IIANP4GI.js";
8
+ } from "./chunk-QU4FXL4X.js";
9
9
 
10
10
  // src/services/published-pull-request-urls.ts
11
11
  function publishedPullRequestUrls(result) {
@@ -5,15 +5,15 @@ import {
5
5
  ENGINE_ENV,
6
6
  monolithRequest,
7
7
  setAgentCredentialSnapshot
8
- } from "./chunk-Z3D5SVUI.js";
8
+ } from "./chunk-TYH3IEXZ.js";
9
9
  import {
10
10
  AppServerProcess,
11
11
  buildCodexAgentEnv
12
- } from "./chunk-SAJ6ERGR.js";
12
+ } from "./chunk-KAJYMTYA.js";
13
13
  import {
14
14
  SUBPROCESS_MAX_BUFFER,
15
15
  execFileAsync
16
- } from "./chunk-4WBOMDAN.js";
16
+ } from "./chunk-2PMHC6IL.js";
17
17
  import {
18
18
  isRecord
19
19
  } from "./chunk-UZSNFLDQ.js";
@@ -27,7 +27,7 @@ import {
27
27
  listOpenAICompatibleModels,
28
28
  readOllamaAuthEnv,
29
29
  resolveOpenAICompatibleModelsUrl
30
- } from "./chunk-IIANP4GI.js";
30
+ } from "./chunk-QU4FXL4X.js";
31
31
 
32
32
  // src/managers/codex-token-manager.ts
33
33
  import { promises as fs } from "fs";
@@ -260,7 +260,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
260
260
  await this.applyCredentialUpdate(++this.credentialGeneration, async () => {
261
261
  await this.applyCredentialsResponse(data);
262
262
  setAgentCredentialSnapshot("codex", data);
263
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-Q474L67V.js");
263
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-LOTCGOVD.js");
264
264
  await restartCodexAspHostIfRunning2();
265
265
  });
266
266
  await this.start(true);
@@ -280,7 +280,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
280
280
  await this.applyCredentialUpdate(generation, async () => {
281
281
  await this.applyCredentialsResponse(data);
282
282
  if (restartOnChange && CODEX_AUTH_ENV_KEYS.some((key, index) => process.env[key] !== previousEnv[index])) {
283
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-Q474L67V.js");
283
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-LOTCGOVD.js");
284
284
  await restartCodexAspHostIfRunning2();
285
285
  }
286
286
  setAgentCredentialSnapshot("codex", data);
@@ -3,18 +3,18 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  monolithRequest
6
- } from "./chunk-Z3D5SVUI.js";
6
+ } from "./chunk-TYH3IEXZ.js";
7
7
  import {
8
8
  SUBPROCESS_MAX_BUFFER,
9
9
  execFileAsync
10
- } from "./chunk-4WBOMDAN.js";
10
+ } from "./chunk-2PMHC6IL.js";
11
11
  import {
12
12
  extractCommandProtectionCommandText,
13
13
  findCommandProtectionGitLookups,
14
14
  findGitCommitSignals,
15
15
  findPrMergeSignals,
16
16
  parseCommandProtectionRequest
17
- } from "./chunk-IIANP4GI.js";
17
+ } from "./chunk-QU4FXL4X.js";
18
18
 
19
19
  // src/services/command-protection-service.ts
20
20
  import { readFile, stat } from "fs/promises";
@@ -3,10 +3,10 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  execFileAsync
6
- } from "./chunk-4WBOMDAN.js";
6
+ } from "./chunk-2PMHC6IL.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-IIANP4GI.js";
9
+ } from "./chunk-QU4FXL4X.js";
10
10
 
11
11
  // src/utils/codex-agent-env.ts
12
12
  function buildCodexAgentEnv(source = process.env) {
@@ -240,7 +240,7 @@ function getCodexModelProvider(method) {
240
240
  if (method === "foundry") return "azure";
241
241
  return method === "ollama" || method === "openai-compatible" ? "replicas_openai_compatible" : "openai";
242
242
  }
243
- var ENGINE_PACKAGE_VERSION = "0.1.986";
243
+ var ENGINE_PACKAGE_VERSION = "0.1.987";
244
244
  var INITIALIZE_METHOD = "initialize";
245
245
  var INITIALIZED_NOTIFICATION = "initialized";
246
246
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -1194,7 +1194,8 @@ var subagentResponseSchema = z3.object({
1194
1194
  chatId: z3.string().uuid(),
1195
1195
  status: z3.enum(["working", "completed"]),
1196
1196
  response: z3.string().optional(),
1197
- timedOut: z3.literal(true).optional()
1197
+ timedOut: z3.literal(true).optional(),
1198
+ interruptedByUserMessage: z3.literal(true).optional()
1198
1199
  });
1199
1200
  var integrationErrorNotificationTargetSchema = z3.discriminatedUnion("type", [
1200
1201
  z3.object({
@@ -14,7 +14,7 @@ import {
14
14
  isValidAgentProvider,
15
15
  parsePosixEnvFile,
16
16
  readReplicasRuntimeEnv
17
- } from "./chunk-IIANP4GI.js";
17
+ } from "./chunk-QU4FXL4X.js";
18
18
 
19
19
  // src/runtime-env-loader.ts
20
20
  import { readFileSync } from "fs";
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  evaluateCommandProtection
6
- } from "./chunk-B2JQRHF4.js";
7
- import "./chunk-Z3D5SVUI.js";
8
- import "./chunk-4WBOMDAN.js";
6
+ } from "./chunk-G6QBYUZR.js";
7
+ import "./chunk-TYH3IEXZ.js";
8
+ import "./chunk-2PMHC6IL.js";
9
9
  import {
10
10
  isRecord
11
11
  } from "./chunk-UZSNFLDQ.js";
12
- import "./chunk-IIANP4GI.js";
12
+ import "./chunk-QU4FXL4X.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/command-protection-hook.ts
@@ -7,12 +7,12 @@ import {
7
7
  import {
8
8
  AppServerProcess,
9
9
  buildCodexAgentEnv
10
- } from "./chunk-SAJ6ERGR.js";
11
- import "./chunk-4WBOMDAN.js";
10
+ } from "./chunk-KAJYMTYA.js";
11
+ import "./chunk-2PMHC6IL.js";
12
12
  import {
13
13
  AGENT,
14
14
  headlessAgentRequestSchema
15
- } from "./chunk-IIANP4GI.js";
15
+ } from "./chunk-QU4FXL4X.js";
16
16
  import "./chunk-VEQXQN22.js";
17
17
 
18
18
  // src/headless-agent.ts
package/dist/src/index.js CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  reportCommandProtectionBlock,
33
33
  resolveDiffBase,
34
34
  runGitCommand
35
- } from "./chunk-B2JQRHF4.js";
35
+ } from "./chunk-G6QBYUZR.js";
36
36
  import {
37
37
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
38
38
  AGENT_MESSAGE_DELTA_METHOD,
@@ -57,7 +57,7 @@ import {
57
57
  } from "./chunk-P73LGPQO.js";
58
58
  import {
59
59
  publishedPullRequestUrls
60
- } from "./chunk-YQE57AE6.js";
60
+ } from "./chunk-5EHMJ4OT.js";
61
61
  import {
62
62
  BaseRefreshManager,
63
63
  CodexAspAuthMethodChangedError,
@@ -72,7 +72,7 @@ import {
72
72
  refreshDelayMs,
73
73
  restartCodexAspHost,
74
74
  routineRefreshExclusions
75
- } from "./chunk-O2ZKAC2G.js";
75
+ } from "./chunk-D5B72SUV.js";
76
76
  import {
77
77
  ENGINE_ENV,
78
78
  monolithRequest,
@@ -80,16 +80,16 @@ import {
80
80
  readRuntimeEnvFile,
81
81
  reloadEngineEnv,
82
82
  setAgentCredentialSnapshot
83
- } from "./chunk-Z3D5SVUI.js";
83
+ } from "./chunk-TYH3IEXZ.js";
84
84
  import {
85
85
  AspClient,
86
86
  getCodexModelProvider
87
- } from "./chunk-SAJ6ERGR.js";
87
+ } from "./chunk-KAJYMTYA.js";
88
88
  import {
89
89
  SUBPROCESS_MAX_BUFFER,
90
90
  execAsync,
91
91
  execFileAsync
92
- } from "./chunk-4WBOMDAN.js";
92
+ } from "./chunk-2PMHC6IL.js";
93
93
  import {
94
94
  isRecord as isRecord2
95
95
  } from "./chunk-UZSNFLDQ.js";
@@ -315,7 +315,7 @@ import {
315
315
  workspaceCredentialProviderSchema,
316
316
  workspaceTokenRequestSchema,
317
317
  workspaceTokenResponseSchema
318
- } from "./chunk-IIANP4GI.js";
318
+ } from "./chunk-QU4FXL4X.js";
319
319
  import {
320
320
  cloneMcpConfig,
321
321
  ensureCompatibilityImports,
@@ -10166,6 +10166,7 @@ var EnvironmentDetailsService = class {
10166
10166
  details.engineVersion = ENGINE_ENV.REPLICAS_SANDBOX_IMAGE_VERSION;
10167
10167
  details.supportsMidTurnSteering = true;
10168
10168
  details.supportsClaudeMidTurnSteering = true;
10169
+ details.supportsSendNow = true;
10169
10170
  details.supportsClaudeGoals = true;
10170
10171
  details.supportsGoalUpdates = true;
10171
10172
  details.supportsQueuedMessageEditing = true;
@@ -11769,6 +11770,7 @@ var MessageQueueService = class {
11769
11770
  // version below.
11770
11771
  acceptanceWithdrawals = /* @__PURE__ */ new WeakSet();
11771
11772
  queueCancellationVersion = 0;
11773
+ activeTurn = null;
11772
11774
  constructor(processMessage, onProcessingChanged = () => {
11773
11775
  }, onMessageStarted = async () => {
11774
11776
  }) {
@@ -11863,6 +11865,8 @@ var MessageQueueService = class {
11863
11865
  return tail.merge === true && tail.type === request.type;
11864
11866
  }
11865
11867
  async startProcessing(queuedMessage) {
11868
+ const turn = new AbortController();
11869
+ this.activeTurn = turn;
11866
11870
  this.processing = true;
11867
11871
  this.onProcessingChanged(true);
11868
11872
  try {
@@ -11874,11 +11878,14 @@ var MessageQueueService = class {
11874
11878
  this.acceptanceByMessage.delete(queuedMessage);
11875
11879
  break;
11876
11880
  }
11877
- await this.onMessageStarted(queuedMessage);
11878
- await this.processMessage(queuedMessage);
11881
+ if (!turn.signal.aborted) {
11882
+ await this.onMessageStarted(queuedMessage);
11883
+ await this.processMessage(queuedMessage, turn.signal);
11884
+ }
11879
11885
  } catch (error) {
11880
11886
  console.error("[MessageQueue] Error processing message:", error);
11881
11887
  } finally {
11888
+ this.activeTurn = null;
11882
11889
  this.processing = false;
11883
11890
  if (this.queue.length === 0) {
11884
11891
  this.onProcessingChanged(false);
@@ -11898,22 +11905,15 @@ var MessageQueueService = class {
11898
11905
  isProcessing() {
11899
11906
  return this.processing;
11900
11907
  }
11901
- drainQueue(options) {
11902
- const maxItems = options?.maxItems ?? this.queue.length;
11903
- const maxChars = options?.maxChars ?? Number.POSITIVE_INFINITY;
11904
- const drained = [];
11905
- let totalChars = 0;
11906
- for (const message of this.queue) {
11907
- if (drained.length >= maxItems) break;
11908
- const text = message.message;
11909
- if (totalChars + text.length > maxChars) break;
11910
- drained.push(text);
11911
- totalChars += text.length;
11912
- }
11908
+ drainQueue() {
11909
+ const drained = this.queue;
11913
11910
  this.queueCancellationVersion++;
11914
11911
  this.queue = [];
11915
11912
  return drained;
11916
11913
  }
11914
+ cancelActiveTurn() {
11915
+ this.activeTurn?.abort();
11916
+ }
11917
11917
  /**
11918
11918
  * Get all queued messages (does not include the currently processing message)
11919
11919
  */
@@ -11973,6 +11973,16 @@ Native subagent tools are disabled in this workspace. To delegate work to a suba
11973
11973
  // src/managers/coding-agent-manager.ts
11974
11974
  var MAX_INTERRUPT_QUEUE_ITEMS = 1e3;
11975
11975
  var MAX_INTERRUPT_QUEUE_CHARS = 2e5;
11976
+ function toQueuedMessageInfo(message) {
11977
+ return {
11978
+ id: message.id,
11979
+ message: message.message,
11980
+ queuedAt: message.queuedAt,
11981
+ ...message.senderUserId ? { senderUserId: message.senderUserId } : {},
11982
+ ...message.senderEmail ? { senderEmail: message.senderEmail } : {},
11983
+ ...message.senderDisplayName ? { senderDisplayName: message.senderDisplayName } : {}
11984
+ };
11985
+ }
11976
11986
  var subagentMcpConfigRequest = null;
11977
11987
  var CodingAgentManager = class {
11978
11988
  workingDirectory;
@@ -11984,13 +11994,14 @@ var CodingAgentManager = class {
11984
11994
  onUsageUpdate;
11985
11995
  linearSessionId;
11986
11996
  chatId;
11987
- onTurnComplete;
11997
+ hostOnTurnComplete;
11988
11998
  onProcessingChanged;
11989
11999
  onMessageStarted;
11990
12000
  onModelSwitched;
11991
12001
  authFallback;
11992
12002
  subagentMcpChatId;
11993
12003
  subagentMcpEnabledForTurn = false;
12004
+ unreadSteers = /* @__PURE__ */ new Map();
11994
12005
  constructor(options) {
11995
12006
  this.workingDirectory = options.workingDirectory ?? ENGINE_ENV.WORKSPACE_ROOT;
11996
12007
  this.initialSessionId = options.initialSessionId;
@@ -12001,7 +12012,7 @@ var CodingAgentManager = class {
12001
12012
  this.subagentMcpChatId = options.parentChatId === null && options.provider && SUBAGENT_MCP_HARNESSES.includes(options.provider) ? options.chatId : void 0;
12002
12013
  this.linearSessionId = options.linearSessionId;
12003
12014
  this.chatId = options.chatId;
12004
- this.onTurnComplete = options.onTurnComplete;
12015
+ this.hostOnTurnComplete = options.onTurnComplete;
12005
12016
  this.onProcessingChanged = options.onProcessingChanged ?? (() => {
12006
12017
  });
12007
12018
  this.onMessageStarted = options.onMessageStarted ?? (async () => {
@@ -12019,6 +12030,10 @@ var CodingAgentManager = class {
12019
12030
  if (result.outcome === "switched" && result.model) await this.onModelSwitched(result.model);
12020
12031
  return result;
12021
12032
  }
12033
+ onTurnComplete = async () => {
12034
+ this.requeueUnreadSteers();
12035
+ await this.hostOnTurnComplete();
12036
+ };
12022
12037
  emitSyntheticEvent(type, payload) {
12023
12038
  this.onEvent({
12024
12039
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12061,9 +12076,10 @@ var CodingAgentManager = class {
12061
12076
  }
12062
12077
  initializeManager(processMessage) {
12063
12078
  this.messageQueue = new MessageQueueService(
12064
- async (request) => {
12079
+ async (request, signal) => {
12065
12080
  await this.refreshSubagentMcp();
12066
- await processMessage(request);
12081
+ if (signal.aborted) return;
12082
+ await processMessage(request, signal);
12067
12083
  },
12068
12084
  this.onProcessingChanged,
12069
12085
  this.onMessageStarted
@@ -12091,46 +12107,55 @@ var CodingAgentManager = class {
12091
12107
  if (!history?.appendDurably) throw new Error("Chat history is unavailable");
12092
12108
  await history.appendDurably(event);
12093
12109
  }
12094
- async interrupt() {
12095
- const queuedMessages = this.messageQueue.getQueue();
12096
- const queue = this.messageQueue.drainQueue({
12097
- maxItems: MAX_INTERRUPT_QUEUE_ITEMS,
12098
- maxChars: MAX_INTERRUPT_QUEUE_CHARS
12099
- });
12110
+ async interrupt(persistRemoval) {
12111
+ const queuedMessages = this.messageQueue.drainQueue();
12112
+ const unreadSteers = [...this.unreadSteers.values()];
12113
+ this.unreadSteers.clear();
12114
+ this.messageQueue.cancelActiveTurn();
12100
12115
  const isProcessing = this.isProcessing();
12101
12116
  if (isProcessing) {
12102
12117
  try {
12103
12118
  await this.interruptActiveTurn();
12104
12119
  } catch (error) {
12105
- this.restoreQueuedMessages(queuedMessages.slice(0, queue.length).map((message) => ({ message, index: 0 })));
12120
+ for (const steer of unreadSteers) this.unreadSteers.set(steer.message.id, steer);
12121
+ this.restoreQueuedMessages(queuedMessages.map((message, index) => ({ message, index })));
12106
12122
  throw error;
12107
12123
  }
12108
- const event = {
12109
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12110
- type: CHAT_INTERRUPTED_EVENT_TYPE,
12111
- payload: {}
12112
- };
12113
- this.onEvent(event);
12114
- this.getHistorySink()?.append(event);
12124
+ }
12125
+ const removed = [...unreadSteers.map(({ message }) => message), ...queuedMessages];
12126
+ if (removed.length > 0) await persistRemoval(removed.map(({ id }) => id));
12127
+ if (isProcessing) this.recordInterrupted();
12128
+ const queue = [];
12129
+ let queueChars = 0;
12130
+ for (const { message } of removed) {
12131
+ if (queue.length >= MAX_INTERRUPT_QUEUE_ITEMS || queueChars + message.length > MAX_INTERRUPT_QUEUE_CHARS) break;
12132
+ queue.push(message);
12133
+ queueChars += message.length;
12115
12134
  }
12116
12135
  this.emitInterruptedQueueEvent(queue);
12117
12136
  return {
12118
- interrupted: isProcessing || queue.length > 0,
12119
- queue
12137
+ interrupted: isProcessing || removed.length > 0,
12138
+ queue,
12139
+ removedMessageIds: removed.map(({ id }) => id)
12140
+ };
12141
+ }
12142
+ recordInterrupted() {
12143
+ const event = {
12144
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
12145
+ type: CHAT_INTERRUPTED_EVENT_TYPE,
12146
+ payload: {}
12120
12147
  };
12148
+ this.onEvent(event);
12149
+ this.getHistorySink()?.append(event);
12121
12150
  }
12122
12151
  isProcessing() {
12123
12152
  return this.messageQueue.isProcessing();
12124
12153
  }
12125
12154
  getQueue() {
12126
- return this.messageQueue.getQueue().map((m) => ({
12127
- id: m.id,
12128
- message: m.message,
12129
- queuedAt: m.queuedAt,
12130
- ...m.senderUserId ? { senderUserId: m.senderUserId } : {},
12131
- ...m.senderEmail ? { senderEmail: m.senderEmail } : {},
12132
- ...m.senderDisplayName ? { senderDisplayName: m.senderDisplayName } : {}
12133
- }));
12155
+ return [
12156
+ ...[...this.unreadSteers.values()].map(({ message, steeredAt }) => ({ ...toQueuedMessageInfo(message), steeredAt })),
12157
+ ...this.messageQueue.getQueue().map(toQueuedMessageInfo)
12158
+ ];
12134
12159
  }
12135
12160
  async removeFromQueue(messageId, persistRemoval) {
12136
12161
  return this.removeQueuedMessages([messageId], persistRemoval);
@@ -12176,33 +12201,54 @@ var CodingAgentManager = class {
12176
12201
  if (!taken) {
12177
12202
  return { success: false, queue: this.getQueue() };
12178
12203
  }
12179
- const keepQueued = this.keepSteeredMessageQueued();
12180
- if (keepQueued) {
12181
- this.messageQueue.restoreToQueue(taken.message, 0);
12182
- }
12183
12204
  let steered = false;
12184
12205
  try {
12185
12206
  steered = await this.steerRequest(taken.message);
12186
12207
  } catch (error) {
12187
12208
  console.error("[CodingAgentManager] steerRequest threw, keeping message queued:", error);
12188
12209
  }
12189
- if (!steered) {
12190
- if (keepQueued) {
12191
- const prioritized = this.messageQueue.takeFromQueue(messageId);
12192
- if (prioritized) this.restoreQueuedMessages([{ message: prioritized.message, index: taken.index }]);
12193
- } else {
12194
- this.restoreQueuedMessages([taken]);
12210
+ if (steered) this.unreadSteers.set(messageId, { message: taken.message, steeredAt: (/* @__PURE__ */ new Date()).toISOString() });
12211
+ else this.restoreQueuedMessages([{ message: taken.message, index: 0 }]);
12212
+ return { success: steered, queue: this.getQueue() };
12213
+ }
12214
+ async sendNow(messageId) {
12215
+ await this.initialized;
12216
+ const queued = this.messageQueue.getQueue().some(({ id }) => id === messageId);
12217
+ if (!queued && !this.unreadSteers.has(messageId)) return { success: false, queue: this.getQueue() };
12218
+ const turnRunning = this.messageQueue.isProcessing();
12219
+ if (turnRunning && this.canDeliverSteersNow()) {
12220
+ if (queued) await this.steerFromQueue(messageId);
12221
+ if (this.unreadSteers.has(messageId)) {
12222
+ await this.deliverSteersNow();
12223
+ return { success: true, queue: this.getQueue() };
12195
12224
  }
12196
12225
  }
12197
- return {
12198
- success: steered,
12199
- queue: this.getQueue(),
12200
- ...steered ? { steeredAt: (/* @__PURE__ */ new Date()).toISOString() } : {}
12201
- };
12226
+ this.requeueUnreadSteers();
12227
+ const taken = this.messageQueue.takeFromQueue(messageId);
12228
+ if (taken) this.messageQueue.restoreToQueue(taken.message, 0);
12229
+ if (turnRunning) {
12230
+ this.recordInterrupted();
12231
+ this.messageQueue.cancelActiveTurn();
12232
+ await this.interruptActiveTurn();
12233
+ }
12234
+ return { success: true, queue: this.getQueue() };
12235
+ }
12236
+ requeueUnreadSteers() {
12237
+ const steers = [...this.unreadSteers.values()].map(({ message }) => message);
12238
+ this.unreadSteers.clear();
12239
+ this.restoreQueuedMessages(steers.map((message) => ({ message, index: 0 })));
12240
+ }
12241
+ markSteerRead(messageId) {
12242
+ const steer = this.unreadSteers.get(messageId);
12243
+ this.unreadSteers.delete(messageId);
12244
+ return steer?.message ?? this.messageQueue.takeFromQueue(messageId)?.message;
12202
12245
  }
12203
- keepSteeredMessageQueued() {
12246
+ canDeliverSteersNow() {
12204
12247
  return false;
12205
12248
  }
12249
+ deliverSteersNow() {
12250
+ return Promise.resolve();
12251
+ }
12206
12252
  steerRequest(_request) {
12207
12253
  return Promise.resolve(false);
12208
12254
  }
@@ -13035,11 +13081,19 @@ function isClaudeSessionActive(activity) {
13035
13081
  // src/managers/claude-background-tasks.ts
13036
13082
  var ClaudeBackgroundTasks = class {
13037
13083
  active = /* @__PURE__ */ new Set();
13084
+ foreground = /* @__PURE__ */ new Set();
13038
13085
  reset() {
13039
13086
  this.active.clear();
13087
+ this.foreground.clear();
13040
13088
  }
13041
13089
  update(message) {
13042
- if (message.type !== "system" || message.subtype !== "background_tasks_changed") return;
13090
+ if (message.type !== "system") return;
13091
+ if (message.subtype === "task_started" && message.is_backgrounded === false) this.foreground.add(message.task_id);
13092
+ if (message.subtype === "task_notification") this.foreground.delete(message.task_id);
13093
+ if (message.subtype === "task_updated" && (message.patch.is_backgrounded || message.patch.status && message.patch.status !== "running")) {
13094
+ this.foreground.delete(message.task_id);
13095
+ }
13096
+ if (message.subtype !== "background_tasks_changed") return;
13043
13097
  this.active.clear();
13044
13098
  for (const task of message.tasks) {
13045
13099
  if (!task.ambient) this.active.add(task.task_id);
@@ -13359,25 +13413,31 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13359
13413
  await this.abortAllPendingToolInputs();
13360
13414
  await this.tearDownSession(true);
13361
13415
  }
13416
+ canDeliverSteersNow() {
13417
+ return this.activeQuery !== null && this.backgroundTasks.foreground.size > 0;
13418
+ }
13419
+ async deliverSteersNow() {
13420
+ await this.activeQuery?.backgroundTasks();
13421
+ }
13362
13422
  // Claude Code folds a `next`-priority prompt into the running turn at its next tool boundary.
13363
13423
  async steerRequest(request) {
13364
13424
  const pendingTurn = this.pendingTurn;
13365
13425
  const promptStream = this.activePromptStream;
13366
13426
  if (!pendingTurn || !promptStream) return false;
13367
- if (getGoalCommand(request.message, request.goalMode)) {
13368
- this.messageQueue.restoreToQueue(request, 0);
13369
- await this.interruptActiveTurn();
13370
- return true;
13371
- }
13427
+ if (getGoalCommand(request.message, request.goalMode)) return false;
13372
13428
  const userMessage = await this.buildUserMessage(request);
13373
13429
  if (this.pendingTurn !== pendingTurn || this.activePromptStream !== promptStream) return false;
13374
13430
  pendingTurn.userMessageUuids.add(userMessage.uuid);
13375
- const steer = { ...userMessage, priority: "next" };
13376
- pendingTurn.steered.push(steer);
13377
- await this.recordEvent(userMessage, request.messageId);
13378
- promptStream.push(steer);
13431
+ pendingTurn.steers.set(userMessage.uuid, { messageId: request.id, userMessage });
13432
+ promptStream.push({ ...userMessage, priority: "next" });
13379
13433
  return true;
13380
13434
  }
13435
+ async recordSteerRead(uuid) {
13436
+ const steer = this.pendingTurn?.steers.get(uuid);
13437
+ if (!steer) return;
13438
+ this.pendingTurn?.steers.delete(uuid);
13439
+ if (this.markSteerRead(steer.messageId)) await this.recordEvent(steer.userMessage, steer.messageId);
13440
+ }
13381
13441
  async abortAllPendingToolInputs() {
13382
13442
  const pending = Array.from(this.pendingToolInputs.values());
13383
13443
  this.pendingToolInputs.clear();
@@ -13633,7 +13693,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13633
13693
  }
13634
13694
  }
13635
13695
  }
13636
- async processMessageInternal(request) {
13696
+ async processMessageInternal(request, signal) {
13637
13697
  let lastError;
13638
13698
  let authRetryExhausted = false;
13639
13699
  let quotaFallbacks = 0;
@@ -13643,7 +13703,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13643
13703
  let midTurnContinues = 0;
13644
13704
  let missingSessionReset = false;
13645
13705
  let currentRequest = request;
13646
- let carriedSteers = [];
13706
+ let carriedSteers = /* @__PURE__ */ new Map();
13647
13707
  await this.goalClear?.catch(() => {
13648
13708
  });
13649
13709
  try {
@@ -13651,10 +13711,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13651
13711
  await this.turnCredentials?.refresh();
13652
13712
  while (true) {
13653
13713
  const attemptedCredential = this.turnCredentials ? this.turnCredentials.snapshot() : ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.claude;
13654
- const steered = carriedSteers;
13655
- carriedSteers = [];
13714
+ const steers = carriedSteers;
13715
+ carriedSteers = /* @__PURE__ */ new Map();
13656
13716
  try {
13657
- await this.executeQuery(currentRequest, { skipUserMessageRecord: attempt > 0, steered });
13717
+ await this.executeQuery(currentRequest, { skipUserMessageRecord: attempt > 0, signal, steers });
13658
13718
  return;
13659
13719
  } catch (error) {
13660
13720
  lastError = error;
@@ -13699,7 +13759,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13699
13759
  missingSessionReset = true;
13700
13760
  console.warn(`[ClaudeManager] Session ${this.sessionId} has no transcript; starting a fresh session:`, error.message);
13701
13761
  this.sessionId = null;
13702
- carriedSteers = steered;
13762
+ carriedSteers = steers;
13703
13763
  await this.onSaveSessionId(null);
13704
13764
  await this.tearDownSession();
13705
13765
  attempt++;
@@ -13882,21 +13942,21 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
13882
13942
  if (!promptStream || !activeQuery) {
13883
13943
  throw new Error("Claude session unavailable after ensureSession");
13884
13944
  }
13885
- if (this.pendingInterrupt) {
13945
+ if (this.pendingInterrupt || options.signal?.aborted) {
13886
13946
  this.pendingInterrupt = false;
13887
13947
  return;
13888
13948
  }
13889
13949
  await new Promise((resolve7, reject) => {
13890
- const steered = options.steered ?? [];
13950
+ const steers = options.steers ?? /* @__PURE__ */ new Map();
13891
13951
  this.pendingTurn = {
13892
13952
  resolve: resolve7,
13893
13953
  reject,
13894
13954
  sawActivity: false,
13895
- steered,
13896
- userMessageUuids: /* @__PURE__ */ new Set([userMessage.uuid, ...steered.flatMap((steer) => steer.uuid ?? [])])
13955
+ steers,
13956
+ userMessageUuids: /* @__PURE__ */ new Set([userMessage.uuid, ...steers.keys()])
13897
13957
  };
13898
13958
  promptStream.push(goalCommand ? { ...userMessage, message: { role: "user", content: `/goal ${goalObjective ?? "clear"}` } } : userMessage);
13899
- for (const steer of steered) promptStream.push(steer);
13959
+ for (const { userMessage: steer } of steers.values()) promptStream.push({ ...steer, priority: "next" });
13900
13960
  });
13901
13961
  }
13902
13962
  async buildUserMessage({ message, images }) {
@@ -14119,6 +14179,10 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14119
14179
  }
14120
14180
  const wasProcessing = this.isProcessing();
14121
14181
  this.setAuthRetrying(false);
14182
+ const lifecycle2 = msg;
14183
+ if (isRecord(lifecycle2) && lifecycle2.type === "command_lifecycle" && lifecycle2.state === "started" && typeof lifecycle2.command_uuid === "string") {
14184
+ await this.recordSteerRead(lifecycle2.command_uuid);
14185
+ }
14122
14186
  if (this.pendingTurn && _ClaudeManager.isTurnActivity(msg)) {
14123
14187
  this.pendingTurn.sawActivity = true;
14124
14188
  }
@@ -14145,6 +14209,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14145
14209
  if (this.activeQuery !== response) break;
14146
14210
  this.sessionLinearForwarder?.flushThoughtAsResponse();
14147
14211
  const pendingUuids = this.pendingTurn?.userMessageUuids;
14212
+ for (const uuid of msg.user_message_uuids ?? []) await this.recordSteerRead(uuid);
14148
14213
  if (msg.user_message_uuid) {
14149
14214
  for (const uuid of msg.user_message_uuids ?? [msg.user_message_uuid]) pendingUuids?.delete(uuid);
14150
14215
  if (pendingUuids?.size === 0) this.resolvePendingTurn();
@@ -15308,10 +15373,13 @@ var CodexAspManager = class extends CodingAgentManager {
15308
15373
  async subagentMcpForTurn(enabled) {
15309
15374
  return this.currentThreadId ? existsSync9(join17(SUBAGENT_MCP_THREADS_DIR, this.currentThreadId)) : enabled;
15310
15375
  }
15311
- async interruptActiveTurn() {
15376
+ async interrupt(persistRemoval) {
15312
15377
  if (this.currentGoal?.status === "active") {
15313
15378
  await this.updateGoal({ status: "paused" });
15314
15379
  }
15380
+ return super.interrupt(persistRemoval);
15381
+ }
15382
+ async interruptActiveTurn() {
15315
15383
  if (!this.currentThreadId || !this.activeTurnId) {
15316
15384
  return;
15317
15385
  }
@@ -15370,7 +15438,7 @@ var CodexAspManager = class extends CodingAgentManager {
15370
15438
  expectedTurnId,
15371
15439
  (host, turnId) => host.client.request(
15372
15440
  TURN_STEER_METHOD,
15373
- { threadId, expectedTurnId: turnId, input }
15441
+ { threadId, expectedTurnId: turnId, input, clientUserMessageId: request.id }
15374
15442
  )
15375
15443
  );
15376
15444
  if (!result.ok) {
@@ -15386,7 +15454,6 @@ var CodexAspManager = class extends CodingAgentManager {
15386
15454
  } else {
15387
15455
  await removeTempImageFiles(tempImagePaths);
15388
15456
  }
15389
- this.recordUserMessageEvent(request);
15390
15457
  return true;
15391
15458
  }
15392
15459
  async getHistory(page2 = {}) {
@@ -15477,7 +15544,7 @@ var CodexAspManager = class extends CodingAgentManager {
15477
15544
  ...extraPayload
15478
15545
  }));
15479
15546
  }
15480
- async processMessageInternal(request) {
15547
+ async processMessageInternal(request, signal) {
15481
15548
  let userMessageRecorded = false;
15482
15549
  let quotaFallbackAttempted = false;
15483
15550
  const recordUserMessage = (extraPayload = {}) => {
@@ -15488,8 +15555,8 @@ var CodexAspManager = class extends CodingAgentManager {
15488
15555
  const goalCommand = getGoalCommand(request.message, request.goalMode);
15489
15556
  const dispatch = async () => {
15490
15557
  if (isCompactCommand(request.message)) {
15491
- await this.executeAspTurn(request, recordUserMessage, {
15492
- runTurn: (host, threadId) => this.observeTurn(host, threadId, request, async () => {
15558
+ await this.executeAspTurn(request, recordUserMessage, signal, {
15559
+ runTurn: (host, threadId) => this.observeTurn(host, threadId, request, signal, async () => {
15493
15560
  await host.client.request(
15494
15561
  THREAD_COMPACT_START_METHOD,
15495
15562
  { threadId }
@@ -15513,13 +15580,13 @@ var CodexAspManager = class extends CodingAgentManager {
15513
15580
  return;
15514
15581
  }
15515
15582
  if (goalCommand?.type === "set") {
15516
- await this.executeAspTurn(request, recordUserMessage, {
15517
- runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective),
15583
+ await this.executeAspTurn(request, recordUserMessage, signal, {
15584
+ runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective, signal),
15518
15585
  userMessagePayload: { command: "goal" }
15519
15586
  });
15520
15587
  return;
15521
15588
  }
15522
- await this.executeAspTurn(request, recordUserMessage);
15589
+ await this.executeAspTurn(request, recordUserMessage, signal);
15523
15590
  };
15524
15591
  let attemptedCredential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.codex;
15525
15592
  try {
@@ -15611,14 +15678,15 @@ var CodexAspManager = class extends CodingAgentManager {
15611
15678
  const threadId = await this.ensureThread(host, request, developerInstructions);
15612
15679
  return { host, threadId };
15613
15680
  }
15614
- async executeAspTurn(request, recordUserMessage, options = {}) {
15681
+ async executeAspTurn(request, recordUserMessage, signal, options = {}) {
15615
15682
  const host = await getCodexAspHost();
15616
15683
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions, true);
15617
15684
  recordUserMessage(options.userMessagePayload);
15618
15685
  const threadId = await this.ensureThread(host, request, developerInstructions);
15619
15686
  const serviceTier = await this.resolveRequestedServiceTier(host, request);
15620
15687
  await this.applyThreadServiceTier(host, threadId, serviceTier);
15621
- const runTurn = options.runTurn ?? ((aspHost, aspThreadId) => this.runTurn(aspHost, aspThreadId, request, serviceTier));
15688
+ if (signal.aborted) return;
15689
+ const runTurn = options.runTurn ?? ((aspHost, aspThreadId) => this.runTurn(aspHost, aspThreadId, request, serviceTier, signal));
15622
15690
  let completedTurn;
15623
15691
  try {
15624
15692
  completedTurn = await runTurn(host, threadId);
@@ -15758,9 +15826,9 @@ ${text.slice(-24e3)}`
15758
15826
  console.warn("[CodexAspManager] Failed to apply skill registries:", error);
15759
15827
  }
15760
15828
  }
15761
- async runTurn(host, threadId, request, serviceTier) {
15829
+ async runTurn(host, threadId, request, serviceTier, signal) {
15762
15830
  const { params, tempImagePaths } = await buildTurnStartParams(threadId, request, serviceTier);
15763
- return this.observeTurn(host, threadId, request, async () => {
15831
+ return this.observeTurn(host, threadId, request, signal, async () => {
15764
15832
  const turnStartResponse = await host.client.request(
15765
15833
  TURN_START_METHOD,
15766
15834
  params
@@ -15768,8 +15836,8 @@ ${text.slice(-24e3)}`
15768
15836
  return { turn: turnStartResponse.turn, tempImagePaths };
15769
15837
  });
15770
15838
  }
15771
- async runGoalTurn(host, threadId, request, objective) {
15772
- return this.observeTurn(host, threadId, request, async () => {
15839
+ async runGoalTurn(host, threadId, request, objective, signal) {
15840
+ return this.observeTurn(host, threadId, request, signal, async () => {
15773
15841
  const response = await host.client.request(
15774
15842
  THREAD_GOAL_SET_METHOD,
15775
15843
  { threadId, objective, status: "active" }
@@ -15778,7 +15846,15 @@ ${text.slice(-24e3)}`
15778
15846
  return { turn: null, tempImagePaths: [] };
15779
15847
  });
15780
15848
  }
15781
- async observeTurn(host, threadId, request, startTurn) {
15849
+ async observeTurn(host, threadId, request, signal, startTurn) {
15850
+ let stopSent = false;
15851
+ const interruptIfStopped = () => {
15852
+ if (!signal.aborted || stopSent) return;
15853
+ stopSent = true;
15854
+ void this.interruptActiveTurn().catch((error) => {
15855
+ console.error("[CodexAspManager] Failed to interrupt a turn stopped before it started:", error);
15856
+ });
15857
+ };
15782
15858
  let resolveCompleted;
15783
15859
  const completed = new Promise((resolve7) => {
15784
15860
  resolveCompleted = resolve7;
@@ -15844,6 +15920,7 @@ ${text.slice(-24e3)}`
15844
15920
  completedItems.length = 0;
15845
15921
  agentMessageDeltas.clear();
15846
15922
  this.startTurn(notification.params.turn);
15923
+ interruptIfStopped();
15847
15924
  linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
15848
15925
  },
15849
15926
  [TURN_PLAN_UPDATED_METHOD]: (notification) => {
@@ -15853,6 +15930,10 @@ ${text.slice(-24e3)}`
15853
15930
  [ITEM_STARTED_METHOD]: (notification) => {
15854
15931
  if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
15855
15932
  if (notification.params.item.type === "contextCompaction") this.compacting = true;
15933
+ if (notification.params.item.type === "userMessage" && notification.params.item.clientId) {
15934
+ const steer = this.markSteerRead(notification.params.item.clientId);
15935
+ if (steer) this.recordUserMessageEvent(steer);
15936
+ }
15856
15937
  this.startItem(
15857
15938
  notification.params.turnId,
15858
15939
  itemToTranscriptItem(notification.params.item, timestampFromMilliseconds(notification.params.startedAtMs), "in_progress")
@@ -15956,6 +16037,7 @@ ${text.slice(-24e3)}`
15956
16037
  this.activeTurnId = started.turn.id;
15957
16038
  this.onCodexTurnStarted?.(threadId, started.turn.id);
15958
16039
  this.startTurn(started.turn);
16040
+ interruptIfStopped();
15959
16041
  }
15960
16042
  const turn = await Promise.race([completed, disposed]);
15961
16043
  linearForwarder.flushThoughtAsResponse();
@@ -17130,7 +17212,7 @@ ${typeof pending.subject.command === "string" ? pending.subject.command : JSON.s
17130
17212
  }, this.historyFile);
17131
17213
  }
17132
17214
  }
17133
- async processMessageInternal(request) {
17215
+ async processMessageInternal(request, signal) {
17134
17216
  let userMessageRecorded = false;
17135
17217
  const dispatch = async () => {
17136
17218
  const model = request.model ?? DEFAULT_MUSE_MODEL;
@@ -17176,6 +17258,7 @@ ${pending}
17176
17258
  </workspace-instructions>
17177
17259
 
17178
17260
  ` : ""}${request.planMode ? "Plan the requested work without modifying files or running mutating commands.\n\n" : ""}${request.message}`;
17261
+ if (signal.aborted) return;
17179
17262
  this.turnError = null;
17180
17263
  const completed = new Promise((resolve7) => {
17181
17264
  this.turnComplete = { resolve: resolve7 };
@@ -17241,17 +17324,6 @@ ${pending}
17241
17324
  turnId: this.activeTurnId
17242
17325
  });
17243
17326
  }
17244
- async steerRequest(request) {
17245
- if (!this.sessionId || !this.activeTurnId) return false;
17246
- await this.request("turn/steer", {
17247
- commandId: uuidV7(),
17248
- sessionId: this.sessionId,
17249
- expectedTurnId: this.activeTurnId,
17250
- input: this.createTurnInput(request.message, request.images),
17251
- reasoningEffort: codexReasoningEffortForThinkingLevel(request.thinkingLevel)
17252
- });
17253
- return true;
17254
- }
17255
17327
  async respondToToolInput(requestId, selectionId) {
17256
17328
  const approval = [...this.pendingApprovals.values()].find((pending2) => pending2.requestId === requestId);
17257
17329
  if (approval) {
@@ -58678,13 +58750,6 @@ var PiManager = class extends CodingAgentManager {
58678
58750
  async interruptActiveTurn() {
58679
58751
  await this.session?.abort();
58680
58752
  }
58681
- async steerRequest(request) {
58682
- const session = this.session;
58683
- if (!session?.isStreaming) return false;
58684
- await session.steer(request.message);
58685
- this.recordUserMessage(session, request);
58686
- return true;
58687
- }
58688
58753
  async getHistory(page2 = {}) {
58689
58754
  await this.historyFile.flush();
58690
58755
  const history = await this.historyFile.loadEventsPage(page2);
@@ -58869,7 +58934,7 @@ var PiManager = class extends CodingAgentManager {
58869
58934
  skillName ? { skillName } : {}
58870
58935
  );
58871
58936
  }
58872
- async processMessageInternal(request) {
58937
+ async processMessageInternal(request, signal) {
58873
58938
  try {
58874
58939
  const session = await this.ensureSession(request);
58875
58940
  this.recordUserMessage(session, request);
@@ -58878,6 +58943,7 @@ var PiManager = class extends CodingAgentManager {
58878
58943
  data: image.source.data,
58879
58944
  mimeType: image.source.media_type
58880
58945
  })) : void 0;
58946
+ if (signal.aborted) return;
58881
58947
  await session.prompt(request.message, images ? { images } : void 0);
58882
58948
  } catch (error) {
58883
58949
  this.recordHistoryEvent("pi-error", { message: error instanceof Error ? error.message : String(error) }, this.historyFile);
@@ -60838,6 +60904,9 @@ var ChatService = class {
60838
60904
  if (signal?.aborted) throw new Error("Subagent wait was cancelled");
60839
60905
  const result = await this.getSubagent(parentChatId, chatId);
60840
60906
  if (result.status === "completed") return result;
60907
+ if (this.requireChat(parentChatId).provider.getQueue().some(({ steeredAt }) => steeredAt && Date.parse(steeredAt) >= startedAt)) {
60908
+ return { ...result, interruptedByUserMessage: true };
60909
+ }
60841
60910
  const remainingMs = timeoutMs === null ? null : timeoutMs - (Date.now() - startedAt);
60842
60911
  if (remainingMs !== null && remainingMs <= 0) return { ...result, timedOut: true };
60843
60912
  await new Promise((resolve7) => setTimeout(
@@ -61020,7 +61089,7 @@ var ChatService = class {
61020
61089
  }
61021
61090
  async interrupt(chatId) {
61022
61091
  const chat = this.requireChat(chatId);
61023
- const result = await chat.provider.interrupt();
61092
+ const result = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
61024
61093
  this.endTrackedTurn(chatId);
61025
61094
  chat.hasActiveTurn = false;
61026
61095
  chat.activeMessageId = null;
@@ -61042,7 +61111,7 @@ var ChatService = class {
61042
61111
  if (!chat.provider.clearGoal) {
61043
61112
  return { interrupted: false, queue: [], goal: null };
61044
61113
  }
61045
- const interruptResult = await chat.provider.interrupt();
61114
+ const interruptResult = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
61046
61115
  this.endTrackedTurn(chatId);
61047
61116
  chat.hasActiveTurn = false;
61048
61117
  chat.activeMessageId = null;
@@ -61190,6 +61259,14 @@ var ChatService = class {
61190
61259
  }
61191
61260
  return result;
61192
61261
  }
61262
+ async sendNow(chatId, messageId) {
61263
+ const chat = this.requireChat(chatId);
61264
+ const result = await chat.provider.sendNow(messageId);
61265
+ if (result.success && !result.queue.some(({ id, steeredAt }) => id === messageId && !steeredAt)) {
61266
+ chat.pendingMessageIds = chat.pendingMessageIds.filter((pendingMessageId) => pendingMessageId !== messageId);
61267
+ }
61268
+ return result;
61269
+ }
61193
61270
  async respondToToolInput(chatId, requestId, selectionId) {
61194
61271
  const chat = this.requireChat(chatId);
61195
61272
  if (!chat.provider.respondToToolInput) {
@@ -61205,7 +61282,7 @@ var ChatService = class {
61205
61282
  const target = this.chats.get(id);
61206
61283
  if (!target || target.persisted.deletedAt) continue;
61207
61284
  if (target.provider.isProcessing()) {
61208
- await target.provider.interrupt().catch(() => {
61285
+ await target.provider.interrupt((messageIds) => this.forgetPendingMessages(target, messageIds)).catch(() => {
61209
61286
  });
61210
61287
  target.hasActiveTurn = false;
61211
61288
  target.activeMessageId = null;
@@ -63490,17 +63567,21 @@ function createV1Routes(deps) {
63490
63567
  return c.json(jsonError("Failed to update queued message", error instanceof Error ? error.message : "Unknown error"), 500);
63491
63568
  }
63492
63569
  });
63493
- app2.post("/chats/:chatId/queue/:messageId/steer", async (c) => {
63494
- try {
63495
- const result = await deps.chatService.steerFromQueue(c.req.param("chatId"), c.req.param("messageId"));
63496
- return c.json(result);
63497
- } catch (error) {
63498
- if (error instanceof ChatNotFoundError) {
63499
- return c.json(jsonError("Chat not found", error.message), 404);
63570
+ for (const { action, failure, run } of [
63571
+ { action: "steer", failure: "Failed to steer queued message", run: (chatId, messageId) => deps.chatService.steerFromQueue(chatId, messageId) },
63572
+ { action: "send-now", failure: "Failed to send queued message now", run: (chatId, messageId) => deps.chatService.sendNow(chatId, messageId) }
63573
+ ]) {
63574
+ app2.post(`/chats/:chatId/queue/:messageId/${action}`, async (c) => {
63575
+ try {
63576
+ return c.json(await run(c.req.param("chatId"), c.req.param("messageId")));
63577
+ } catch (error) {
63578
+ if (error instanceof ChatNotFoundError) {
63579
+ return c.json(jsonError("Chat not found", error.message), 404);
63580
+ }
63581
+ return c.json(jsonError(failure, error instanceof Error ? error.message : "Unknown error"), 500);
63500
63582
  }
63501
- return c.json(jsonError("Failed to steer queued message", error instanceof Error ? error.message : "Unknown error"), 500);
63502
- }
63503
- });
63583
+ });
63584
+ }
63504
63585
  app2.post("/chats/:chatId/tool-input", async (c) => {
63505
63586
  try {
63506
63587
  const body = respondToolInputSchema.parse(await c.req.json());
@@ -3,13 +3,13 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  publishedPullRequestUrls
6
- } from "./chunk-YQE57AE6.js";
6
+ } from "./chunk-5EHMJ4OT.js";
7
7
  import {
8
8
  isRecord
9
9
  } from "./chunk-UZSNFLDQ.js";
10
10
  import {
11
11
  shouldRefreshPullRequests
12
- } from "./chunk-IIANP4GI.js";
12
+ } from "./chunk-QU4FXL4X.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/services/post-tool-pr-notifier.ts
@@ -9,7 +9,7 @@ import {
9
9
  spawnSubagentRequestSchema,
10
10
  subagentResponseSchema,
11
11
  waitSubagentRequestSchema
12
- } from "./chunk-IIANP4GI.js";
12
+ } from "./chunk-QU4FXL4X.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/subagent-mcp.ts
@@ -101,7 +101,7 @@ server.registerTool(SUBAGENT_MCP_TOOLS.getAgent, {
101
101
  content: [{ type: "text", text: await request(`/chats/${parentChatId}/subagents/${chat_id}`, "GET") }]
102
102
  }));
103
103
  server.registerTool(SUBAGENT_MCP_TOOLS.waitAgent, {
104
- description: "Wait for one of this chat\u2019s subagents to finish over a heartbeat stream. A timeout stops waiting without interrupting the child.",
104
+ description: "Wait for one of this chat\u2019s subagents to finish over a heartbeat stream. A timeout, or a new message from the user, stops waiting without interrupting the child.",
105
105
  inputSchema: {
106
106
  chat_id: z.string().uuid(),
107
107
  timeout_minutes: waitSubagentRequestSchema.shape.timeoutMinutes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.986",
3
+ "version": "0.1.987",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",