replicas-engine 0.1.986 → 0.1.988

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-D246GL25.js";
9
+ import "./chunk-W4UVXL3R.js";
10
+ import "./chunk-4V3WESQO.js";
11
+ import "./chunk-5MEISXIX.js";
12
12
  import "./chunk-UZSNFLDQ.js";
13
- import "./chunk-IIANP4GI.js";
13
+ import "./chunk-FWG6GISJ.js";
14
14
  import "./chunk-VEQXQN22.js";
15
15
  export {
16
16
  getCodexAspHost,
@@ -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-5MEISXIX.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-IIANP4GI.js";
9
+ } from "./chunk-FWG6GISJ.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.988";
244
244
  var INITIALIZE_METHOD = "initialize";
245
245
  var INITIALIZED_NOTIFICATION = "initialized";
246
246
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -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-FWG6GISJ.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-FWG6GISJ.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-W4UVXL3R.js";
9
9
  import {
10
10
  AppServerProcess,
11
11
  buildCodexAgentEnv
12
- } from "./chunk-SAJ6ERGR.js";
12
+ } from "./chunk-4V3WESQO.js";
13
13
  import {
14
14
  SUBPROCESS_MAX_BUFFER,
15
15
  execFileAsync
16
- } from "./chunk-4WBOMDAN.js";
16
+ } from "./chunk-5MEISXIX.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-FWG6GISJ.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-64RO3V36.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-64RO3V36.js");
284
284
  await restartCodexAspHostIfRunning2();
285
285
  }
286
286
  setAgentCredentialSnapshot("codex", data);
