replicas-engine 0.1.1067 → 0.1.1070

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.
@@ -8,12 +8,12 @@ import {
8
8
  listRunningCodexModels,
9
9
  restartCodexAspHost,
10
10
  restartCodexAspHostIfRunning
11
- } from "./chunk-CO7ITKY7.js";
12
- import "./chunk-W347RMUG.js";
13
- import "./chunk-OR4IW7GB.js";
14
- import "./chunk-DIKPGEYN.js";
11
+ } from "./chunk-KKAF3IGR.js";
12
+ import "./chunk-SNB36JQS.js";
13
+ import "./chunk-K72ZN4MS.js";
14
+ import "./chunk-3Q7NCMIF.js";
15
15
  import "./chunk-UZSNFLDQ.js";
16
- import "./chunk-PBDIRFDJ.js";
16
+ import "./chunk-T6JO4V4V.js";
17
17
  import "./chunk-VEQXQN22.js";
18
18
  export {
19
19
  CODEX_ULTRAFAST_SERVICE_TIER,
@@ -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-PBDIRFDJ.js";
6
+ } from "./chunk-T6JO4V4V.js";
7
7
 
8
8
  // src/utils/exec.ts
9
9
  import { exec, execFile } from "child_process";
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  AspClient
6
- } from "./chunk-OR4IW7GB.js";
6
+ } from "./chunk-K72ZN4MS.js";
7
7
  import {
8
8
  ASTER_BASE_URL,
9
9
  ASTER_PROVIDER,
@@ -11,7 +11,7 @@ import {
11
11
  codexReasoningEffortForThinkingLevel,
12
12
  isRecord,
13
13
  readOllamaAuthEnv
14
- } from "./chunk-PBDIRFDJ.js";
14
+ } from "./chunk-T6JO4V4V.js";
15
15
 
16
16
  // src/managers/codex-asp/notification-dispatch.ts
17
17
  var TURN_STARTED_METHOD = "turn/started";
@@ -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-DIKPGEYN.js";
6
+ } from "./chunk-3Q7NCMIF.js";
7
7
  import {
8
8
  isVersionBelow
9
- } from "./chunk-PBDIRFDJ.js";
9
+ } from "./chunk-T6JO4V4V.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.1067";
243
+ var ENGINE_PACKAGE_VERSION = "0.1.1070";
244
244
  var INITIALIZE_METHOD = "initialize";
245
245
  var INITIALIZED_NOTIFICATION = "initialized";
246
246
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -10,15 +10,15 @@ import {
10
10
  resolveDiffBase,
11
11
  runGitCommand,
12
12
  setAgentCredentialSnapshot
13
- } from "./chunk-W347RMUG.js";
13
+ } from "./chunk-SNB36JQS.js";
14
14
  import {
15
15
  AppServerProcess,
16
16
  buildCodexAgentEnv
17
- } from "./chunk-OR4IW7GB.js";
17
+ } from "./chunk-K72ZN4MS.js";
18
18
  import {
19
19
  SUBPROCESS_MAX_BUFFER,
20
20
  execFileAsync
21
- } from "./chunk-DIKPGEYN.js";
21
+ } from "./chunk-3Q7NCMIF.js";
22
22
  import {
23
23
  isRecord
24
24
  } from "./chunk-UZSNFLDQ.js";
@@ -55,7 +55,7 @@ import {
55
55
  resolveOpenAICompatibleModelsUrl,
56
56
  splitMultitaskInstructions,
57
57
  withTimeout
58
- } from "./chunk-PBDIRFDJ.js";
58
+ } from "./chunk-T6JO4V4V.js";
59
59
 
60
60
  // src/managers/codex-token-manager.ts
61
61
  import { promises as fs } from "fs";
@@ -291,7 +291,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
291
291
  await this.applyCredentialUpdate(++this.credentialGeneration, async () => {
292
292
  await this.applyCredentialsResponse(data);
293
293
  setAgentCredentialSnapshot("codex", data);
294
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-GK5THSUA.js");
294
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-QQ7NEGG4.js");
295
295
  await restartCodexAspHostIfRunning2();
296
296
  });
297
297
  await this.start(true);
@@ -312,7 +312,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
312
312
  const accountChanged = ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS.codex?.credentialId !== data.credentialId;
313
313
  await this.applyCredentialsResponse(data);
314
314
  if (restartOnChange && (accountChanged || CODEX_AUTH_ENV_KEYS.some((key, index) => process.env[key] !== previousEnv[index]))) {
315
- const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-GK5THSUA.js");
315
+ const { restartCodexAspHostIfRunning: restartCodexAspHostIfRunning2 } = await import("./asp-host-QQ7NEGG4.js");
316
316
  await restartCodexAspHostIfRunning2();
317
317
  }
318
318
  setAgentCredentialSnapshot("codex", data);
@@ -1394,6 +1394,7 @@ var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
1394
1394
  var THREAD_COMPACT_START_METHOD = "thread/compact/start";
1395
1395
  var THREAD_INJECT_ITEMS_METHOD = "thread/inject_items";
1396
1396
  var THREAD_SETTINGS_UPDATE_METHOD = "thread/settings/update";
1397
+ var THREAD_BACKGROUND_TERMINALS_CLEAN_METHOD = "thread/backgroundTerminals/clean";
1397
1398
  var TURN_START_METHOD = "turn/start";
1398
1399
  var TURN_INTERRUPT_METHOD = "turn/interrupt";
1399
1400
  var TURN_STEER_METHOD = "turn/steer";
@@ -2021,6 +2022,7 @@ export {
2021
2022
  THREAD_COMPACT_START_METHOD,
2022
2023
  THREAD_INJECT_ITEMS_METHOD,
2023
2024
  THREAD_SETTINGS_UPDATE_METHOD,
2025
+ THREAD_BACKGROUND_TERMINALS_CLEAN_METHOD,
2024
2026
  TURN_START_METHOD,
2025
2027
  TURN_INTERRUPT_METHOD,
2026
2028
  TURN_STEER_METHOD,
@@ -4,7 +4,7 @@ const require = __createRequire(import.meta.url);
4
4
  import {
5
5
  SUBPROCESS_MAX_BUFFER,
6
6
  execFileAsync
7
- } from "./chunk-DIKPGEYN.js";
7
+ } from "./chunk-3Q7NCMIF.js";
8
8
  import {
9
9
  isRecord as isRecord2
10
10
  } from "./chunk-UZSNFLDQ.js";
@@ -18,7 +18,7 @@ import {
18
18
  isValidAgentProvider,
19
19
  parsePosixEnvFile,
20
20
  readReplicasRuntimeEnv
21
- } from "./chunk-PBDIRFDJ.js";
21
+ } from "./chunk-T6JO4V4V.js";
22
22
 
23
23
  // src/runtime-env-loader.ts
24
24
  import { readFileSync } from "fs";
@@ -1174,6 +1174,7 @@ function getEventStreamId(event) {
1174
1174
  var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
1175
1175
  var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
1176
1176
  var CHAT_MESSAGE_REJECTED_EVENT_TYPE = "replicas-message-rejected";
1177
+ var CHAT_TURN_LOST_EVENT_TYPE = "replicas-turn-lost";
1177
1178
  var CHAT_GOAL_EVENT_TYPE = "chat-goal";
1178
1179
  var AUTH_RETRY_STATUS_EVENT_TYPE = "auth-retry-status";
1179
1180
  var CURSOR_CONNECTION_STATUS_EVENT_TYPE = "cursor-connection-status";
@@ -7550,6 +7551,15 @@ function applyEngineNotices(messages, events) {
7550
7551
  });
7551
7552
  return;
7552
7553
  }
7554
+ if (event.type === CHAT_TURN_LOST_EVENT_TYPE) {
7555
+ notices.push({
7556
+ id: `turn-lost-${event.timestamp}-${index}`,
7557
+ type: "error",
7558
+ message: "The agent stopped because the workspace restarted. Send a message to continue.",
7559
+ timestamp: event.timestamp
7560
+ });
7561
+ return;
7562
+ }
7553
7563
  if (event.type !== AUTH_FALLBACK_EVENT_TYPE) return;
7554
7564
  const payload = coerceAuthFallbackPayload(event.payload);
7555
7565
  if (!payload) return;