@@ -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({
@@ -7179,10 +7180,6 @@ function detectLanguageByPath(filePath) {
7179
7180
  }
7180
7181
 
7181
7182
  // ../shared/src/context-usage.ts
7182
- var CODEX_CATEGORY_COLORS = {
7183
- input: "#3eeba3",
7184
- output: "#56b6c2"
7185
- };
7186
7183
  function isRecord2(value) {
7187
7184
  return typeof value === "object" && value !== null;
7188
7185
  }
@@ -7213,9 +7210,7 @@ function normalizeAutoCompactThreshold(threshold, maxTokens) {
7213
7210
  return clampPercentage(value);
7214
7211
  }
7215
7212
  function clampTokensToWindow(rawTokens, maxTokens) {
7216
- if (!maxTokens || maxTokens <= 0) return { totalTokens: rawTokens };
7217
- if (rawTokens <= maxTokens) return { totalTokens: rawTokens };
7218
- return { totalTokens: maxTokens, totalProcessedTokens: rawTokens };
7213
+ return maxTokens && maxTokens > 0 ? Math.min(rawTokens, maxTokens) : rawTokens;
7219
7214
  }
7220
7215
  function compactCategories(categories) {
7221
7216
  return categories.filter((category) => Boolean(category && category.tokens > 0)).map((category) => ({
@@ -7227,16 +7222,12 @@ function buildCodexTokenUsageContextUsagePayload(usage) {
7227
7222
  const maxTokens = usage.modelContextWindow;
7228
7223
  const inputTokens = usage.last.inputTokens;
7229
7224
  const outputTokens = usage.last.outputTokens;
7230
- const lastTurnTokens = usage.last.totalTokens;
7231
- const { totalTokens } = clampTokensToWindow(lastTurnTokens, maxTokens);
7232
- const cumulativeTotalTokens = usage.total?.totalTokens ?? null;
7233
- const totalProcessedTokens = cumulativeTotalTokens !== null && cumulativeTotalTokens > totalTokens ? cumulativeTotalTokens : clampTokensToWindow(lastTurnTokens, maxTokens).totalProcessedTokens;
7225
+ const totalTokens = clampTokensToWindow(usage.last.totalTokens, maxTokens);
7234
7226
  return {
7235
7227
  provider: "codex",
7236
7228
  source: "codex_token_count",
7237
7229
  ...usage.model !== void 0 ? { model: usage.model } : {},
7238
7230
  totalTokens,
7239
- ...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
7240
7231
  maxTokens,
7241
7232
  rawMaxTokens: maxTokens,
7242
7233
  percentage: percentage(totalTokens, maxTokens),
@@ -7245,23 +7236,14 @@ function buildCodexTokenUsageContextUsagePayload(usage) {
7245
7236
  {
7246
7237
  name: "Input context",
7247
7238
  tokens: inputTokens,
7248
- percentage: percentage(inputTokens, maxTokens),
7249
- color: CODEX_CATEGORY_COLORS.input
7239
+ percentage: percentage(inputTokens, maxTokens)
7250
7240
  },
7251
7241
  {
7252
7242
  name: "Generated output",
7253
7243
  tokens: outputTokens,
7254
- percentage: percentage(outputTokens, maxTokens),
7255
- color: CODEX_CATEGORY_COLORS.output
7244
+ percentage: percentage(outputTokens, maxTokens)
7256
7245
  }
7257
7246
  ]),
7258
- apiUsage: {
7259
- inputTokens,
7260
- outputTokens,
7261
- ...usage.last.cachedInputTokens !== null && usage.last.cachedInputTokens !== void 0 ? { cachedInputTokens: usage.last.cachedInputTokens } : {},
7262
- ...usage.last.reasoningOutputTokens !== null && usage.last.reasoningOutputTokens !== void 0 ? { reasoningOutputTokens: usage.last.reasoningOutputTokens } : {},
7263
- ...cumulativeTotalTokens !== null ? { totalTokens: cumulativeTotalTokens } : {}
7264
- },
7265
7247
  updatedAt: usage.updatedAt
7266
7248
  };
7267
7249
  }
@@ -7287,10 +7269,7 @@ function coerceContextUsagePayload(payload, timestamp) {
7287
7269
  const reportedMaxTokens = numberOrNull(payload.maxTokens);
7288
7270
  const { maxTokens, usedKnownWindow } = resolveClaudeContextWindow(provider, model, reportedMaxTokens);
7289
7271
  const rawMaxTokens = usedKnownWindow ? maxTokens : numberOrNull(payload.rawMaxTokens);
7290
- const clamped = clampTokensToWindow(rawTotalTokens, maxTokens);
7291
- const totalTokens = clamped.totalTokens;
7292
- const payloadTotalProcessedTokens = numberOrNull(payload.totalProcessedTokens);
7293
- const totalProcessedTokens = payloadTotalProcessedTokens !== null ? payloadTotalProcessedTokens : clamped.totalProcessedTokens ?? null;
7272
+ const totalTokens = clampTokensToWindow(rawTotalTokens, maxTokens);
7294
7273
  const source = payload.source === "codex_token_count" || payload.source === "claude_usage" || payload.source === "provider_usage" ? payload.source : "claude_context";
7295
7274
  const categories = Array.isArray(payload.categories) ? compactCategories(payload.categories.map((item) => {
7296
7275
  if (!isRecord2(item)) return null;
@@ -7298,23 +7277,13 @@ function coerceContextUsagePayload(payload, timestamp) {
7298
7277
  const tokens = numberOrNull(item.tokens);
7299
7278
  const categoryPercentage = numberOrNull(item.percentage);
7300
7279
  if (!name || tokens === null || categoryPercentage === null) return null;
7280
+ if (item.isDeferred === true || name === "Free space" || name === "Autocompact buffer") return null;
7301
7281
  return {
7302
7282
  name,
7303
7283
  tokens,
7304
- percentage: usedKnownWindow ? percentage(tokens, maxTokens) : categoryPercentage,
7305
- ...typeof item.color === "string" ? { color: item.color } : {},
7306
- ...typeof item.isDeferred === "boolean" ? { isDeferred: item.isDeferred } : {}
7284
+ percentage: usedKnownWindow ? percentage(tokens, maxTokens) : categoryPercentage
7307
7285
  };
7308
7286
  })) : [];
7309
- const apiUsage = isRecord2(payload.apiUsage) ? {
7310
- ...numberOrNull(payload.apiUsage.inputTokens) !== null ? { inputTokens: numberOrNull(payload.apiUsage.inputTokens) } : {},
7311
- ...numberOrNull(payload.apiUsage.outputTokens) !== null ? { outputTokens: numberOrNull(payload.apiUsage.outputTokens) } : {},
7312
- ...numberOrNull(payload.apiUsage.cacheCreationInputTokens) !== null ? { cacheCreationInputTokens: numberOrNull(payload.apiUsage.cacheCreationInputTokens) } : {},
7313
- ...numberOrNull(payload.apiUsage.cacheReadInputTokens) !== null ? { cacheReadInputTokens: numberOrNull(payload.apiUsage.cacheReadInputTokens) } : {},
7314
- ...numberOrNull(payload.apiUsage.cachedInputTokens) !== null ? { cachedInputTokens: numberOrNull(payload.apiUsage.cachedInputTokens) } : {},
7315
- ...numberOrNull(payload.apiUsage.reasoningOutputTokens) !== null ? { reasoningOutputTokens: numberOrNull(payload.apiUsage.reasoningOutputTokens) } : {},
7316
- ...numberOrNull(payload.apiUsage.totalTokens) !== null ? { totalTokens: numberOrNull(payload.apiUsage.totalTokens) } : {}
7317
- } : null;
7318
7287
  const compactsAutomatically = typeof payload.compactsAutomatically === "boolean" ? payload.compactsAutomatically : void 0;
7319
7288
  const autoCompactThreshold = normalizeAutoCompactThreshold(payload.autoCompactThreshold, maxTokens);
7320
7289
  return {
@@ -7322,14 +7291,12 @@ function coerceContextUsagePayload(payload, timestamp) {
7322
7291
  source,
7323
7292
  ...model ? { model } : {},
7324
7293
  totalTokens,
7325
- ...totalProcessedTokens !== null ? { totalProcessedTokens } : {},
7326
7294
  maxTokens,
7327
7295
  rawMaxTokens,
7328
7296
  percentage: usedKnownWindow ? percentage(totalTokens, maxTokens) : clampPercentage(percentageValue),
7329
7297
  ...compactsAutomatically !== void 0 ? { compactsAutomatically } : {},
7330
7298
  ...autoCompactThreshold !== null ? { autoCompactThreshold } : {},
7331
7299
  categories,
7332
- apiUsage,
7333
7300
  updatedAt: typeof payload.updatedAt === "string" ? payload.updatedAt : timestamp
7334
7301
  };
7335
7302
  }
@@ -7340,20 +7307,15 @@ function codexTokenCountToContextUsage(event) {
7340
7307
  if (!info) return null;
7341
7308
  const lastUsage = isRecord2(info.last_token_usage) ? info.last_token_usage : null;
7342
7309
  if (!lastUsage) return null;
7343
- const totalUsage = isRecord2(info.total_token_usage) ? info.total_token_usage : null;
7344
7310
  const maxTokens = numberOrNull(info.model_context_window);
7345
7311
  const inputTokens = numberOrNull(lastUsage.input_tokens) ?? 0;
7346
7312
  const outputTokens = numberOrNull(lastUsage.output_tokens) ?? 0;
7347
- const cumulativeTotalTokens = numberOrNull(totalUsage?.total_tokens);
7348
7313
  return buildCodexTokenUsageContextUsagePayload({
7349
7314
  last: {
7350
7315
  inputTokens,
7351
7316
  outputTokens,
7352
- totalTokens: numberOrNull(lastUsage.total_tokens) ?? inputTokens + outputTokens,
7353
- cachedInputTokens: numberOrNull(lastUsage.cached_input_tokens),
7354
- reasoningOutputTokens: numberOrNull(lastUsage.reasoning_output_tokens)
7317
+ totalTokens: numberOrNull(lastUsage.total_tokens) ?? inputTokens + outputTokens
7355
7318
  },
7356
- total: cumulativeTotalTokens !== null ? { totalTokens: cumulativeTotalTokens } : null,
7357
7319
  modelContextWindow: maxTokens,
7358
7320
  updatedAt: event.timestamp
7359
7321
  });
@@ -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-W4UVXL3R.js";
7
7
  import {
8
8
  SUBPROCESS_MAX_BUFFER,
9
9
  execFileAsync
10
- } from "./chunk-4WBOMDAN.js";
10
+ } from "./chunk-5MEISXIX.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-FWG6GISJ.js";
18
18
 
19
19
  // src/services/command-protection-service.ts
20
20
  import { readFile, stat } from "fs/promises";
@@ -14,7 +14,7 @@ import {
14
14
  isValidAgentProvider,
15
15
  parsePosixEnvFile,
16
16
  readReplicasRuntimeEnv
17
- } from "./chunk-IIANP4GI.js";
17
+ } from "./chunk-FWG6GISJ.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-TZHZU4JB.js";
7
+ import "./chunk-W4UVXL3R.js";
8
+ import "./chunk-5MEISXIX.js";
9
9
  import {
10
10
  isRecord
11
11
  } from "./chunk-UZSNFLDQ.js";
12
- import "./chunk-IIANP4GI.js";
12
+ import "./chunk-FWG6GISJ.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-4V3WESQO.js";
11
+ import "./chunk-5MEISXIX.js";
12
12
  import {
13
13
  AGENT,
14
14
  headlessAgentRequestSchema
15
- } from "./chunk-IIANP4GI.js";
15
+ } from "./chunk-FWG6GISJ.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-TZHZU4JB.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-6LIDXAYC.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-D246GL25.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-W4UVXL3R.js";
84
84
  import {
85
85
  AspClient,
86
86
  getCodexModelProvider
87
- } from "./chunk-SAJ6ERGR.js";
87
+ } from "./chunk-4V3WESQO.js";
88
88
  import {
89
89
  SUBPROCESS_MAX_BUFFER,
90
90
  execAsync,
91
91
  execFileAsync
92
- } from "./chunk-4WBOMDAN.js";
92
+ } from "./chunk-5MEISXIX.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-FWG6GISJ.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() };
12202
12235
  }
12203
- keepSteeredMessageQueued() {
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;
12245
+ }
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();
@@ -14327,32 +14392,23 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
14327
14392
  const reportedMaxTokens = Number.isFinite(usage.maxTokens) ? usage.maxTokens : null;
14328
14393
  const knownMaxTokens = getClaudeModelContextWindow(usage.model);
14329
14394
  const maxTokens = knownMaxTokens ?? reportedMaxTokens;
14330
- const clamped = clampTokensToWindow(usage.totalTokens, maxTokens);
14395
+ const totalTokens = clampTokensToWindow(usage.totalTokens, maxTokens);
14331
14396
  const autoCompactThreshold = normalizeAutoCompactThreshold(usage.autoCompactThreshold, maxTokens);
14332
14397
  return {
14333
14398
  provider: "claude",
14334
14399
  source: "claude_context",
14335
14400
  model: usage.model,
14336
- totalTokens: clamped.totalTokens,
14337
- ...clamped.totalProcessedTokens !== void 0 ? { totalProcessedTokens: clamped.totalProcessedTokens } : {},
14401
+ totalTokens,
14338
14402
  maxTokens,
14339
14403
  rawMaxTokens: knownMaxTokens ?? (Number.isFinite(usage.rawMaxTokens) ? usage.rawMaxTokens : maxTokens),
14340
- percentage: percentage(clamped.totalTokens, maxTokens),
14404
+ percentage: percentage(totalTokens, maxTokens),
14341
14405
  compactsAutomatically: usage.isAutoCompactEnabled === true,
14342
14406
  ...autoCompactThresholdField(autoCompactThreshold),
14343
- categories: usage.categories.map((category) => ({
14407
+ categories: usage.categories.filter((category) => category.kind === "used").map((category) => ({
14344
14408
  name: category.name,
14345
14409
  tokens: category.tokens,
14346
- percentage: percentage(category.tokens, maxTokens),
14347
- color: category.color,
14348
- ...category.isDeferred !== void 0 ? { isDeferred: category.isDeferred } : {}
14410
+ percentage: percentage(category.tokens, maxTokens)
14349
14411
  })),
14350
- apiUsage: usage.apiUsage ? {
14351
- inputTokens: usage.apiUsage.input_tokens,
14352
- outputTokens: usage.apiUsage.output_tokens,
14353
- cacheCreationInputTokens: usage.apiUsage.cache_creation_input_tokens,
14354
- cacheReadInputTokens: usage.apiUsage.cache_read_input_tokens
14355
- } : null,
14356
14412
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
14357
14413
  };
14358
14414
  }
@@ -15308,10 +15364,13 @@ var CodexAspManager = class extends CodingAgentManager {
15308
15364
  async subagentMcpForTurn(enabled) {
15309
15365
  return this.currentThreadId ? existsSync9(join17(SUBAGENT_MCP_THREADS_DIR, this.currentThreadId)) : enabled;
15310
15366
  }
15311
- async interruptActiveTurn() {
15367
+ async interrupt(persistRemoval) {
15312
15368
  if (this.currentGoal?.status === "active") {
15313
15369
  await this.updateGoal({ status: "paused" });
15314
15370
  }
15371
+ return super.interrupt(persistRemoval);
15372
+ }
15373
+ async interruptActiveTurn() {
15315
15374
  if (!this.currentThreadId || !this.activeTurnId) {
15316
15375
  return;
15317
15376
  }
@@ -15370,7 +15429,7 @@ var CodexAspManager = class extends CodingAgentManager {
15370
15429
  expectedTurnId,
15371
15430
  (host, turnId) => host.client.request(
15372
15431
  TURN_STEER_METHOD,
15373
- { threadId, expectedTurnId: turnId, input }
15432
+ { threadId, expectedTurnId: turnId, input, clientUserMessageId: request.id }
15374
15433
  )
15375
15434
  );
15376
15435
  if (!result.ok) {
@@ -15386,7 +15445,6 @@ var CodexAspManager = class extends CodingAgentManager {
15386
15445
  } else {
15387
15446
  await removeTempImageFiles(tempImagePaths);
15388
15447
  }
15389
- this.recordUserMessageEvent(request);
15390
15448
  return true;
15391
15449
  }
15392
15450
  async getHistory(page2 = {}) {
@@ -15477,7 +15535,7 @@ var CodexAspManager = class extends CodingAgentManager {
15477
15535
  ...extraPayload
15478
15536
  }));
15479
15537
  }
15480
- async processMessageInternal(request) {
15538
+ async processMessageInternal(request, signal) {
15481
15539
  let userMessageRecorded = false;
15482
15540
  let quotaFallbackAttempted = false;
15483
15541
  const recordUserMessage = (extraPayload = {}) => {
@@ -15488,8 +15546,8 @@ var CodexAspManager = class extends CodingAgentManager {
15488
15546
  const goalCommand = getGoalCommand(request.message, request.goalMode);
15489
15547
  const dispatch = async () => {
15490
15548
  if (isCompactCommand(request.message)) {
15491
- await this.executeAspTurn(request, recordUserMessage, {
15492
- runTurn: (host, threadId) => this.observeTurn(host, threadId, request, async () => {
15549
+ await this.executeAspTurn(request, recordUserMessage, signal, {
15550
+ runTurn: (host, threadId) => this.observeTurn(host, threadId, request, signal, async () => {
15493
15551
  await host.client.request(
15494
15552
  THREAD_COMPACT_START_METHOD,
15495
15553
  { threadId }
@@ -15513,13 +15571,13 @@ var CodexAspManager = class extends CodingAgentManager {
15513
15571
  return;
15514
15572
  }
15515
15573
  if (goalCommand?.type === "set") {
15516
- await this.executeAspTurn(request, recordUserMessage, {
15517
- runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective),
15574
+ await this.executeAspTurn(request, recordUserMessage, signal, {
15575
+ runTurn: (host, threadId) => this.runGoalTurn(host, threadId, request, goalCommand.objective, signal),
15518
15576
  userMessagePayload: { command: "goal" }
15519
15577
  });
15520
15578
  return;
15521
15579
  }
15522
- await this.executeAspTurn(request, recordUserMessage);
15580
+ await this.executeAspTurn(request, recordUserMessage, signal);
15523
15581
  };
15524
15582
  let attemptedCredential = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.codex;
15525
15583
  try {
@@ -15611,14 +15669,15 @@ var CodexAspManager = class extends CodingAgentManager {
15611
15669
  const threadId = await this.ensureThread(host, request, developerInstructions);
15612
15670
  return { host, threadId };
15613
15671
  }
15614
- async executeAspTurn(request, recordUserMessage, options = {}) {
15672
+ async executeAspTurn(request, recordUserMessage, signal, options = {}) {
15615
15673
  const host = await getCodexAspHost();
15616
15674
  const developerInstructions = this.buildCombinedInstructions(request.customInstructions, true);
15617
15675
  recordUserMessage(options.userMessagePayload);
15618
15676
  const threadId = await this.ensureThread(host, request, developerInstructions);
15619
15677
  const serviceTier = await this.resolveRequestedServiceTier(host, request);
15620
15678
  await this.applyThreadServiceTier(host, threadId, serviceTier);
15621
- const runTurn = options.runTurn ?? ((aspHost, aspThreadId) => this.runTurn(aspHost, aspThreadId, request, serviceTier));
15679
+ if (signal.aborted) return;
15680
+ const runTurn = options.runTurn ?? ((aspHost, aspThreadId) => this.runTurn(aspHost, aspThreadId, request, serviceTier, signal));
15622
15681
  let completedTurn;
15623
15682
  try {
15624
15683
  completedTurn = await runTurn(host, threadId);
@@ -15758,9 +15817,9 @@ ${text.slice(-24e3)}`
15758
15817
  console.warn("[CodexAspManager] Failed to apply skill registries:", error);
15759
15818
  }
15760
15819
  }
15761
- async runTurn(host, threadId, request, serviceTier) {
15820
+ async runTurn(host, threadId, request, serviceTier, signal) {
15762
15821
  const { params, tempImagePaths } = await buildTurnStartParams(threadId, request, serviceTier);
15763
- return this.observeTurn(host, threadId, request, async () => {
15822
+ return this.observeTurn(host, threadId, request, signal, async () => {
15764
15823
  const turnStartResponse = await host.client.request(
15765
15824
  TURN_START_METHOD,
15766
15825
  params
@@ -15768,8 +15827,8 @@ ${text.slice(-24e3)}`
15768
15827
  return { turn: turnStartResponse.turn, tempImagePaths };
15769
15828
  });
15770
15829
  }
15771
- async runGoalTurn(host, threadId, request, objective) {
15772
- return this.observeTurn(host, threadId, request, async () => {
15830
+ async runGoalTurn(host, threadId, request, objective, signal) {
15831
+ return this.observeTurn(host, threadId, request, signal, async () => {
15773
15832
  const response = await host.client.request(
15774
15833
  THREAD_GOAL_SET_METHOD,
15775
15834
  { threadId, objective, status: "active" }
@@ -15778,7 +15837,15 @@ ${text.slice(-24e3)}`
15778
15837
  return { turn: null, tempImagePaths: [] };
15779
15838
  });
15780
15839
  }
15781
- async observeTurn(host, threadId, request, startTurn) {
15840
+ async observeTurn(host, threadId, request, signal, startTurn) {
15841
+ let stopSent = false;
15842
+ const interruptIfStopped = () => {
15843
+ if (!signal.aborted || stopSent) return;
15844
+ stopSent = true;
15845
+ void this.interruptActiveTurn().catch((error) => {
15846
+ console.error("[CodexAspManager] Failed to interrupt a turn stopped before it started:", error);
15847
+ });
15848
+ };
15782
15849
  let resolveCompleted;
15783
15850
  const completed = new Promise((resolve7) => {
15784
15851
  resolveCompleted = resolve7;
@@ -15844,6 +15911,7 @@ ${text.slice(-24e3)}`
15844
15911
  completedItems.length = 0;
15845
15912
  agentMessageDeltas.clear();
15846
15913
  this.startTurn(notification.params.turn);
15914
+ interruptIfStopped();
15847
15915
  linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
15848
15916
  },
15849
15917
  [TURN_PLAN_UPDATED_METHOD]: (notification) => {
@@ -15853,6 +15921,10 @@ ${text.slice(-24e3)}`
15853
15921
  [ITEM_STARTED_METHOD]: (notification) => {
15854
15922
  if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
15855
15923
  if (notification.params.item.type === "contextCompaction") this.compacting = true;
15924
+ if (notification.params.item.type === "userMessage" && notification.params.item.clientId) {
15925
+ const steer = this.markSteerRead(notification.params.item.clientId);
15926
+ if (steer) this.recordUserMessageEvent(steer);
15927
+ }
15856
15928
  this.startItem(
15857
15929
  notification.params.turnId,
15858
15930
  itemToTranscriptItem(notification.params.item, timestampFromMilliseconds(notification.params.startedAtMs), "in_progress")
@@ -15956,6 +16028,7 @@ ${text.slice(-24e3)}`
15956
16028
  this.activeTurnId = started.turn.id;
15957
16029
  this.onCodexTurnStarted?.(threadId, started.turn.id);
15958
16030
  this.startTurn(started.turn);
16031
+ interruptIfStopped();
15959
16032
  }
15960
16033
  const turn = await Promise.race([completed, disposed]);
15961
16034
  linearForwarder.flushThoughtAsResponse();
@@ -16322,12 +16395,7 @@ ${text.slice(-24e3)}`
16322
16395
  last: {
16323
16396
  inputTokens: tokenUsage.last.inputTokens,
16324
16397
  outputTokens: tokenUsage.last.outputTokens,
16325
- totalTokens: tokenUsage.last.totalTokens,
16326
- cachedInputTokens: tokenUsage.last.cachedInputTokens,
16327
- reasoningOutputTokens: tokenUsage.last.reasoningOutputTokens
16328
- },
16329
- total: {
16330
- totalTokens: tokenUsage.total.totalTokens
16398
+ totalTokens: tokenUsage.last.totalTokens
16331
16399
  },
16332
16400
  updatedAt
16333
16401
  });
@@ -16432,12 +16500,6 @@ var CURSOR_CANCEL_TIMEOUT_MS = 1e4;
16432
16500
  var CURSOR_RECONNECT_NOTICE_DELAY_MS = 15e3;
16433
16501
  var TURN_ABORTED = /* @__PURE__ */ Symbol("cursor-turn-aborted");
16434
16502
  var CURSOR_COMPOSER_CONTEXT_WINDOW = 2e5;
16435
- var CURSOR_CATEGORY_COLORS = {
16436
- input: "#3eeba3",
16437
- cacheRead: "#61afef",
16438
- cacheWrite: "#e5c07b",
16439
- output: "#56b6c2"
16440
- };
16441
16503
  var CURSOR_TRANSPORT_ERROR_PATTERNS = [
16442
16504
  /connection failed/,
16443
16505
  /connection stalled/,
@@ -16797,31 +16859,27 @@ ${instructions}
16797
16859
  if (rawTotalTokens <= 0) return;
16798
16860
  this.onUsageUpdate(update, (/* @__PURE__ */ new Date()).toISOString());
16799
16861
  const maxTokens = CURSOR_COMPOSER_CONTEXT_WINDOW;
16800
- const { totalTokens, totalProcessedTokens } = clampTokensToWindow(rawTotalTokens, maxTokens);
16862
+ const totalTokens = clampTokensToWindow(rawTotalTokens, maxTokens);
16801
16863
  const categories = [
16802
16864
  {
16803
16865
  name: "Input",
16804
16866
  tokens: inputTokens,
16805
- percentage: percentage(inputTokens, maxTokens),
16806
- color: CURSOR_CATEGORY_COLORS.input
16867
+ percentage: percentage(inputTokens, maxTokens)
16807
16868
  },
16808
16869
  {
16809
16870
  name: "Cache read",
16810
16871
  tokens: cacheReadTokens,
16811
- percentage: percentage(cacheReadTokens, maxTokens),
16812
- color: CURSOR_CATEGORY_COLORS.cacheRead
16872
+ percentage: percentage(cacheReadTokens, maxTokens)
16813
16873
  },
16814
16874
  {
16815
16875
  name: "Cache write",
16816
16876
  tokens: cacheWriteTokens,
16817
- percentage: percentage(cacheWriteTokens, maxTokens),
16818
- color: CURSOR_CATEGORY_COLORS.cacheWrite
16877
+ percentage: percentage(cacheWriteTokens, maxTokens)
16819
16878
  },
16820
16879
  {
16821
16880
  name: "Output",
16822
16881
  tokens: outputTokens,
16823
- percentage: percentage(outputTokens, maxTokens),
16824
- color: CURSOR_CATEGORY_COLORS.output
16882
+ percentage: percentage(outputTokens, maxTokens)
16825
16883
  }
16826
16884
  ].filter((category) => category.tokens > 0);
16827
16885
  const payload = {
@@ -16829,18 +16887,11 @@ ${instructions}
16829
16887
  source: "provider_usage",
16830
16888
  model: this.activeModel,
16831
16889
  totalTokens,
16832
- ...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
16833
16890
  maxTokens,
16834
16891
  rawMaxTokens: maxTokens,
16835
16892
  percentage: percentage(totalTokens, maxTokens),
16836
16893
  compactsAutomatically: true,
16837
16894
  categories,
16838
- apiUsage: {
16839
- inputTokens,
16840
- outputTokens,
16841
- cacheReadInputTokens: cacheReadTokens,
16842
- cacheCreationInputTokens: cacheWriteTokens
16843
- },
16844
16895
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
16845
16896
  };
16846
16897
  this.historyFile.append(this.emitContextUsage(payload));
@@ -17130,7 +17181,7 @@ ${typeof pending.subject.command === "string" ? pending.subject.command : JSON.s
17130
17181
  }, this.historyFile);
17131
17182
  }
17132
17183
  }
17133
- async processMessageInternal(request) {
17184
+ async processMessageInternal(request, signal) {
17134
17185
  let userMessageRecorded = false;
17135
17186
  const dispatch = async () => {
17136
17187
  const model = request.model ?? DEFAULT_MUSE_MODEL;
@@ -17176,6 +17227,7 @@ ${pending}
17176
17227
  </workspace-instructions>
17177
17228
 
17178
17229
  ` : ""}${request.planMode ? "Plan the requested work without modifying files or running mutating commands.\n\n" : ""}${request.message}`;
17230
+ if (signal.aborted) return;
17179
17231
  this.turnError = null;
17180
17232
  const completed = new Promise((resolve7) => {
17181
17233
  this.turnComplete = { resolve: resolve7 };
@@ -17241,17 +17293,6 @@ ${pending}
17241
17293
  turnId: this.activeTurnId
17242
17294
  });
17243
17295
  }
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
17296
  async respondToToolInput(requestId, selectionId) {
17256
17297
  const approval = [...this.pendingApprovals.values()].find((pending2) => pending2.requestId === requestId);
17257
17298
  if (approval) {
@@ -58678,13 +58719,6 @@ var PiManager = class extends CodingAgentManager {
58678
58719
  async interruptActiveTurn() {
58679
58720
  await this.session?.abort();
58680
58721
  }
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
58722
  async getHistory(page2 = {}) {
58689
58723
  await this.historyFile.flush();
58690
58724
  const history = await this.historyFile.loadEventsPage(page2);
@@ -58869,7 +58903,7 @@ var PiManager = class extends CodingAgentManager {
58869
58903
  skillName ? { skillName } : {}
58870
58904
  );
58871
58905
  }
58872
- async processMessageInternal(request) {
58906
+ async processMessageInternal(request, signal) {
58873
58907
  try {
58874
58908
  const session = await this.ensureSession(request);
58875
58909
  this.recordUserMessage(session, request);
@@ -58878,6 +58912,7 @@ var PiManager = class extends CodingAgentManager {
58878
58912
  data: image.source.data,
58879
58913
  mimeType: image.source.media_type
58880
58914
  })) : void 0;
58915
+ if (signal.aborted) return;
58881
58916
  await session.prompt(request.message, images ? { images } : void 0);
58882
58917
  } catch (error) {
58883
58918
  this.recordHistoryEvent("pi-error", { message: error instanceof Error ? error.message : String(error) }, this.historyFile);
@@ -60838,6 +60873,9 @@ var ChatService = class {
60838
60873
  if (signal?.aborted) throw new Error("Subagent wait was cancelled");
60839
60874
  const result = await this.getSubagent(parentChatId, chatId);
60840
60875
  if (result.status === "completed") return result;
60876
+ if (this.requireChat(parentChatId).provider.getQueue().some(({ steeredAt }) => steeredAt && Date.parse(steeredAt) >= startedAt)) {
60877
+ return { ...result, interruptedByUserMessage: true };
60878
+ }
60841
60879
  const remainingMs = timeoutMs === null ? null : timeoutMs - (Date.now() - startedAt);
60842
60880
  if (remainingMs !== null && remainingMs <= 0) return { ...result, timedOut: true };
60843
60881
  await new Promise((resolve7) => setTimeout(
@@ -61020,7 +61058,7 @@ var ChatService = class {
61020
61058
  }
61021
61059
  async interrupt(chatId) {
61022
61060
  const chat = this.requireChat(chatId);
61023
- const result = await chat.provider.interrupt();
61061
+ const result = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
61024
61062
  this.endTrackedTurn(chatId);
61025
61063
  chat.hasActiveTurn = false;
61026
61064
  chat.activeMessageId = null;
@@ -61042,7 +61080,7 @@ var ChatService = class {
61042
61080
  if (!chat.provider.clearGoal) {
61043
61081
  return { interrupted: false, queue: [], goal: null };
61044
61082
  }
61045
- const interruptResult = await chat.provider.interrupt();
61083
+ const interruptResult = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
61046
61084
  this.endTrackedTurn(chatId);
61047
61085
  chat.hasActiveTurn = false;
61048
61086
  chat.activeMessageId = null;
@@ -61190,6 +61228,14 @@ var ChatService = class {
61190
61228
  }
61191
61229
  return result;
61192
61230
  }
61231
+ async sendNow(chatId, messageId) {
61232
+ const chat = this.requireChat(chatId);
61233
+ const result = await chat.provider.sendNow(messageId);
61234
+ if (result.success && !result.queue.some(({ id, steeredAt }) => id === messageId && !steeredAt)) {
61235
+ chat.pendingMessageIds = chat.pendingMessageIds.filter((pendingMessageId) => pendingMessageId !== messageId);
61236
+ }
61237
+ return result;
61238
+ }
61193
61239
  async respondToToolInput(chatId, requestId, selectionId) {
61194
61240
  const chat = this.requireChat(chatId);
61195
61241
  if (!chat.provider.respondToToolInput) {
@@ -61205,7 +61251,7 @@ var ChatService = class {
61205
61251
  const target = this.chats.get(id);
61206
61252
  if (!target || target.persisted.deletedAt) continue;
61207
61253
  if (target.provider.isProcessing()) {
61208
- await target.provider.interrupt().catch(() => {
61254
+ await target.provider.interrupt((messageIds) => this.forgetPendingMessages(target, messageIds)).catch(() => {
61209
61255
  });
61210
61256
  target.hasActiveTurn = false;
61211
61257
  target.activeMessageId = null;
@@ -63490,17 +63536,21 @@ function createV1Routes(deps) {
63490
63536
  return c.json(jsonError("Failed to update queued message", error instanceof Error ? error.message : "Unknown error"), 500);
63491
63537
  }
63492
63538
  });
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);
63539
+ for (const { action, failure, run } of [
63540
+ { action: "steer", failure: "Failed to steer queued message", run: (chatId, messageId) => deps.chatService.steerFromQueue(chatId, messageId) },
63541
+ { action: "send-now", failure: "Failed to send queued message now", run: (chatId, messageId) => deps.chatService.sendNow(chatId, messageId) }
63542
+ ]) {
63543
+ app2.post(`/chats/:chatId/queue/:messageId/${action}`, async (c) => {
63544
+ try {
63545
+ return c.json(await run(c.req.param("chatId"), c.req.param("messageId")));
63546
+ } catch (error) {
63547
+ if (error instanceof ChatNotFoundError) {
63548
+ return c.json(jsonError("Chat not found", error.message), 404);
63549
+ }
63550
+ return c.json(jsonError(failure, error instanceof Error ? error.message : "Unknown error"), 500);
63500
63551
  }
63501
- return c.json(jsonError("Failed to steer queued message", error instanceof Error ? error.message : "Unknown error"), 500);
63502
- }
63503
- });
63552
+ });
63553
+ }
63504
63554
  app2.post("/chats/:chatId/tool-input", async (c) => {
63505
63555
  try {
63506
63556
  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-6LIDXAYC.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-FWG6GISJ.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-FWG6GISJ.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.988",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",