@@ -9897,6 +9907,7 @@ export {
9897
9907
  getEventStreamId,
9898
9908
  CODEX_QUOTA_STATUS_EVENT_TYPE,
9899
9909
  CHAT_INTERRUPTED_EVENT_TYPE,
9910
+ CHAT_TURN_LOST_EVENT_TYPE,
9900
9911
  CHAT_GOAL_EVENT_TYPE,
9901
9912
  AUTH_RETRY_STATUS_EVENT_TYPE,
9902
9913
  CURSOR_CONNECTION_STATUS_EVENT_TYPE,
@@ -5,7 +5,7 @@ import {
5
5
  extractCommandProtectionCommandText,
6
6
  isRecord,
7
7
  parseCodeHostPrUrl
8
- } from "./chunk-PBDIRFDJ.js";
8
+ } from "./chunk-T6JO4V4V.js";
9
9
 
10
10
  // src/services/published-pull-request-urls.ts
11
11
  function publishedPullRequestUrls(result) {
@@ -5,14 +5,14 @@ import {
5
5
  getOriginDefaultBranch,
6
6
  monolithRequest,
7
7
  runGitCommand
8
- } from "./chunk-W347RMUG.js";
8
+ } from "./chunk-SNB36JQS.js";
9
9
  import {
10
10
  extractCommandProtectionCommandText,
11
11
  findCommandProtectionGitLookups,
12
12
  findGitCommitSignals,
13
13
  findPrMergeSignals,
14
14
  parseCommandProtectionRequest
15
- } from "./chunk-PBDIRFDJ.js";
15
+ } from "./chunk-T6JO4V4V.js";
16
16
 
17
17
  // src/services/command-protection-service.ts
18
18
  import { readFile, stat } from "fs/promises";
@@ -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-NPPQ2SNN.js";
7
- import "./chunk-W347RMUG.js";
8
- import "./chunk-DIKPGEYN.js";
6
+ } from "./chunk-XV3XBNZR.js";
7
+ import "./chunk-SNB36JQS.js";
8
+ import "./chunk-3Q7NCMIF.js";
9
9
  import {
10
10
  isRecord
11
11
  } from "./chunk-UZSNFLDQ.js";
12
- import "./chunk-PBDIRFDJ.js";
12
+ import "./chunk-T6JO4V4V.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/command-protection-hook.ts
@@ -13,12 +13,12 @@ import {
13
13
  recoverCompletedTurn,
14
14
  spawnMuseMsp,
15
15
  startMuseSession
16
- } from "./chunk-YETX3YG2.js";
16
+ } from "./chunk-IZSM723M.js";
17
17
  import {
18
18
  AppServerProcess,
19
19
  buildCodexAgentEnv
20
- } from "./chunk-OR4IW7GB.js";
21
- import "./chunk-DIKPGEYN.js";
20
+ } from "./chunk-K72ZN4MS.js";
21
+ import "./chunk-3Q7NCMIF.js";
22
22
  import {
23
23
  AGENT,
24
24
  OPENCODE_GO_PROVIDER,
@@ -28,7 +28,7 @@ import {
28
28
  headlessAgentUsagePath,
29
29
  isRecord,
30
30
  withTimeout
31
- } from "./chunk-PBDIRFDJ.js";
31
+ } from "./chunk-T6JO4V4V.js";
32
32
  import "./chunk-VEQXQN22.js";
33
33
 
34
34
  // src/headless-agent.ts
package/dist/src/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  evaluateCommandProtection,
28
28
  extractToolCommand,
29
29
  reportCommandProtectionBlock
30
- } from "./chunk-NPPQ2SNN.js";
30
+ } from "./chunk-XV3XBNZR.js";
31
31
  import {
32
32
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
33
33
  AGENT_MESSAGE_DELTA_METHOD,
@@ -64,10 +64,10 @@ import {
64
64
  spawnMuseMsp,
65
65
  startMuseSession,
66
66
  uuidV7
67
- } from "./chunk-YETX3YG2.js";
67
+ } from "./chunk-IZSM723M.js";
68
68
  import {
69
69
  publishedPullRequestUrls
70
- } from "./chunk-G647LINI.js";
70
+ } from "./chunk-WJCC4O2M.js";
71
71
  import {
72
72
  ACCOUNT_RATE_LIMITS_READ_METHOD,
73
73
  AsyncLock,
@@ -78,6 +78,7 @@ import {
78
78
  DEFAULT_MODEL,
79
79
  SKILLS_LIST_METHOD,
80
80
  SUBAGENT_MCP_TOOL_TIMEOUT_MS,
81
+ THREAD_BACKGROUND_TERMINALS_CLEAN_METHOD,
81
82
  THREAD_COMPACT_START_METHOD,
82
83
  THREAD_GOAL_CLEAR_METHOD,
83
84
  THREAD_GOAL_GET_METHOD,
@@ -129,7 +130,7 @@ import {
129
130
  timestampFromSeconds,
130
131
  upsertCredentialFileLines,
131
132
  writeSecureCredentialFile
132
- } from "./chunk-CO7ITKY7.js";
133
+ } from "./chunk-KKAF3IGR.js";
133
134
  import {
134
135
  ENGINE_ENV,
135
136
  monolithRequest,
@@ -138,15 +139,15 @@ import {
138
139
  reloadEngineEnv,
139
140
  resolveDiffBase,
140
141
  setAgentCredentialSnapshot
141
- } from "./chunk-W347RMUG.js";
142
+ } from "./chunk-SNB36JQS.js";
142
143
  import {
143
144
  getCodexModelProvider
144
- } from "./chunk-OR4IW7GB.js";
145
+ } from "./chunk-K72ZN4MS.js";
145
146
  import {
146
147
  SUBPROCESS_MAX_BUFFER,
147
148
  execAsync,
148
149
  execFileAsync
149
- } from "./chunk-DIKPGEYN.js";
150
+ } from "./chunk-3Q7NCMIF.js";
150
151
  import {
151
152
  isRecord as isRecord2
152
153
  } from "./chunk-UZSNFLDQ.js";
@@ -166,6 +167,7 @@ import {
166
167
  CHAT_FORK_EXCERPT_OMITTED_NOTICE,
167
168
  CHAT_GOAL_EVENT_TYPE,
168
169
  CHAT_INTERRUPTED_EVENT_TYPE,
170
+ CHAT_TURN_LOST_EVENT_TYPE,
169
171
  CLAUDE_AUTH_ENV_KEYS,
170
172
  CLAUDE_AUTH_ENV_KEYS_BY_METHOD,
171
173
  CLAUDE_HAIKU_4_5_BEDROCK_MODEL,
@@ -374,7 +376,7 @@ import {
374
376
  workspaceTokenRequestSchema,
375
377
  workspaceTokenResponseSchema,
376
378
  wrapInTag
377
- } from "./chunk-PBDIRFDJ.js";
379
+ } from "./chunk-T6JO4V4V.js";
378
380
  import {
379
381
  cloneMcpConfig,
380
382
  ensureCompatibilityImports,
@@ -14083,6 +14085,14 @@ var CodexAspManager = class extends CodingAgentManager {
14083
14085
  }
14084
14086
  const threadId = this.currentThreadId;
14085
14087
  const expectedTurnId = this.activeTurnId;
14088
+ try {
14089
+ await (await getCodexAspHost()).client.request(
14090
+ THREAD_BACKGROUND_TERMINALS_CLEAN_METHOD,
14091
+ { threadId }
14092
+ );
14093
+ } catch (error) {
14094
+ console.warn("[CodexAspManager] Failed to stop the turn's running commands:", error);
14095
+ }
14086
14096
  const result = await this.requestActiveTurnWithReconciliation(
14087
14097
  "interrupt",
14088
14098
  expectedTurnId,
@@ -59408,10 +59418,10 @@ function hasActiveBackgroundWork(chat) {
59408
59418
  return (chat.provider.hasActiveBackgroundTasks?.() ?? false) || isForkPreparing(chat);
59409
59419
  }
59410
59420
  function hasActiveAgentWork(chat) {
59411
- return chat.provider.isProcessing() || hasActiveBackgroundWork(chat);
59421
+ return chat.provider.isProcessing() || hasActiveBackgroundWork(chat) || chat.turnCompletion !== null;
59412
59422
  }
59413
59423
  function hasActiveChatTurn(chat) {
59414
- return chat.hasActiveTurn || chat.pendingMessageIds.length > 0 || hasActiveBackgroundWork(chat) || (chat.settledProcessing ?? chat.provider.isProcessing());
59424
+ return chat.hasActiveTurn || chat.pendingMessageIds.length > 0 || hasActiveBackgroundWork(chat) || chat.provider.isProcessing();
59415
59425
  }
59416
59426
  function isPersistedChat(value) {
59417
59427
  if (!isRecord2(value)) {
@@ -59438,6 +59448,7 @@ function normalizePersistedChat(chat) {
59438
59448
  ...chat.chatCredential === "workspace" || agentCredentialSnapshotSchema.safeParse(chat.chatCredential).success ? { chatCredential: chat.chatCredential } : {},
59439
59449
  transcriptAuth: normalizeTranscriptAuth(chat.transcriptAuth),
59440
59450
  ...typeof chat.lastSenderKey === "string" ? { lastSenderKey: chat.lastSenderKey } : {},
59451
+ ...typeof chat.turnStartedAt === "string" ? { turnStartedAt: chat.turnStartedAt } : {},
59441
59452
  ...isRecord2(chat.uploadedHistory) && typeof chat.uploadedHistory.version === "string" && typeof chat.uploadedHistory.size === "number" ? { uploadedHistory: { version: chat.uploadedHistory.version, size: chat.uploadedHistory.size } } : {},
59442
59453
  removedMessageIds: Array.isArray(chat.removedMessageIds) ? chat.removedMessageIds.filter((id) => typeof id === "string").slice(-MAX_REMOVED_MESSAGE_IDS) : [],
59443
59454
  acceptedSendResponses: isRecord2(chat.acceptedSendResponses) ? Object.fromEntries(Object.entries(chat.acceptedSendResponses).filter((entry) => {
@@ -59480,20 +59491,29 @@ var ChatService = class {
59480
59491
  await mkdir17(OPENCODE_HISTORY_DIR, { recursive: true });
59481
59492
  await mkdir17(CHAT_SENDERS_DIR, { recursive: true });
59482
59493
  const persisted = await this.loadChats();
59494
+ const lostTurn = persisted.some((chat) => chat.turnStartedAt);
59483
59495
  for (const chat of persisted) {
59484
59496
  const runtime = this.createRuntimeChat(chat);
59497
+ if (chat.turnStartedAt && !chat.deletedAt) {
59498
+ await runtime.provider.persistHistoryEvent({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: CHAT_TURN_LOST_EVENT_TYPE, payload: {} }).then(() => {
59499
+ delete chat.turnStartedAt;
59500
+ }).catch((error) => console.error("[ChatService] Failed to record a turn lost to an engine restart:", { chatId: chat.id, error }));
59501
+ } else {
59502
+ delete chat.turnStartedAt;
59503
+ }
59485
59504
  if (chat.uploadedHistory) {
59486
59505
  const size = await stat6(this.historyPath(chat)).then(({ size: size2 }) => size2, () => null);
59487
59506
  if (size === chat.uploadedHistory.size) runtime.historyVersion = chat.uploadedHistory.version;
59488
59507
  }
59489
59508
  this.chats.set(chat.id, runtime);
59490
59509
  }
59510
+ if (lostTurn) await this.persistAllChats();
59491
59511
  }
59492
59512
  listChats(includeChildren = false) {
59493
59513
  return Array.from(this.chats.values()).filter((chat) => !chat.persisted.deletedAt && (includeChildren || chat.persisted.parentChatId === null)).map((chat) => this.toSummary(chat)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
59494
59514
  }
59495
59515
  hasActiveAgentWork(provider) {
59496
- return [...this.chats.values()].some((chat) => chat.persisted.provider === provider && (chat.provider.isProcessing() || chat.acceptingMessages > 0 || chat.pendingMessageIds.length > 0));
59516
+ return [...this.chats.values()].some((chat) => chat.persisted.provider === provider && (hasActiveAgentWork(chat) || chat.acceptingMessages > 0 || chat.pendingMessageIds.length > 0));
59497
59517
  }
59498
59518
  listDeletedChats(includeChildren = false) {
59499
59519
  return Array.from(this.chats.values()).filter((chat) => chat.persisted.deletedAt && (includeChildren || chat.persisted.parentChatId === null)).map((chat) => this.toSummary(chat)).sort((a, b) => (b.deletedAt ?? "").localeCompare(a.deletedAt ?? ""));
@@ -59853,7 +59873,7 @@ var ChatService = class {
59853
59873
  async interrupt(chatId) {
59854
59874
  const chat = this.requireChat(chatId);
59855
59875
  const result = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
59856
- this.endTrackedTurn(chatId);
59876
+ this.endTrackedTurn(chat);
59857
59877
  chat.hasActiveTurn = false;
59858
59878
  chat.activeMessageId = null;
59859
59879
  chat.pendingMessageIds = [];
@@ -59876,7 +59896,7 @@ var ChatService = class {
59876
59896
  return { interrupted: false, queue: [], goal: null };
59877
59897
  }
59878
59898
  const interruptResult = await chat.provider.interrupt((messageIds) => this.forgetPendingMessages(chat, messageIds));
59879
- this.endTrackedTurn(chatId);
59899
+ this.endTrackedTurn(chat);
59880
59900
  chat.hasActiveTurn = false;
59881
59901
  chat.activeMessageId = null;
59882
59902
  chat.pendingMessageIds = [];
@@ -60495,7 +60515,7 @@ var ChatService = class {
60495
60515
  lastTurnErrors: null,
60496
60516
  turnRequest: null,
60497
60517
  turnAnswer: null,
60498
- settledProcessing: null,
60518
+ turnCompletion: null,
60499
60519
  errorNotificationTargets: /* @__PURE__ */ new Map(),
60500
60520
  activeErrorNotificationTargets: [],
60501
60521
  slackActivityMessageIds: /* @__PURE__ */ new Set(),
@@ -60542,10 +60562,11 @@ var ChatService = class {
60542
60562
  activateTurn(chat, messageId) {
60543
60563
  chat.hasActiveTurn = true;
60544
60564
  chat.activeMessageId = messageId;
60565
+ chat.persisted.turnStartedAt = (/* @__PURE__ */ new Date()).toISOString();
60566
+ this.touch(chat);
60545
60567
  const request = chat.acceptedUserEvents.get(messageId)?.payload.message;
60546
60568
  chat.turnRequest = typeof request === "string" ? request.trim().slice(0, LAST_MESSAGE_PREVIEW_MAX) : null;
60547
60569
  chat.turnAnswer = null;
60548
- chat.settledProcessing = null;
60549
60570
  const context = this.agentChatActivityTrackerService.noteTurnStarted(
60550
60571
  chat.persisted.id,
60551
60572
  messageId,
@@ -60674,14 +60695,15 @@ var ChatService = class {
60674
60695
  handleProcessingStopped(chatId) {
60675
60696
  const chat = this.getRuntimeChat(chatId);
60676
60697
  if (!chat) return;
60677
- chat.settledProcessing = null;
60678
60698
  this.publish({ type: "chat.updated", payload: { chat: this.toSummary(chat) } }).catch(() => {
60679
60699
  });
60680
60700
  this.notifyWorkspaceAgentIdle();
60681
60701
  }
60682
- endTrackedTurn(chatId) {
60683
- this.agentTokenUsageTracker.endTurn(chatId);
60684
- this.agentChatActivityTrackerService.noteTurnEnded(chatId);
60702
+ endTrackedTurn(chat) {
60703
+ this.agentTokenUsageTracker.endTurn(chat.persisted.id);
60704
+ this.agentChatActivityTrackerService.noteTurnEnded(chat.persisted.id);
60705
+ delete chat.persisted.turnStartedAt;
60706
+ this.touch(chat);
60685
60707
  }
60686
60708
  async handleTurnFinished(chatId) {
60687
60709
  const chat = this.getRuntimeChat(chatId);
@@ -60698,7 +60720,7 @@ var ChatService = class {
60698
60720
  chat.activeMessageId = null;
60699
60721
  chat.slackActivity?.finish();
60700
60722
  chat.slackActivity = null;
60701
- this.endTrackedTurn(chatId);
60723
+ this.endTrackedTurn(chat);
60702
60724
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
60703
60725
  const processing = chat.provider.getQueue().length > 0 || (chat.provider.hasActiveBackgroundTasks?.() ?? false);
60704
60726
  const exhaustion = chat.quotaExhaustion;
@@ -60713,12 +60735,14 @@ var ChatService = class {
60713
60735
  }
60714
60736
  }
60715
60737
  const stillProcessing = processing || forked;
60716
- chat.settledProcessing = stillProcessing;
60717
60738
  const answer = chat.turnAnswer;
60718
60739
  chat.turnAnswer = null;
60719
60740
  const responseComplete = this.publishTurnCompleted(chat, completedAt, stillProcessing, answer, userTurn);
60720
- await this.publishAgentTurnCompleteWebhook(chat, completedAt, stillProcessing, forked ? null : exhaustion, responseComplete);
60721
- this.notifyWorkspaceAgentIdle();
60741
+ const completion = this.publishAgentTurnCompleteWebhook(chat, completedAt, stillProcessing, forked ? null : exhaustion, responseComplete, chat.turnCompletion).catch((error) => console.error("[ChatService] Failed to report turn completion:", { chatId, error })).finally(() => {
60742
+ if (chat.turnCompletion === completion) chat.turnCompletion = null;
60743
+ this.notifyWorkspaceAgentIdle();
60744
+ });
60745
+ chat.turnCompletion = completion;
60722
60746
  }
60723
60747
  async publishTurnCompleted(chat, completedAt, processing, answer, userTurn) {
60724
60748
  if (chat.persisted.parentChatId) {
@@ -60837,13 +60861,16 @@ var ChatService = class {
60837
60861
  } catch {
60838
60862
  }
60839
60863
  }
60840
- async publishAgentTurnCompleteWebhook(chat, completedAt, processing, quotaExhaustion, responseComplete) {
60864
+ async publishAgentTurnCompleteWebhook(chat, completedAt, processing, quotaExhaustion, responseComplete, previousCompletion) {
60841
60865
  const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
60842
60866
  const errors = chat.persisted.parentChatId === null ? chat.lastTurnErrors : null;
60843
60867
  chat.lastTurnErrors = null;
60844
60868
  const errorNotificationTargets = chat.activeErrorNotificationTargets;
60845
60869
  chat.activeErrorNotificationTargets = [];
60846
- const refreshedRepoStatuses = await this.refreshTurnRepos(chat);
60870
+ const observedBranches = chat.observedBranchesByRepo;
60871
+ chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
60872
+ await previousCompletion;
60873
+ const refreshedRepoStatuses = await this.refreshTurnRepos(observedBranches);
60847
60874
  const repoStatuses = refreshedRepoStatuses ?? [];
60848
60875
  await responseComplete;
60849
60876
  if (chat.persisted.parentChatId) return;
@@ -60855,7 +60882,8 @@ var ChatService = class {
60855
60882
  const payload = {
60856
60883
  ...linearSessionId ? { linearSessionId } : {},
60857
60884
  completedAt,
60858
- processing,
60885
+ // A newer turn may have started while this completion ran; reporting idle would clear its Slack status.
60886
+ processing: processing || hasActiveChatTurn(chat),
60859
60887
  chatId: chat.persisted.id,
60860
60888
  repoStatuses,
60861
60889
  ...unlinkedPullRequestUrls.length > 0 ? { unlinkedPullRequestUrls } : {},
@@ -60868,9 +60896,7 @@ var ChatService = class {
60868
60896
  console.error("[ChatService] Failed to send agent_turn_complete event:", error);
60869
60897
  }
60870
60898
  }
60871
- async refreshTurnRepos(chat) {
60872
- const observedBranches = chat.observedBranchesByRepo;
60873
- chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
60899
+ async refreshTurnRepos(observedBranches) {
60874
60900
  try {
60875
60901
  const repoStatuses = await refreshRepoStatuses(observedBranches);
60876
60902
  console.log(`Repository Statuses Refreshed: `, repoStatuses);
@@ -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-G647LINI.js";
6
+ } from "./chunk-WJCC4O2M.js";
7
7
  import {
8
8
  isRecord
9
9
  } from "./chunk-UZSNFLDQ.js";
10
10
  import {
11
11
  shouldRefreshPullRequests
12
- } from "./chunk-PBDIRFDJ.js";
12
+ } from "./chunk-T6JO4V4V.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-PBDIRFDJ.js";
12
+ } from "./chunk-T6JO4V4V.js";
13
13
  import "./chunk-VEQXQN22.js";
14
14
 
15
15
  // src/subagent-mcp.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.1067",
3
+ "version": "0.1.1070",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",