replicas-engine 0.1.702 → 0.1.704

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/index.js CHANGED
@@ -1,9 +1,47 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- ACCEPTED_USER_MESSAGE_SOURCE,
3
+ ENGINE_ENV,
4
+ IS_WARMING_MODE,
5
+ evaluateCommandProtection,
6
+ extractToolCommand,
7
+ monolithRequest,
8
+ monolithService,
9
+ reportCommandProtectionBlock,
10
+ setAgentCredentialSnapshot
11
+ } from "./chunk-ZCBMGGAC.js";
12
+ import {
4
13
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
5
- AGENT,
6
14
  AGENT_MESSAGE_DELTA_METHOD,
15
+ AppServerProcess,
16
+ COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
17
+ FILE_CHANGE_OUTPUT_DELTA_METHOD,
18
+ ITEM_COMPLETED_METHOD,
19
+ ITEM_STARTED_METHOD,
20
+ MODEL_REROUTED_METHOD,
21
+ REASONING_SUMMARY_PART_ADDED_METHOD,
22
+ REASONING_SUMMARY_TEXT_DELTA_METHOD,
23
+ REASONING_TEXT_DELTA_METHOD,
24
+ SUBPROCESS_MAX_BUFFER,
25
+ THREAD_COMPACTED_METHOD,
26
+ THREAD_GOAL_CLEARED_METHOD,
27
+ THREAD_GOAL_UPDATED_METHOD,
28
+ THREAD_TOKEN_USAGE_UPDATED_METHOD,
29
+ TURN_COMPLETED_METHOD,
30
+ TURN_PLAN_UPDATED_METHOD,
31
+ TURN_STARTED_METHOD,
32
+ buildCodexAgentEnv,
33
+ dispatchAspNotification,
34
+ execAsync,
35
+ execFileAsync,
36
+ putPresignedFile,
37
+ recoverCompletedTurn
38
+ } from "./chunk-EC4MFY4M.js";
39
+ import {
40
+ isRecord as isRecord2
41
+ } from "./chunk-2RB7SIP3.js";
42
+ import {
43
+ ACCEPTED_USER_MESSAGE_SOURCE,
44
+ AGENT,
7
45
  AGENT_MODELS,
8
46
  AI_GATEWAY_BASE_URL,
9
47
  ASTER_BASE_URL,
@@ -12,7 +50,6 @@ import {
12
50
  ASTER_PROVIDER,
13
51
  AUTH_FALLBACK_EVENT_TYPE,
14
52
  AUTH_RETRY_STATUS_EVENT_TYPE,
15
- AppServerProcess,
16
53
  CHAT_GOAL_EVENT_TYPE,
17
54
  CHAT_INTERRUPTED_EVENT_TYPE,
18
55
  CLAUDE_AUTH_ENV_KEYS,
@@ -32,7 +69,6 @@ import {
32
69
  CODEX_AUTH_ENV_KEYS,
33
70
  CODEX_AUTH_ENV_KEYS_BY_METHOD,
34
71
  CODEX_QUOTA_STATUS_EVENT_TYPE,
35
- COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
36
72
  COMPACTION_STATUS_EVENT_TYPE,
37
73
  CONTEXT_USAGE_EVENT_TYPE,
38
74
  CURSOR_CONNECTION_STATUS_EVENT_TYPE,
@@ -52,14 +88,11 @@ import {
52
88
  ENGINE_HEALTH_MAX_WAIT_MS,
53
89
  ENGINE_HEALTH_WAIT_HEADER,
54
90
  ENGINE_HEALTH_WAIT_QUERY_PARAM,
55
- FILE_CHANGE_OUTPUT_DELTA_METHOD,
56
91
  GITHUB_CREDENTIAL_REFRESH_PATH,
57
92
  GITLAB_CREDENTIAL_REFRESH_PATH,
58
93
  GIT_CREDENTIAL_HELPER_FILENAME,
59
94
  HOOK_EXEC_MAX_BUFFER_BYTES,
60
95
  IMAGE_MEDIA_TYPES,
61
- ITEM_COMPLETED_METHOD,
62
- ITEM_STARTED_METHOD,
63
96
  InFlightMap,
64
97
  MAX_CANVAS_FILE_BYTES,
65
98
  MAX_CANVAS_PREVIEW_BYTES,
@@ -68,34 +101,19 @@ import {
68
101
  MEMORY_ROOT,
69
102
  MEMORY_SUMMARY_FILENAME,
70
103
  MERGED_MESSAGE_SEPARATOR,
71
- MODEL_REROUTED_METHOD,
72
- REASONING_SUMMARY_PART_ADDED_METHOD,
73
- REASONING_SUMMARY_TEXT_DELTA_METHOD,
74
- REASONING_TEXT_DELTA_METHOD,
75
104
  REPLICAS_CONFIG_FILENAMES,
76
- REPLICAS_RUNTIME_ENV_ALIASES,
77
105
  SANDBOX_PATHS,
78
- SUBPROCESS_MAX_BUFFER,
79
- THREAD_COMPACTED_METHOD,
80
- THREAD_GOAL_CLEARED_METHOD,
81
- THREAD_GOAL_UPDATED_METHOD,
82
- THREAD_TOKEN_USAGE_UPDATED_METHOD,
83
106
  TIMEOUT,
84
107
  TOKEN_USAGE_PATH,
85
- TURN_COMPLETED_METHOD,
86
- TURN_PLAN_UPDATED_METHOD,
87
- TURN_STARTED_METHOD,
88
108
  TaskAccumulator,
89
109
  USER_MESSAGE_ID_PAYLOAD_KEY,
90
110
  VALID_AGENT_PROVIDERS,
91
111
  VALID_RELAY_SUBAGENT_PROVIDERS,
92
112
  VALID_THINKING_LEVELS,
93
- agentCredentialSnapshotSchema,
94
113
  applyCodexAspTranscriptDelta,
95
114
  areSameUserMessageEvents,
96
115
  areUserMessagesWithinMatchWindow,
97
116
  autoCompactThresholdField,
98
- buildCodexAgentEnv,
99
117
  buildCodexTokenUsageContextUsagePayload,
100
118
  buildGitCredentialHelperScript,
101
119
  buildHookOutputPreview,
@@ -122,16 +140,10 @@ import {
122
140
  decodePathSegments,
123
141
  detectAgentQuotaLimit,
124
142
  detectLanguageByPath,
125
- dispatchAspNotification,
126
- execAsync,
127
- execFileAsync,
128
- extractCommandProtectionCommandText,
129
143
  extractErrorText,
130
144
  extractToolResultText,
131
145
  fetchAiGatewayModels,
132
146
  fetchModelsDevCatalog,
133
- findGitCommitSignals,
134
- findPrMergeSignals,
135
147
  getChatHistoryPageSenders,
136
148
  getChatHistoryPageWindow,
137
149
  getChatTranscriptHistorySize,
@@ -172,6 +184,7 @@ import {
172
184
  isTransientErrorText,
173
185
  isValidAgentProvider,
174
186
  isValidEngineLogSessionId,
187
+ mayCreatePullRequest,
175
188
  mergeSlashCommands,
176
189
  normalizeAutoCompactThreshold,
177
190
  normalizeClaudeModel,
@@ -187,179 +200,30 @@ import {
187
200
  parseGitRemote,
188
201
  parseLatestCodexAspTranscriptTurn,
189
202
  parseMcpToolName,
190
- parsePosixEnvFile,
191
203
  parseReplicasConfigString,
192
204
  percentage,
193
- putPresignedFile,
194
205
  raceWithTimeout,
195
206
  readJsonlPage,
196
- readReplicasRuntimeEnv,
197
- recoverCompletedTurn,
198
207
  renumberCodexAspTranscriptItems,
199
208
  resolveWarmHookConfig,
200
209
  sanitizeCanvasFilename,
201
210
  sendChatMessageRequestSchema,
202
211
  serializeCanvasContentResponse,
203
212
  shellQuotePosix,
204
- stripAgentDiagnosticErrors
205
- } from "./chunk-UNES5UCP.js";
213
+ stripAgentDiagnosticErrors,
214
+ withTimeout
215
+ } from "./chunk-WUJ4IALO.js";
206
216
 
207
217
  // src/index.ts
208
218
  import { serve } from "@hono/node-server";
209
219
  import { Hono as Hono2 } from "hono";
210
- import { existsSync as existsSync12 } from "fs";
220
+ import { existsSync as existsSync13 } from "fs";
211
221
  import { randomUUID as randomUUID11 } from "crypto";
212
222
  import { connect } from "net";
213
223
 
214
224
  // src/managers/github-token-manager.ts
215
225
  import path from "path";
216
226
 
217
- // src/engine-env.ts
218
- import { readFileSync as readFileSync2 } from "fs";
219
- import { homedir as homedir2 } from "os";
220
- import { join as join2 } from "path";
221
-
222
- // src/runtime-env-loader.ts
223
- import { readFileSync } from "fs";
224
- import { homedir } from "os";
225
- import { join } from "path";
226
- function loadRuntimeEnvFile() {
227
- let content;
228
- try {
229
- content = readFileSync(join(homedir(), ".replicas", "runtime-env.sh"), "utf-8");
230
- } catch {
231
- return;
232
- }
233
- for (const [key, value] of Object.entries(parsePosixEnvFile(content))) {
234
- process.env[key] = value;
235
- }
236
- }
237
-
238
- // src/utils/type-guards.ts
239
- function isRecord2(value) {
240
- return typeof value === "object" && value !== null;
241
- }
242
-
243
- // src/engine-env.ts
244
- var SANDBOX_IMAGE_VERSION_FILE = "/usr/local/lib/replicas-sandbox-image-version";
245
- function readEnv(name) {
246
- const value = process.env[name]?.trim();
247
- return value ? value : void 0;
248
- }
249
- function readSandboxImageVersion() {
250
- const environmentVersion = readEnv("REPLICAS_SANDBOX_IMAGE_VERSION");
251
- if (environmentVersion) return environmentVersion;
252
- try {
253
- return readFileSync2(SANDBOX_IMAGE_VERSION_FILE, "utf8").trim() || "development";
254
- } catch {
255
- return "development";
256
- }
257
- }
258
- function parsePort(value) {
259
- if (!value) {
260
- return 3737;
261
- }
262
- const parsed = Number(value);
263
- if (!Number.isInteger(parsed) || parsed <= 0) {
264
- throw new Error("Invalid engine environment: REPLICAS_ENGINE_PORT must be a positive integer");
265
- }
266
- return parsed;
267
- }
268
- function requireDefined(value, name) {
269
- if (value === void 0 || value === null) {
270
- throw new Error(`Invalid engine environment: ${name} is required`);
271
- }
272
- return value;
273
- }
274
- function requireValidURL(value, name) {
275
- try {
276
- new URL(value);
277
- return value;
278
- } catch {
279
- throw new Error(`Invalid engine environment: ${name} must be a valid URL`);
280
- }
281
- }
282
- function parseClaudeAuthMethod(value) {
283
- if (value === "oauth" || value === "api_key" || value === "bedrock" || value === "foundry") {
284
- return value;
285
- }
286
- return void 0;
287
- }
288
- function parseCodexAuthMethod(value) {
289
- if (value === "oauth" || value === "api_key" || value === "foundry") {
290
- return value;
291
- }
292
- return void 0;
293
- }
294
- function parseAgentCredentialSnapshots(value) {
295
- if (!value) return {};
296
- try {
297
- const parsed = JSON.parse(value);
298
- if (!isRecord2(parsed)) return {};
299
- const snapshots = {};
300
- for (const [provider, snapshot] of Object.entries(parsed)) {
301
- if (!isValidAgentProvider(provider)) continue;
302
- const parsedSnapshot = agentCredentialSnapshotSchema.safeParse(snapshot);
303
- if (parsedSnapshot.success) snapshots[provider] = parsedSnapshot.data;
304
- }
305
- return snapshots;
306
- } catch {
307
- return {};
308
- }
309
- }
310
- var IS_WARMING_MODE = process.argv.includes("--warming");
311
- function loadEngineEnv() {
312
- loadRuntimeEnvFile();
313
- const HOME_DIR = homedir2();
314
- const env = {
315
- // Defined: always available
316
- REPLICAS_ENGINE_SECRET: requireDefined(readEnv("REPLICAS_ENGINE_SECRET"), "REPLICAS_ENGINE_SECRET"),
317
- REPLICAS_ENGINE_PORT: parsePort(readEnv("REPLICAS_ENGINE_PORT")),
318
- REPLICAS_MONOLITH_URL: requireValidURL(
319
- requireDefined(readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.monolithUrl), "REPLICAS_MONOLITH_URL"),
320
- "REPLICAS_MONOLITH_URL"
321
- ),
322
- HOME_DIR,
323
- WORKSPACE_ROOT: join2(HOME_DIR, "workspaces"),
324
- REPLICAS_SANDBOX_IMAGE_VERSION: readSandboxImageVersion(),
325
- // Runtime: may not be set during warming
326
- REPLICAS_WORKSPACE_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.workspaceId),
327
- REPLICAS_LINEAR_SESSION_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearSessionId),
328
- REPLICAS_LINEAR_ACCESS_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.linearAccessToken),
329
- REPLICAS_SLACK_BOT_TOKEN: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackBotToken),
330
- REPLICAS_SLACK_CHANNEL_ID: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackChannelId),
331
- REPLICAS_SLACK_THREAD_TS: readReplicasRuntimeEnv(readEnv, REPLICAS_RUNTIME_ENV_ALIASES.slackThreadTs),
332
- ANTHROPIC_API_KEY: readEnv("ANTHROPIC_API_KEY"),
333
- OPENAI_API_KEY: readEnv("OPENAI_API_KEY"),
334
- CURSOR_API_KEY: readEnv("CURSOR_API_KEY"),
335
- AI_GATEWAY_API_KEY: readEnv("AI_GATEWAY_API_KEY"),
336
- CLAUDE_CODE_USE_BEDROCK: readEnv("CLAUDE_CODE_USE_BEDROCK"),
337
- AWS_ACCESS_KEY_ID: readEnv("AWS_ACCESS_KEY_ID"),
338
- AWS_SECRET_ACCESS_KEY: readEnv("AWS_SECRET_ACCESS_KEY"),
339
- AWS_REGION: readEnv("AWS_REGION"),
340
- ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION: readEnv("ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION"),
341
- REPLICAS_CLAUDE_AUTH_METHOD: parseClaudeAuthMethod(readEnv("REPLICAS_CLAUDE_AUTH_METHOD")),
342
- REPLICAS_CODEX_AUTH_METHOD: parseCodexAuthMethod(readEnv("REPLICAS_CODEX_AUTH_METHOD")),
343
- REPLICAS_AGENT_CREDENTIALS: parseAgentCredentialSnapshots(readEnv("REPLICAS_AGENT_CREDENTIALS")),
344
- REPLICAS_ENV_SYSTEM_PROMPT: readEnv("REPLICAS_ENV_SYSTEM_PROMPT"),
345
- REPLICAS_ENV_START_HOOK: readEnv("REPLICAS_ENV_START_HOOK"),
346
- REPLICAS_DISABLE_AUTO_START_HOOKS: readEnv("REPLICAS_DISABLE_AUTO_START_HOOKS")?.toLowerCase() === "true",
347
- REPLICAS_ENGINE_DEFER_INITIALIZATION: readEnv("REPLICAS_ENGINE_DEFER_INITIALIZATION")?.toLowerCase() === "true"
348
- };
349
- if (!IS_WARMING_MODE && !env.REPLICAS_WORKSPACE_ID) {
350
- console.error("REPLICAS_WORKSPACE_ID is not set \u2014 this is required in normal (non-warming) mode");
351
- }
352
- return env;
353
- }
354
- var ENGINE_ENV = loadEngineEnv();
355
- function setAgentCredentialSnapshot(provider, snapshot) {
356
- ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS = {
357
- ...ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS,
358
- [provider]: snapshot
359
- };
360
- process.env.REPLICAS_AGENT_CREDENTIALS = JSON.stringify(ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS);
361
- }
362
-
363
227
  // src/services/credential-fallbacks.ts
364
228
  var fallbacksByAgent = /* @__PURE__ */ new Map();
365
229
  var exhaustedByAgent = /* @__PURE__ */ new Map();
@@ -519,69 +383,10 @@ var BaseRefreshManager = class {
519
383
  }
520
384
  };
521
385
 
522
- // src/services/monolith-service.ts
523
- async function monolithRequest(path6, init = {}) {
524
- if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
525
- throw new Error("REPLICAS_WORKSPACE_ID is not set; cannot call monolith");
526
- }
527
- const headers = {
528
- Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
529
- "X-Workspace-Id": ENGINE_ENV.REPLICAS_WORKSPACE_ID
530
- };
531
- if (!(init.body instanceof FormData)) headers["Content-Type"] = "application/json";
532
- return fetch(`${ENGINE_ENV.REPLICAS_MONOLITH_URL}${path6}`, {
533
- method: init.method ?? "POST",
534
- headers,
535
- body: init.body === void 0 ? void 0 : init.body instanceof FormData ? init.body : JSON.stringify(init.body),
536
- signal: init.signal
537
- });
538
- }
539
- var MonolithService = class {
540
- async getRelaySubagentProviders() {
541
- try {
542
- const response = await monolithRequest("/v1/engine/relay-subagent-providers", { method: "GET" });
543
- if (!response.ok) {
544
- return null;
545
- }
546
- const body = await response.json();
547
- return isRecord(body) && Array.isArray(body.providers) && body.providers.every((provider) => typeof provider === "string" && isValidAgentProvider(provider)) ? body.providers : null;
548
- } catch {
549
- return null;
550
- }
551
- }
552
- async getOpenRouterModels() {
553
- try {
554
- const response = await monolithRequest("/v1/engine/openrouter-models", { method: "GET" });
555
- if (!response.ok) {
556
- return null;
557
- }
558
- const body = await response.json();
559
- return isRecord(body) && Array.isArray(body.models) && body.models.every((model) => typeof model === "string") ? body.models : null;
560
- } catch {
561
- return null;
562
- }
563
- }
564
- async sendEvent(event) {
565
- if (!ENGINE_ENV.REPLICAS_WORKSPACE_ID) {
566
- return;
567
- }
568
- try {
569
- const response = await monolithRequest("/v1/engine/webhook", { body: event });
570
- if (!response.ok) {
571
- const errorText = await response.text();
572
- console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
573
- }
574
- } catch (error) {
575
- console.error("[MonolithService] Failed to send event:", error);
576
- }
577
- }
578
- };
579
- var monolithService = new MonolithService();
580
-
581
386
  // src/utils/file.ts
582
387
  import { randomUUID } from "crypto";
583
388
  import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
584
- import { dirname, join as join3 } from "path";
389
+ import { dirname, join } from "path";
585
390
 
586
391
  // src/utils/async-lock.ts
587
392
  var AsyncLock = class {
@@ -614,8 +419,8 @@ async function writeSecureCredentialFile(filePath, content, options) {
614
419
  await atomicWriteFile(filePath, content, { mode: 384 });
615
420
  }
616
421
  async function configureManagedGitCredentialHelper(homeDir) {
617
- const credentialsPath = join3(homeDir, ".git-credentials");
618
- const helperPath = join3(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
422
+ const credentialsPath = join(homeDir, ".git-credentials");
423
+ const helperPath = join(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
619
424
  await atomicWriteFile(helperPath, buildGitCredentialHelperScript(credentialsPath), { mode: 448 });
620
425
  await execFileAsync("git", ["config", "--global", "credential.helper", helperPath], {
621
426
  env: { ...process.env, HOME: homeDir }
@@ -650,15 +455,15 @@ function removeCredentialFileLines(filePath, shouldRemove) {
650
455
  // src/git/service.ts
651
456
  import { readdir, readFile as readFile3, stat } from "fs/promises";
652
457
  import { spawn } from "child_process";
653
- import { join as join6 } from "path";
458
+ import { join as join4 } from "path";
654
459
 
655
460
  // src/utils/state.ts
656
461
  import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
657
462
  import { existsSync } from "fs";
658
- import { join as join4 } from "path";
659
- import { homedir as homedir3 } from "os";
660
- var STATE_DIR = join4(homedir3(), ".replicas");
661
- var STATE_FILE = join4(STATE_DIR, "engine-state.json");
463
+ import { join as join2 } from "path";
464
+ import { homedir } from "os";
465
+ var STATE_DIR = join2(homedir(), ".replicas");
466
+ var STATE_FILE = join2(STATE_DIR, "engine-state.json");
662
467
  var DEFAULT_STATE = {
663
468
  repos: {}
664
469
  };
@@ -755,8 +560,8 @@ async function saveRepoState(repoName, state, fallbackState) {
755
560
  }
756
561
 
757
562
  // src/git/commands.ts
758
- import { readFileSync as readFileSync3 } from "fs";
759
- import { join as join5 } from "path";
563
+ import { readFileSync } from "fs";
564
+ import { join as join3 } from "path";
760
565
  async function runGitCommand(args, cwd, options = {}) {
761
566
  const { stdout } = await execFileAsync("git", args, {
762
567
  cwd,
@@ -767,7 +572,7 @@ async function runGitCommand(args, cwd, options = {}) {
767
572
  }
768
573
  function readRepoHeadBranch(repoPath) {
769
574
  try {
770
- const contents = readFileSync3(join5(repoPath, ".git", "HEAD"), "utf-8").trim();
575
+ const contents = readFileSync(join3(repoPath, ".git", "HEAD"), "utf-8").trim();
771
576
  const match = contents.match(/^ref:\s+refs\/heads\/(.+)$/);
772
577
  return match ? match[1] : null;
773
578
  } catch {
@@ -829,13 +634,13 @@ var GitService = class {
829
634
  const repos = [];
830
635
  let complete = true;
831
636
  for (const entry of entries) {
832
- const fullPath = join6(root, entry);
637
+ const fullPath = join4(root, entry);
833
638
  try {
834
639
  const entryStat = await stat(fullPath);
835
640
  if (!entryStat.isDirectory()) {
836
641
  continue;
837
642
  }
838
- const hasGit = Boolean(await this.safeStat(join6(fullPath, ".git")));
643
+ const hasGit = Boolean(await this.safeStat(join4(fullPath, ".git")));
839
644
  if (!hasGit) {
840
645
  continue;
841
646
  }
@@ -1026,7 +831,7 @@ var GitService = class {
1026
831
  let total = 0;
1027
832
  for (const path6 of paths) {
1028
833
  try {
1029
- const contents = await readFile3(join6(repoPath, path6));
834
+ const contents = await readFile3(join4(repoPath, path6));
1030
835
  if (contents.length === 0 || contents.includes(0)) {
1031
836
  continue;
1032
837
  }
@@ -1214,7 +1019,7 @@ var GitService = class {
1214
1019
  }
1215
1020
  async getGitLabAccessToken(host) {
1216
1021
  try {
1217
- const credentials = await readFile3(join6(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
1022
+ const credentials = await readFile3(join4(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
1218
1023
  for (const line of credentials.split("\n")) {
1219
1024
  const trimmed = line.trim();
1220
1025
  if (!trimmed) continue;
@@ -1761,8 +1566,8 @@ var infisicalTokenManager = new InfisicalTokenManager();
1761
1566
 
1762
1567
  // src/utils/logger.ts
1763
1568
  import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
1764
- import { homedir as homedir4 } from "os";
1765
- import { join as join7 } from "path";
1569
+ import { homedir as homedir2 } from "os";
1570
+ import { join as join5 } from "path";
1766
1571
  import { format } from "util";
1767
1572
  import { randomBytes } from "crypto";
1768
1573
 
@@ -1837,7 +1642,7 @@ var StreamWriter = class {
1837
1642
  };
1838
1643
 
1839
1644
  // src/utils/logger.ts
1840
- var LOG_DIR = join7(homedir4(), ".replicas", "logs");
1645
+ var LOG_DIR = join5(homedir2(), ".replicas", "logs");
1841
1646
  var EngineLogger = class {
1842
1647
  _sessionId = null;
1843
1648
  patched = false;
@@ -1848,7 +1653,7 @@ var EngineLogger = class {
1848
1653
  async initialize() {
1849
1654
  await mkdir3(LOG_DIR, { recursive: true });
1850
1655
  this._sessionId = this.createSessionId();
1851
- const logPath = join7(LOG_DIR, `${this._sessionId}.log`);
1656
+ const logPath = join5(LOG_DIR, `${this._sessionId}.log`);
1852
1657
  await writeFile2(logPath, `=== Replicas Engine Session ${this._sessionId} ===
1853
1658
  `, "utf-8");
1854
1659
  this.writer.open(logPath);
@@ -1895,32 +1700,32 @@ var engineLogger = new EngineLogger();
1895
1700
  // src/services/replicas-config-service.ts
1896
1701
  import { readFile as readFile6, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
1897
1702
  import { existsSync as existsSync3 } from "fs";
1898
- import { join as join11 } from "path";
1899
- import { homedir as homedir8 } from "os";
1703
+ import { join as join9 } from "path";
1704
+ import { homedir as homedir6 } from "os";
1900
1705
  import { spawn as spawn2 } from "child_process";
1901
1706
 
1902
1707
  // src/services/environment-details-service.ts
1903
1708
  import { mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
1904
1709
  import { existsSync as existsSync2 } from "fs";
1905
- import { homedir as homedir6 } from "os";
1906
- import { join as join9 } from "path";
1710
+ import { homedir as homedir4 } from "os";
1711
+ import { join as join7 } from "path";
1907
1712
 
1908
1713
  // src/managers/agent-auth-paths.ts
1909
- import { homedir as homedir5 } from "os";
1910
- import { join as join8 } from "path";
1911
- var OPENCODE_AUTH_PATH = join8(homedir5(), ".local", "share", "opencode", "auth.json");
1912
- var PI_AUTH_PATH = join8(homedir5(), ".pi", "agent", "auth.json");
1913
- var DEEPSEEK_AUTH_PATH = join8(homedir5(), ".replicas", "deepseek-auth.json");
1714
+ import { homedir as homedir3 } from "os";
1715
+ import { join as join6 } from "path";
1716
+ var OPENCODE_AUTH_PATH = join6(homedir3(), ".local", "share", "opencode", "auth.json");
1717
+ var PI_AUTH_PATH = join6(homedir3(), ".pi", "agent", "auth.json");
1718
+ var DEEPSEEK_AUTH_PATH = join6(homedir3(), ".replicas", "deepseek-auth.json");
1914
1719
 
1915
1720
  // src/managers/deepseek-auth.ts
1916
- import { readFileSync as readFileSync4 } from "fs";
1721
+ import { readFileSync as readFileSync2 } from "fs";
1917
1722
  import { z } from "zod";
1918
1723
  var authSchema = z.object({
1919
1724
  openrouter: z.object({ type: z.literal("api"), key: z.string().min(1) })
1920
1725
  });
1921
1726
  function getDeepseekApiKey() {
1922
1727
  try {
1923
- const parsed = authSchema.safeParse(JSON.parse(readFileSync4(DEEPSEEK_AUTH_PATH, "utf8")));
1728
+ const parsed = authSchema.safeParse(JSON.parse(readFileSync2(DEEPSEEK_AUTH_PATH, "utf8")));
1924
1729
  return parsed.success ? parsed.data.openrouter.key : null;
1925
1730
  } catch {
1926
1731
  return null;
@@ -1933,11 +1738,11 @@ function detectCodexAuthMethod() {
1933
1738
  }
1934
1739
 
1935
1740
  // src/services/environment-details-service.ts
1936
- var REPLICAS_DIR = join9(homedir6(), ".replicas");
1937
- var DETAILS_FILE = join9(REPLICAS_DIR, "environment-details.json");
1938
- var CLAUDE_CREDENTIALS_PATH = join9(homedir6(), ".claude", ".credentials.json");
1939
- var GH_HOSTS_PATH = join9(homedir6(), ".config", "gh", "hosts.yml");
1940
- var GIT_CREDENTIALS_PATH = join9(homedir6(), ".git-credentials");
1741
+ var REPLICAS_DIR = join7(homedir4(), ".replicas");
1742
+ var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
1743
+ var CLAUDE_CREDENTIALS_PATH = join7(homedir4(), ".claude", ".credentials.json");
1744
+ var GH_HOSTS_PATH = join7(homedir4(), ".config", "gh", "hosts.yml");
1745
+ var GIT_CREDENTIALS_PATH = join7(homedir4(), ".git-credentials");
1941
1746
  function detectClaudeAuthMethod() {
1942
1747
  if (existsSync2(CLAUDE_CREDENTIALS_PATH)) {
1943
1748
  return "oauth";
@@ -2149,8 +1954,8 @@ var environmentDetailsService = new EnvironmentDetailsService();
2149
1954
 
2150
1955
  // src/services/start-hook-logs-service.ts
2151
1956
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
2152
- import { homedir as homedir7 } from "os";
2153
- import { join as join10 } from "path";
1957
+ import { homedir as homedir5 } from "os";
1958
+ import { join as join8 } from "path";
2154
1959
 
2155
1960
  // src/services/hook-log-files.ts
2156
1961
  import { createHash } from "crypto";
@@ -2162,7 +1967,7 @@ function repoHookLogFilename(repoName) {
2162
1967
  }
2163
1968
 
2164
1969
  // src/services/start-hook-logs-service.ts
2165
- var LOGS_DIR = join10(homedir7(), ".replicas", "start-hook-logs");
1970
+ var LOGS_DIR = join8(homedir5(), ".replicas", "start-hook-logs");
2166
1971
  function withPreview(stored) {
2167
1972
  const preview = buildHookOutputPreview(stored.output);
2168
1973
  return { ...stored, ...preview };
@@ -2200,7 +2005,7 @@ var StartHookLogsService = class {
2200
2005
  await this.ensureDir();
2201
2006
  const log = { hookType, hookName, repoName: hookName, ...entry };
2202
2007
  const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
2203
- await writeFile3(join10(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
2008
+ await writeFile3(join8(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
2204
2009
  `, "utf-8");
2205
2010
  }
2206
2011
  async saveEnvironmentLog(entry) {
@@ -2225,7 +2030,7 @@ var StartHookLogsService = class {
2225
2030
  continue;
2226
2031
  }
2227
2032
  try {
2228
- const raw = await readFile5(join10(LOGS_DIR, file), "utf-8");
2033
+ const raw = await readFile5(join8(LOGS_DIR, file), "utf-8");
2229
2034
  const stored = normalizeStored(JSON.parse(raw));
2230
2035
  if (stored) {
2231
2036
  logs.push(withPreview(stored));
@@ -2244,7 +2049,7 @@ var StartHookLogsService = class {
2244
2049
  async getFullOutput(hookType, hookName) {
2245
2050
  const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
2246
2051
  try {
2247
- const raw = await readFile5(join10(LOGS_DIR, filename), "utf-8");
2052
+ const raw = await readFile5(join8(LOGS_DIR, filename), "utf-8");
2248
2053
  const stored = normalizeStored(JSON.parse(raw));
2249
2054
  if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
2250
2055
  return null;
@@ -2261,7 +2066,7 @@ var StartHookLogsService = class {
2261
2066
  var startHookLogsService = new StartHookLogsService();
2262
2067
 
2263
2068
  // src/services/replicas-config-service.ts
2264
- var START_HOOKS_LOG = join11(homedir8(), ".replicas", "startHooks.log");
2069
+ var START_HOOKS_LOG = join9(homedir6(), ".replicas", "startHooks.log");
2265
2070
  var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
2266
2071
  Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
2267
2072
  These hooks are currently executing in the background. You can:
@@ -2272,7 +2077,7 @@ The start hooks may install dependencies, build projects, or perform other setup
2272
2077
  If your task depends on setup being complete, check the log file before proceeding.`;
2273
2078
  async function readReplicasConfigFromDir(dirPath) {
2274
2079
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
2275
- const configPath = join11(dirPath, filename);
2080
+ const configPath = join9(dirPath, filename);
2276
2081
  if (!existsSync3(configPath)) {
2277
2082
  continue;
2278
2083
  }
@@ -2342,7 +2147,7 @@ var ReplicasConfigService = class {
2342
2147
  const logLine = `[${timestamp}] ${message}
2343
2148
  `;
2344
2149
  try {
2345
- await mkdir6(join11(homedir8(), ".replicas"), { recursive: true });
2150
+ await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
2346
2151
  await appendFile(START_HOOKS_LOG, logLine, "utf-8");
2347
2152
  } catch (error) {
2348
2153
  console.error("Failed to write to start hooks log:", error);
@@ -2354,7 +2159,7 @@ var ReplicasConfigService = class {
2354
2159
  command: params.content,
2355
2160
  label: "env start hook",
2356
2161
  repoName: "environment",
2357
- cwd: homedir8(),
2162
+ cwd: homedir6(),
2358
2163
  timeout: params.timeoutMs ?? DEFAULT_START_HOOK_TIMEOUT_MS,
2359
2164
  onOutputChunk: params.onOutputChunk
2360
2165
  });
@@ -2477,7 +2282,7 @@ var ReplicasConfigService = class {
2477
2282
  this.hooksCompleted = false;
2478
2283
  this.hooksFailed = false;
2479
2284
  try {
2480
- await mkdir6(join11(homedir8(), ".replicas"), { recursive: true });
2285
+ await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
2481
2286
  await writeFile4(
2482
2287
  START_HOOKS_LOG,
2483
2288
  `=== Start Hooks Execution Log ===
@@ -2672,11 +2477,11 @@ var replicasConfigService = new ReplicasConfigService();
2672
2477
 
2673
2478
  // src/services/event-service.ts
2674
2479
  import { mkdir as mkdir7 } from "fs/promises";
2675
- import { homedir as homedir9 } from "os";
2676
- import { join as join12 } from "path";
2480
+ import { homedir as homedir7 } from "os";
2481
+ import { join as join10 } from "path";
2677
2482
  import { randomUUID as randomUUID2 } from "crypto";
2678
- var ENGINE_DIR = join12(homedir9(), ".replicas", "engine");
2679
- var EVENTS_FILE = join12(ENGINE_DIR, "events.jsonl");
2483
+ var ENGINE_DIR = join10(homedir7(), ".replicas", "engine");
2484
+ var EVENTS_FILE = join10(ENGINE_DIR, "events.jsonl");
2680
2485
  var EventService = class {
2681
2486
  subscribers = /* @__PURE__ */ new Map();
2682
2487
  writer = new StreamWriter();
@@ -2710,9 +2515,9 @@ var eventService = new EventService();
2710
2515
  import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
2711
2516
  import { existsSync as existsSync4 } from "fs";
2712
2517
  import { randomUUID as randomUUID3 } from "crypto";
2713
- import { homedir as homedir10 } from "os";
2714
- import { dirname as dirname2, join as join13 } from "path";
2715
- var PREVIEW_PORTS_FILE = join13(homedir10(), ".replicas", "preview-ports.json");
2518
+ import { homedir as homedir8 } from "os";
2519
+ import { dirname as dirname2, join as join11 } from "path";
2520
+ var PREVIEW_PORTS_FILE = join11(homedir8(), ".replicas", "preview-ports.json");
2716
2521
  async function readPreviewsFile() {
2717
2522
  try {
2718
2523
  if (!existsSync4(PREVIEW_PORTS_FILE)) {
@@ -2822,9 +2627,9 @@ async function registerDesktopPreview() {
2822
2627
  }
2823
2628
 
2824
2629
  // src/services/chat/chat-service.ts
2825
- import { existsSync as existsSync9 } from "fs";
2826
- import { appendFile as appendFile4, copyFile, mkdir as mkdir17, readFile as readFile17, rename as rename3, rm as rm2 } from "fs/promises";
2827
- import { join as join29 } from "path";
2630
+ import { existsSync as existsSync10 } from "fs";
2631
+ import { appendFile as appendFile4, copyFile as copyFile2, mkdir as mkdir18, readFile as readFile19, rename as rename3, rm as rm3 } from "fs/promises";
2632
+ import { join as join28 } from "path";
2828
2633
  import { randomUUID as randomUUID9 } from "crypto";
2829
2634
 
2830
2635
  // ../shared/src/workspace-chat.ts
@@ -2957,9 +2762,9 @@ import {
2957
2762
  query
2958
2763
  } from "@anthropic-ai/claude-agent-sdk";
2959
2764
  import { randomUUID as randomUUID5 } from "crypto";
2960
- import { dirname as dirname4, join as join17 } from "path";
2765
+ import { dirname as dirname4, join as join15 } from "path";
2961
2766
  import { mkdir as mkdir10 } from "fs/promises";
2962
- import { homedir as homedir12 } from "os";
2767
+ import { homedir as homedir10 } from "os";
2963
2768
 
2964
2769
  // src/utils/linear-converter.ts
2965
2770
  function linearThoughtToResponse(thought) {
@@ -3344,8 +3149,8 @@ function extractPlanFromCodexAspNotification(notification) {
3344
3149
  // src/utils/image-utils.ts
3345
3150
  import { randomUUID as randomUUID4 } from "crypto";
3346
3151
  import { mkdir as mkdir9, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
3347
- import { homedir as homedir11 } from "os";
3348
- import { join as join14 } from "path";
3152
+ import { homedir as homedir9 } from "os";
3153
+ import { join as join12 } from "path";
3349
3154
  function isImageMediaType(value) {
3350
3155
  return IMAGE_MEDIA_TYPES.includes(value);
3351
3156
  }
@@ -3445,14 +3250,14 @@ async function normalizeImages(images) {
3445
3250
  }
3446
3251
  return normalized;
3447
3252
  }
3448
- async function saveNormalizedImagesToTempFiles(images, tempImageDir = join14(homedir11(), ".replicas", "codex", "temp-images")) {
3253
+ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir9(), ".replicas", "codex", "temp-images")) {
3449
3254
  await mkdir9(tempImageDir, { recursive: true });
3450
3255
  const tempPaths = [];
3451
3256
  try {
3452
3257
  for (const image of images) {
3453
3258
  const ext = image.source.media_type.split("/")[1] || "png";
3454
3259
  const filename = `img_${randomUUID4()}.${ext}`;
3455
- const filepath = join14(tempImageDir, filename);
3260
+ const filepath = join12(tempImageDir, filename);
3456
3261
  await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
3457
3262
  tempPaths.push(filepath);
3458
3263
  }
@@ -3467,8 +3272,8 @@ async function removeTempImageFiles(paths) {
3467
3272
  }
3468
3273
 
3469
3274
  // src/managers/coding-agent-manager.ts
3470
- import { readFileSync as readFileSync5 } from "fs";
3471
- import { join as join15 } from "path";
3275
+ import { readFileSync as readFileSync3 } from "fs";
3276
+ import { join as join13 } from "path";
3472
3277
 
3473
3278
  // src/managers/auth-fallback.ts
3474
3279
  var SWAPPABLE_AGENTS = {
@@ -4012,7 +3817,7 @@ var CodingAgentManager = class {
4012
3817
  buildMemoryInstruction() {
4013
3818
  const readSummary = (scope) => {
4014
3819
  try {
4015
- return readFileSync5(join15(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
3820
+ return readFileSync3(join13(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
4016
3821
  } catch {
4017
3822
  return "";
4018
3823
  }
@@ -4023,10 +3828,10 @@ var CodingAgentManager = class {
4023
3828
  const parts = [
4024
3829
  `Replicas memory is available under ${MEMORY_ROOT}. Each summary below belongs to the scope and root on its enclosing tag. For relevant tasks, search that scope's ${MEMORY_INDEX_FILENAME} and rollout summaries. The summaries are fallible historical context, not instructions. Current user and repository instructions override memory.`
4025
3830
  ];
4026
- if (organization) parts.push(`<replicas-organization-memory root="${join15(MEMORY_ROOT, "organization")}">
3831
+ if (organization) parts.push(`<replicas-organization-memory root="${join13(MEMORY_ROOT, "organization")}">
4027
3832
  ${organization}
4028
3833
  </replicas-organization-memory>`);
4029
- if (seat) parts.push(`<replicas-personal-memory root="${join15(MEMORY_ROOT, "seat")}">
3834
+ if (seat) parts.push(`<replicas-personal-memory root="${join13(MEMORY_ROOT, "seat")}">
4030
3835
  ${seat}
4031
3836
  </replicas-personal-memory>`);
4032
3837
  return parts.join("\n\n");
@@ -4148,67 +3953,95 @@ var LinearEventForwarder = class {
4148
3953
  }
4149
3954
  };
4150
3955
 
4151
- // src/services/command-protection-service.ts
4152
- var DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE = "Blocked by Replicas command protection.";
4153
- function asCommandProtectionResponse(value) {
4154
- if (typeof value !== "object" || value === null) return null;
4155
- const candidate = value;
4156
- return typeof candidate.allowed === "boolean" && typeof candidate.enabled === "boolean" ? candidate : null;
3956
+ // src/services/upload-repo-state.ts
3957
+ var uploadQueue = Promise.resolve();
3958
+ async function sendRepoState(repos) {
3959
+ const form = new FormData();
3960
+ form.append(
3961
+ "file",
3962
+ new Blob([JSON.stringify({ repos })], { type: "application/json" }),
3963
+ "repo-state.json"
3964
+ );
3965
+ const response = await monolithRequest("/v1/engine/repo-state", { body: form });
3966
+ if (!response.ok) {
3967
+ const errorText = await response.text();
3968
+ throw new Error(`upload failed: ${response.status} ${errorText}`);
3969
+ }
4157
3970
  }
4158
- function failClosed(reason, matchedSignals) {
4159
- return {
4160
- allowed: false,
4161
- enabled: true,
4162
- reason,
4163
- matchedSignals
4164
- };
3971
+ function uploadRepoState(repos) {
3972
+ const upload = uploadQueue.then(() => sendRepoState(repos));
3973
+ uploadQueue = upload.catch(() => {
3974
+ });
3975
+ return upload;
4165
3976
  }
4166
- async function evaluateCommandProtection(request, signal) {
4167
- const matchedSignals = [...findGitCommitSignals(request), ...findPrMergeSignals(request)];
4168
- if (matchedSignals.length === 0) {
4169
- return { allowed: true, enabled: true };
4170
- }
4171
- let response;
3977
+ async function flushRepoState() {
4172
3978
  try {
4173
- response = await monolithRequest("/v1/engine/command-protection/evaluate", {
4174
- body: request,
4175
- signal
3979
+ const { repos, complete } = await gitService.listReposWithCompleteness({ includeDiffs: true });
3980
+ if (!complete) return { flushed: 0, skipped: 1, failed: 0 };
3981
+ await uploadRepoState(repos);
3982
+ return { flushed: 1, skipped: 0, failed: 0 };
3983
+ } catch (err) {
3984
+ console.error("[RepoStateUploader] upload failed:", err);
3985
+ return { flushed: 0, skipped: 0, failed: 1 };
3986
+ }
3987
+ }
3988
+
3989
+ // src/services/refresh-repo-state.ts
3990
+ async function refreshRepoStatuses(observedBranchesByRepo, includeDiffs = false) {
3991
+ await Promise.all([
3992
+ githubTokenManager.refreshOnce().catch(() => {
3993
+ }),
3994
+ gitlabTokenManager.refreshOnce().catch(() => {
3995
+ })
3996
+ ]);
3997
+ const { repos, complete } = await gitService.refreshReposWithCompleteness(
3998
+ observedBranchesByRepo,
3999
+ { includeDiffs }
4000
+ );
4001
+ if (complete) {
4002
+ uploadRepoState(repos).catch((error) => {
4003
+ console.error("[RefreshRepoState] Failed to upload repository state:", error);
4176
4004
  });
4177
- } catch (error) {
4178
- const detail = error instanceof Error ? error.message : String(error);
4179
- return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
4180
4005
  }
4181
- if (!response.ok) {
4182
- const detail = await response.text().catch(() => "");
4183
- return failClosed(`Command protection failed closed: ${response.status} ${detail}`, matchedSignals);
4006
+ return repos;
4007
+ }
4008
+
4009
+ // src/services/pull-request-check-service.ts
4010
+ var CHECK_TIMEOUT_MS = 3e4;
4011
+ var PullRequestCheckService = class {
4012
+ running = null;
4013
+ trailing = null;
4014
+ // Plain single-flight would hand a caller a result gathered before its PR
4015
+ // existed, so callers arriving mid-run instead share one trailing run that
4016
+ // starts after the current one settles.
4017
+ check() {
4018
+ const running = this.running;
4019
+ if (!running) return this.start();
4020
+ this.trailing ??= running.catch(() => {
4021
+ }).then(() => {
4022
+ this.trailing = null;
4023
+ return this.start();
4024
+ });
4025
+ return this.trailing;
4184
4026
  }
4185
- let payload;
4186
- try {
4187
- payload = await response.json();
4188
- } catch (error) {
4189
- const detail = error instanceof Error ? error.message : String(error);
4190
- return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
4027
+ start() {
4028
+ const run = withTimeout(this.refresh(), CHECK_TIMEOUT_MS, "Pull request check timed out").finally(() => {
4029
+ if (this.running === run) this.running = null;
4030
+ });
4031
+ this.running = run;
4032
+ return run;
4191
4033
  }
4192
- return asCommandProtectionResponse(payload) ?? failClosed("Command protection failed closed: invalid response from policy service", matchedSignals);
4193
- }
4194
- function extractToolCommand(input) {
4195
- return extractCommandProtectionCommandText({ toolInput: input }, { stringifyFallback: false });
4196
- }
4197
- function reportCommandProtectionBlock(options) {
4198
- const message = options.result.reason || DEFAULT_COMMAND_PROTECTION_BLOCK_MESSAGE;
4199
- console.warn(`[${options.managerName}] blocked ${options.action} by command protection: ${message}`);
4200
- options.recordHistoryEvent(options.eventType, {
4201
- type: "error",
4202
- message,
4203
- ...options.context,
4204
- command: options.command
4205
- }, options.historyFile);
4206
- return message;
4207
- }
4034
+ async refresh() {
4035
+ const repoStatuses = await refreshRepoStatuses();
4036
+ await monolithService.sendEvent({ type: "pull_request_check", payload: { repoStatuses } });
4037
+ return repoStatuses;
4038
+ }
4039
+ };
4040
+ var pullRequestCheckService = new PullRequestCheckService();
4208
4041
 
4209
4042
  // src/services/skill-registry-service.ts
4210
4043
  import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
4211
- import { dirname as dirname3, isAbsolute, join as join16, relative, resolve } from "path";
4044
+ import { dirname as dirname3, isAbsolute, join as join14, relative, resolve } from "path";
4212
4045
  var REGISTRY_ROOT_DIR = ".replicas/skill-registries";
4213
4046
  var REGISTRY_MANIFEST = "manifest.json";
4214
4047
  async function getSkillRegistryInventory(homeDir) {
@@ -4269,10 +4102,10 @@ async function scanRegistry(registryDir) {
4269
4102
  }
4270
4103
  async function findSkillCollections(registryDir) {
4271
4104
  const candidateRoots = [
4272
- { source: "skills", root: join16(registryDir, "skills") },
4273
- { source: "agents", root: join16(registryDir, ".agents", "skills") },
4274
- { source: "codex", root: join16(registryDir, ".codex", "skills") },
4275
- { source: "claude", root: join16(registryDir, ".claude", "skills") }
4105
+ { source: "skills", root: join14(registryDir, "skills") },
4106
+ { source: "agents", root: join14(registryDir, ".agents", "skills") },
4107
+ { source: "codex", root: join14(registryDir, ".codex", "skills") },
4108
+ { source: "claude", root: join14(registryDir, ".claude", "skills") }
4276
4109
  ];
4277
4110
  const collections = [];
4278
4111
  for (const { source, root } of candidateRoots) {
@@ -4286,9 +4119,9 @@ async function findSkillCollections(registryDir) {
4286
4119
  async function findSkillDirsInRoot(root) {
4287
4120
  const skillDirs = [];
4288
4121
  for (const dirent of await safeReadDir(root)) {
4289
- const skillDir = join16(root, dirent.name);
4122
+ const skillDir = join14(root, dirent.name);
4290
4123
  if (!await isDirectoryDirent(dirent, skillDir)) continue;
4291
- if (await fileExists(join16(skillDir, "SKILL.md"))) {
4124
+ if (await fileExists(join14(skillDir, "SKILL.md"))) {
4292
4125
  skillDirs.push(skillDir);
4293
4126
  }
4294
4127
  }
@@ -4298,9 +4131,9 @@ async function findTopLevelSkills(registryDir) {
4298
4131
  const skills = [];
4299
4132
  for (const dirent of await safeReadDir(registryDir)) {
4300
4133
  if (dirent.name.startsWith(".") || dirent.name === "skills" || dirent.name === "plugins") continue;
4301
- const skillDir = join16(registryDir, dirent.name);
4134
+ const skillDir = join14(registryDir, dirent.name);
4302
4135
  if (!await isDirectoryDirent(dirent, skillDir)) continue;
4303
- if (await fileExists(join16(skillDir, "SKILL.md"))) {
4136
+ if (await fileExists(join14(skillDir, "SKILL.md"))) {
4304
4137
  skills.push({ source: "root", skillDir });
4305
4138
  }
4306
4139
  }
@@ -4311,10 +4144,10 @@ async function findClaudePluginRoots(registryDir) {
4311
4144
  async function walk(dir) {
4312
4145
  for (const dirent of await safeReadDir(dir)) {
4313
4146
  if (dirent.name === ".git") continue;
4314
- const child = join16(dir, dirent.name);
4147
+ const child = join14(dir, dirent.name);
4315
4148
  if (!await isDirectoryDirent(dirent, child)) continue;
4316
4149
  if (dirent.name === ".claude-plugin") {
4317
- if (await fileExists(join16(child, "plugin.json"))) {
4150
+ if (await fileExists(join14(child, "plugin.json"))) {
4318
4151
  roots.push(dirname3(child));
4319
4152
  }
4320
4153
  continue;
@@ -4326,7 +4159,7 @@ async function findClaudePluginRoots(registryDir) {
4326
4159
  return uniqueStrings(roots);
4327
4160
  }
4328
4161
  async function hasCodexMarketplace(registryDir) {
4329
- return await fileExists(join16(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join16(registryDir, ".codex", "plugins", "marketplace.json"));
4162
+ return await fileExists(join14(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join14(registryDir, ".codex", "plugins", "marketplace.json"));
4330
4163
  }
4331
4164
  async function installCodexRegistryPlugins(client2, inventory) {
4332
4165
  const cwds = inventory.codexMarketplaceCwds;
@@ -4368,10 +4201,10 @@ async function readManifest(homeDir) {
4368
4201
  }
4369
4202
  }
4370
4203
  function getRegistryRoot(homeDir) {
4371
- return join16(homeDir, REGISTRY_ROOT_DIR);
4204
+ return join14(homeDir, REGISTRY_ROOT_DIR);
4372
4205
  }
4373
4206
  function getManifestPath(homeDir) {
4374
- return join16(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
4207
+ return join14(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
4375
4208
  }
4376
4209
  function emptyInventory(registryRoot) {
4377
4210
  return {
@@ -4819,7 +4652,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
4819
4652
  authRetrying = false;
4820
4653
  constructor(options) {
4821
4654
  super({ ...options, provider: AGENT.CLAUDE });
4822
- this.historyFilePath = options.historyFilePath ?? join17(homedir12(), ".replicas", "claude", "history.jsonl");
4655
+ this.historyFilePath = options.historyFilePath ?? join15(homedir10(), ".replicas", "claude", "history.jsonl");
4823
4656
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
4824
4657
  this.systemPromptOverride = options.systemPromptOverride;
4825
4658
  this.toolsOverride = options.tools;
@@ -5409,6 +5242,17 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5409
5242
  matcher: "*",
5410
5243
  hooks: [this.buildCommandProtectionHook()],
5411
5244
  timeout: 30
5245
+ }],
5246
+ PostToolUse: [{
5247
+ matcher: "*",
5248
+ hooks: [async (input) => {
5249
+ if (input.hook_event_name === "PostToolUse" && mayCreatePullRequest(input.tool_name, input.tool_input)) {
5250
+ pullRequestCheckService.check().catch(() => {
5251
+ });
5252
+ }
5253
+ return {};
5254
+ }],
5255
+ timeout: 30
5412
5256
  }]
5413
5257
  }
5414
5258
  }
@@ -5873,7 +5717,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5873
5717
 
5874
5718
  // src/managers/codex-asp/codex-asp-manager.ts
5875
5719
  import { readdir as readdir4 } from "fs/promises";
5876
- import { join as join19 } from "path";
5720
+ import { join as join17 } from "path";
5877
5721
 
5878
5722
  // src/managers/codex-asp/asp-host.ts
5879
5723
  var hostPromise = null;
@@ -5988,7 +5832,7 @@ var CodexQuotaStatusTracker = class {
5988
5832
  };
5989
5833
 
5990
5834
  // src/managers/codex-asp/mappers.ts
5991
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
5835
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
5992
5836
  var localImageCache = /* @__PURE__ */ new Map();
5993
5837
  var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
5994
5838
  var THREAD_START_METHOD = "thread/start";
@@ -6116,7 +5960,7 @@ function userImageForLocalPath(path6) {
6116
5960
  const image = {
6117
5961
  type: "image",
6118
5962
  mediaType: inferMediaType(path6),
6119
- data: readFileSync6(path6).toString("base64")
5963
+ data: readFileSync4(path6).toString("base64")
6120
5964
  };
6121
5965
  if (image.data.length > 0) localImageCache.set(path6, image);
6122
5966
  return image;
@@ -6607,19 +6451,19 @@ var TranscriptUpdateCoalescer = class {
6607
6451
  };
6608
6452
 
6609
6453
  // src/services/chat/history-paths.ts
6610
- import { homedir as homedir13 } from "os";
6611
- import { join as join18 } from "path";
6612
- var ENGINE_DIR2 = join18(homedir13(), ".replicas", "engine");
6613
- var CHATS_FILE = join18(ENGINE_DIR2, "chats.json");
6614
- var CLAUDE_HISTORY_DIR = join18(ENGINE_DIR2, "claude-histories");
6615
- var RELAY_HISTORY_DIR = join18(ENGINE_DIR2, "relay-histories");
6616
- var CODEX_HISTORY_DIR = join18(ENGINE_DIR2, "codex-histories");
6617
- var CURSOR_HISTORY_DIR = join18(ENGINE_DIR2, "cursor-histories");
6618
- var OPENCODE_HISTORY_DIR = join18(ENGINE_DIR2, "opencode-histories");
6619
- var PI_HISTORY_DIR = join18(ENGINE_DIR2, "pi-histories");
6620
- var DEEPSEEK_HISTORY_DIR = join18(ENGINE_DIR2, "deepseek-histories");
6621
- var FX_HISTORY_DIR = join18(ENGINE_DIR2, "fx-histories");
6622
- var KIMI_HISTORY_DIR = join18(ENGINE_DIR2, "kimi-histories");
6454
+ import { homedir as homedir11 } from "os";
6455
+ import { join as join16 } from "path";
6456
+ var ENGINE_DIR2 = join16(homedir11(), ".replicas", "engine");
6457
+ var CHATS_FILE = join16(ENGINE_DIR2, "chats.json");
6458
+ var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR2, "claude-histories");
6459
+ var RELAY_HISTORY_DIR = join16(ENGINE_DIR2, "relay-histories");
6460
+ var CODEX_HISTORY_DIR = join16(ENGINE_DIR2, "codex-histories");
6461
+ var CURSOR_HISTORY_DIR = join16(ENGINE_DIR2, "cursor-histories");
6462
+ var OPENCODE_HISTORY_DIR = join16(ENGINE_DIR2, "opencode-histories");
6463
+ var PI_HISTORY_DIR = join16(ENGINE_DIR2, "pi-histories");
6464
+ var DEEPSEEK_HISTORY_DIR = join16(ENGINE_DIR2, "deepseek-histories");
6465
+ var FX_HISTORY_DIR = join16(ENGINE_DIR2, "fx-histories");
6466
+ var KIMI_HISTORY_DIR = join16(ENGINE_DIR2, "kimi-histories");
6623
6467
  var HISTORY_DIR_BY_PROVIDER = {
6624
6468
  claude: CLAUDE_HISTORY_DIR,
6625
6469
  relay: RELAY_HISTORY_DIR,
@@ -6685,7 +6529,7 @@ async function readCodexAspThreadHistory(threadId) {
6685
6529
  }
6686
6530
  for (const entry of entries) {
6687
6531
  if (!entry.isFile() || !entry.name.endsWith(".jsonl") || entry.name.endsWith(".pages.jsonl")) continue;
6688
- const history = await new CodexHistoryFile(join19(CODEX_HISTORY_DIR, entry.name)).load();
6532
+ const history = await new CodexHistoryFile(join17(CODEX_HISTORY_DIR, entry.name)).load();
6689
6533
  const transcript = history.transcriptsByThreadId.get(threadId);
6690
6534
  if (transcript) {
6691
6535
  return {
@@ -7358,6 +7202,11 @@ var CodexAspManager = class extends CodingAgentManager {
7358
7202
  );
7359
7203
  this.emitTranscriptUpdated(notification.params.threadId, { immediate: true });
7360
7204
  linearForwarder.sendEvent(convertCodexAspNotification(notification, linearSessionId ?? ""));
7205
+ const completedItem = notification.params.item;
7206
+ if (completedItem.type === "commandExecution" && completedItem.exitCode === 0 && mayCreatePullRequest("commandExecution", { command: completedItem.command }) || completedItem.type === "mcpToolCall" && completedItem.status === "completed" && mayCreatePullRequest(completedItem.tool, completedItem.arguments) || completedItem.type === "dynamicToolCall" && completedItem.success !== false && mayCreatePullRequest(completedItem.tool, completedItem.arguments)) {
7207
+ pullRequestCheckService.check().catch(() => {
7208
+ });
7209
+ }
7361
7210
  },
7362
7211
  [AGENT_MESSAGE_DELTA_METHOD]: (notification) => {
7363
7212
  if (!matchesTurn(notification.params.threadId, notification.params.turnId)) return;
@@ -8065,10 +7914,98 @@ var CodexAspManager = class extends CodingAgentManager {
8065
7914
  };
8066
7915
 
8067
7916
  // src/managers/cursor-manager.ts
8068
- import { mkdir as mkdir11, readFile as readFile10, readdir as readdir5 } from "fs/promises";
8069
- import { basename, dirname as dirname5, extname, join as join20 } from "path";
7917
+ import { mkdir as mkdir12, readFile as readFile11, readdir as readdir5 } from "fs/promises";
7918
+ import { basename as basename2, dirname as dirname6, extname, join as join19 } from "path";
8070
7919
  import { parse as parseYaml } from "yaml";
8071
7920
  import { Agent as CursorAgent } from "@cursor/sdk";
7921
+
7922
+ // src/services/native-command-protection-hooks.ts
7923
+ import { existsSync as existsSync6 } from "fs";
7924
+ import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
7925
+ import { basename, dirname as dirname5, join as join18 } from "path";
7926
+ import { fileURLToPath } from "url";
7927
+ var moduleDirectory = dirname5(fileURLToPath(import.meta.url));
7928
+ var hookPath = [
7929
+ join18(moduleDirectory, "command-protection-hook.js"),
7930
+ join18(moduleDirectory, "..", "command-protection-hook.ts")
7931
+ ].find(existsSync6);
7932
+ var postToolHookPath = [
7933
+ join18(moduleDirectory, "post-tool-pr-hook.js"),
7934
+ join18(moduleDirectory, "..", "post-tool-pr-hook.ts")
7935
+ ].find(existsSync6);
7936
+ function shellCommand(path6, provider) {
7937
+ if (!path6) throw new Error("Replicas hook executable is missing.");
7938
+ const args = [
7939
+ process.execPath,
7940
+ ...path6.endsWith(".ts") && basename(process.execPath) !== "bun" ? ["--import", "tsx"] : [],
7941
+ path6,
7942
+ ...provider ? [provider] : []
7943
+ ];
7944
+ return args.map((value) => `'${value.replaceAll("'", "'\\''")}'`).join(" ");
7945
+ }
7946
+ function getCommandProtectionHookPath() {
7947
+ if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
7948
+ return hookPath;
7949
+ }
7950
+ function getPostToolPrHookPath() {
7951
+ if (!postToolHookPath) throw new Error("Replicas post-tool PR hook executable is missing.");
7952
+ return postToolHookPath;
7953
+ }
7954
+ async function ensureCursorCommandProtectionHook() {
7955
+ const path6 = join18(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
7956
+ let config = {};
7957
+ try {
7958
+ const parsed = JSON.parse(await readFile10(path6, "utf8"));
7959
+ if (!isRecord2(parsed)) throw new Error("Invalid Cursor hooks configuration.");
7960
+ config = parsed;
7961
+ } catch (error) {
7962
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
7963
+ }
7964
+ const hooks = isRecord2(config.hooks) ? config.hooks : {};
7965
+ const existing = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
7966
+ hooks.preToolUse = [
7967
+ ...existing.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("command-protection-hook")),
7968
+ { command: shellCommand(hookPath, "cursor") }
7969
+ ];
7970
+ const existingPost = Array.isArray(hooks.postToolUse) ? hooks.postToolUse : [];
7971
+ hooks.postToolUse = [
7972
+ ...existingPost.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("post-tool-pr-hook")),
7973
+ { command: shellCommand(postToolHookPath) }
7974
+ ];
7975
+ await mkdir11(dirname5(path6), { recursive: true });
7976
+ await writeFile6(path6, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
7977
+ `, "utf8");
7978
+ }
7979
+ async function ensureKimiCommandProtectionHook() {
7980
+ const path6 = join18(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
7981
+ const start = "# Replicas command protection hook";
7982
+ const end = "# End Replicas command protection hook";
7983
+ let config = "";
7984
+ try {
7985
+ config = await readFile10(path6, "utf8");
7986
+ } catch (error) {
7987
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
7988
+ }
7989
+ const block = `${start}
7990
+ [[hooks]]
7991
+ event = "PreToolUse"
7992
+ command = ${JSON.stringify(shellCommand(hookPath, "kimi"))}
7993
+ timeout = 30
7994
+
7995
+ [[hooks]]
7996
+ event = "PostToolUse"
7997
+ command = ${JSON.stringify(shellCommand(postToolHookPath))}
7998
+ timeout = 30
7999
+ ${end}`;
8000
+ const markerStart = config.indexOf(start);
8001
+ const markerEnd = config.indexOf(end, markerStart + start.length);
8002
+ config = markerStart >= 0 && markerEnd >= 0 ? `${config.slice(0, markerStart)}${block}${config.slice(markerEnd + end.length)}` : `${config.trimEnd()}${config.trim() ? "\n\n" : ""}${block}
8003
+ `;
8004
+ await mkdir11(dirname5(path6), { recursive: true });
8005
+ await writeFile6(path6, config, "utf8");
8006
+ }
8007
+
8008
+ // src/managers/cursor-manager.ts
8072
8009
  var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
8073
8010
  var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
8074
8011
  var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
@@ -8108,24 +8045,6 @@ function extractCursorCommandDescription(content) {
8108
8045
  }
8109
8046
  return void 0;
8110
8047
  }
8111
- function cursorToolInputFromDelta(update) {
8112
- if (update.type !== "tool-call-started") return null;
8113
- const toolCall = update.toolCall;
8114
- return {
8115
- toolName: toolCall.type,
8116
- toolInput: toolCall,
8117
- command: extractToolCommand(toolCall),
8118
- toolUseId: update.callId
8119
- };
8120
- }
8121
- function cursorToolInputFromMessage(event) {
8122
- return {
8123
- toolName: event.name,
8124
- toolInput: event.args ?? {},
8125
- command: extractToolCommand(event.args),
8126
- toolUseId: event.call_id
8127
- };
8128
- }
8129
8048
  async function listCursorCommandsInDirectory(directory) {
8130
8049
  let entries;
8131
8050
  try {
@@ -8136,10 +8055,10 @@ async function listCursorCommandsInDirectory(directory) {
8136
8055
  return [];
8137
8056
  }
8138
8057
  const commands = await Promise.all(entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map(async (entry) => {
8139
- const name = basename(entry.name, ".md");
8058
+ const name = basename2(entry.name, ".md");
8140
8059
  let description;
8141
8060
  try {
8142
- description = extractCursorCommandDescription(await readFile10(join20(directory, entry.name), "utf8"));
8061
+ description = extractCursorCommandDescription(await readFile11(join19(directory, entry.name), "utf8"));
8143
8062
  } catch (error) {
8144
8063
  console.warn("[CursorManager] Failed to read slash command file:", error);
8145
8064
  }
@@ -8152,8 +8071,6 @@ var CursorManager = class extends CodingAgentManager {
8152
8071
  activeRun = null;
8153
8072
  activeModel = null;
8154
8073
  abortActiveTurn = null;
8155
- blockedToolCallIds = /* @__PURE__ */ new Set();
8156
- pendingCursorBlockReason = null;
8157
8074
  reconnectNoticeVisible = false;
8158
8075
  instructionsDelivered = false;
8159
8076
  resumeSessionId = null;
@@ -8163,12 +8080,13 @@ var CursorManager = class extends CodingAgentManager {
8163
8080
  slashCommandsRequest = null;
8164
8081
  constructor(options) {
8165
8082
  super(options);
8166
- this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
8083
+ this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
8167
8084
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
8168
8085
  this.initializeManager(this.processMessageInternal.bind(this));
8169
8086
  }
8170
8087
  async initialize() {
8171
- await mkdir11(dirname5(this.historyFilePath), { recursive: true });
8088
+ await mkdir12(dirname6(this.historyFilePath), { recursive: true });
8089
+ await ensureCursorCommandProtectionHook();
8172
8090
  }
8173
8091
  getHistorySink() {
8174
8092
  return this.historyFile;
@@ -8237,7 +8155,7 @@ var CursorManager = class extends CodingAgentManager {
8237
8155
  this.slashCommandsRequest ??= (async () => {
8238
8156
  try {
8239
8157
  const repoDirectories = await getAgentAdditionalDirectories();
8240
- const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join20(directory, ".cursor", "commands"));
8158
+ const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join19(directory, ".cursor", "commands"));
8241
8159
  const commands = mergeSlashCommands(
8242
8160
  ...await Promise.all(commandDirectories.map(listCursorCommandsInDirectory))
8243
8161
  );
@@ -8308,17 +8226,12 @@ var CursorManager = class extends CodingAgentManager {
8308
8226
  run = await agent.send(message, {
8309
8227
  model: { id: model },
8310
8228
  mode: request.planMode ? "plan" : "agent",
8311
- onDelta: async (args) => {
8229
+ onDelta: (args) => {
8312
8230
  markCursorActivity();
8313
8231
  this.recordCursorContextUsage(args.update);
8314
- await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
8315
8232
  }
8316
8233
  });
8317
8234
  this.activeRun = run;
8318
- if (this.pendingCursorBlockReason) {
8319
- await run.cancel();
8320
- throw new Error(this.pendingCursorBlockReason);
8321
- }
8322
8235
  const events = run.stream()[Symbol.asyncIterator]();
8323
8236
  let interrupted = false;
8324
8237
  while (true) {
@@ -8340,9 +8253,6 @@ var CursorManager = class extends CodingAgentManager {
8340
8253
  markCursorActivity();
8341
8254
  const event = next.value;
8342
8255
  this.recordHistoryEvent(`cursor-${event.type}`, event, this.historyFile);
8343
- if (event.type === "tool_call" && event.status === "running") {
8344
- await this.handleCursorToolCall(cursorToolInputFromMessage(event));
8345
- }
8346
8256
  if (linearSessionId) {
8347
8257
  linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
8348
8258
  }
@@ -8413,8 +8323,6 @@ var CursorManager = class extends CodingAgentManager {
8413
8323
  this.abortActiveTurn = null;
8414
8324
  this.activeRun = null;
8415
8325
  this.activeModel = null;
8416
- this.blockedToolCallIds.clear();
8417
- this.pendingCursorBlockReason = null;
8418
8326
  await this.historyFile.flush();
8419
8327
  await this.onTurnComplete();
8420
8328
  }
@@ -8434,39 +8342,6 @@ var CursorManager = class extends CodingAgentManager {
8434
8342
  return null;
8435
8343
  }
8436
8344
  }
8437
- async handleCursorToolCall(toolCall) {
8438
- if (!toolCall) return;
8439
- if (this.blockedToolCallIds.has(toolCall.toolUseId)) return;
8440
- const result = await evaluateCommandProtection({
8441
- provider: "cursor",
8442
- source: "cursor_tool_call",
8443
- toolName: toolCall.toolName,
8444
- toolInput: toolCall.toolInput,
8445
- command: toolCall.command,
8446
- cwd: this.workingDirectory,
8447
- toolUseId: toolCall.toolUseId
8448
- });
8449
- if (result.allowed) return;
8450
- this.blockedToolCallIds.add(toolCall.toolUseId);
8451
- const message = reportCommandProtectionBlock({
8452
- managerName: "CursorManager",
8453
- action: "tool call",
8454
- eventType: "cursor-command-protection.blocked",
8455
- result,
8456
- command: toolCall.command,
8457
- context: {
8458
- toolName: toolCall.toolName,
8459
- toolUseId: toolCall.toolUseId
8460
- },
8461
- historyFile: this.historyFile,
8462
- recordHistoryEvent: this.recordHistoryEvent.bind(this)
8463
- });
8464
- if (this.activeRun) {
8465
- await this.activeRun.cancel();
8466
- throw new Error(message);
8467
- }
8468
- this.pendingCursorBlockReason = message;
8469
- }
8470
8345
  async toCursorMessage(request, includeInstructions) {
8471
8346
  const instructions = includeInstructions ? this.buildCombinedInstructions(request.customInstructions) : void 0;
8472
8347
  const text = [
@@ -8553,10 +8428,10 @@ ${instructions}
8553
8428
  import { spawn as spawn3 } from "child_process";
8554
8429
  import { randomUUID as randomUUID6 } from "crypto";
8555
8430
  import { createRequire } from "module";
8556
- import { dirname as dirname6, join as join21 } from "path";
8557
- import { fileURLToPath } from "url";
8558
- import { existsSync as existsSync6 } from "fs";
8559
- import { mkdir as mkdir12 } from "fs/promises";
8431
+ import { dirname as dirname7, join as join20 } from "path";
8432
+ import { fileURLToPath as fileURLToPath2 } from "url";
8433
+ import { existsSync as existsSync7 } from "fs";
8434
+ import { mkdir as mkdir13, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
8560
8435
  import { z as z2 } from "zod";
8561
8436
  import WebSocket from "ws";
8562
8437
  import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
@@ -8568,12 +8443,16 @@ var questionSelectionSchema = z2.record(z2.string(), z2.object({
8568
8443
  custom: z2.string().optional()
8569
8444
  }));
8570
8445
  var require2 = createRequire(import.meta.url);
8571
- var DSH_BIN = join21(dirname6(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
8572
- var MANAGER_DIR = dirname6(fileURLToPath(import.meta.url));
8446
+ var DSH_BIN = join20(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
8447
+ var MANAGER_DIR = dirname7(fileURLToPath2(import.meta.url));
8573
8448
  var DSH_PATCH = [
8574
- join21(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
8575
- join21(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
8576
- ].find(existsSync6) ?? "";
8449
+ join20(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
8450
+ join20(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
8451
+ ].find(existsSync7) ?? "";
8452
+ var DSH_COMMAND_PROTECTION_PLUGIN = [
8453
+ join20(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
8454
+ join20(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
8455
+ ].find(existsSync7) ?? "";
8577
8456
  var DeepseekApiClient = class extends AbstractApiClient {
8578
8457
  constructor(baseUrl) {
8579
8458
  super(3e4);
@@ -8686,12 +8565,12 @@ var DeepseekManager = class extends CodingAgentManager {
8686
8565
  constructor(options) {
8687
8566
  super(options);
8688
8567
  this.sessionId = options.initialSessionId ? sessionIdSchema.parse(options.initialSessionId) : null;
8689
- this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
8568
+ this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
8690
8569
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
8691
8570
  this.initializeManager(this.processMessageInternal.bind(this));
8692
8571
  }
8693
8572
  async initialize() {
8694
- await mkdir12(dirname6(this.historyFilePath), { recursive: true });
8573
+ await mkdir13(dirname7(this.historyFilePath), { recursive: true });
8695
8574
  }
8696
8575
  getHistorySink() {
8697
8576
  return this.historyFile;
@@ -8765,15 +8644,32 @@ var DeepseekManager = class extends CodingAgentManager {
8765
8644
  }
8766
8645
  async ensureClient() {
8767
8646
  if (this.client) return this.client;
8768
- if (!DSH_PATCH) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
8647
+ if (!DSH_PATCH || !DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
8648
+ const runtimePatch = join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "replicas.cordis.patch.yml");
8649
+ await mkdir13(dirname7(runtimePatch), { recursive: true });
8650
+ await writeFile7(runtimePatch, (await readFile12(DSH_PATCH, "utf8")).replace(
8651
+ "__REPLICAS_DSH_COMMAND_PROTECTION_PLUGIN__",
8652
+ JSON.stringify(DSH_COMMAND_PROTECTION_PLUGIN)
8653
+ ));
8769
8654
  const apiKey = await getOpenRouterKey();
8770
- const child = spawn3(process.execPath, [DSH_BIN, "--profile", "web", "--patch", DSH_PATCH, "--host", "127.0.0.1", "--port", "0"], {
8655
+ const child = spawn3(process.execPath, [
8656
+ ...DSH_COMMAND_PROTECTION_PLUGIN.endsWith(".ts") ? ["--import", "tsx"] : [],
8657
+ DSH_BIN,
8658
+ "--profile",
8659
+ "web",
8660
+ "--patch",
8661
+ runtimePatch,
8662
+ "--host",
8663
+ "127.0.0.1",
8664
+ "--port",
8665
+ "0"
8666
+ ], {
8771
8667
  cwd: this.workingDirectory,
8772
8668
  env: {
8773
8669
  ...process.env,
8774
8670
  DEEPSEEK_API_KEY: apiKey,
8775
8671
  DEEPSEEK_BASE_URL: "https://openrouter.ai/api/v1",
8776
- DSH_HOME: join21(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
8672
+ DSH_HOME: join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
8777
8673
  DSH_PERMISSION_MODE: "danger-full-access",
8778
8674
  DSH_TELEMETRY_DISABLED: "true"
8779
8675
  },
@@ -8958,14 +8854,14 @@ ${request.message}` : request.message;
8958
8854
  };
8959
8855
 
8960
8856
  // src/managers/fx-manager.ts
8961
- import { readFile as readFile12 } from "fs/promises";
8857
+ import { readFile as readFile14 } from "fs/promises";
8962
8858
  import { spawn as spawn5 } from "child_process";
8963
8859
 
8964
8860
  // src/managers/acp-manager.ts
8965
8861
  import { spawn as spawn4 } from "child_process";
8966
8862
  import { randomUUID as randomUUID7 } from "crypto";
8967
- import { mkdir as mkdir13, readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
8968
- import { dirname as dirname7 } from "path";
8863
+ import { mkdir as mkdir14, readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
8864
+ import { dirname as dirname8 } from "path";
8969
8865
  import { Writable } from "stream";
8970
8866
  import {
8971
8867
  PROTOCOL_VERSION,
@@ -9004,7 +8900,7 @@ var AcpManager = class extends CodingAgentManager {
9004
8900
  this.initializeManager(this.processMessageInternal.bind(this));
9005
8901
  }
9006
8902
  async initialize() {
9007
- await mkdir13(dirname7(this.historyFilePath), { recursive: true });
8903
+ await mkdir14(dirname8(this.historyFilePath), { recursive: true });
9008
8904
  await this.acp.prepare?.();
9009
8905
  }
9010
8906
  getHistorySink() {
@@ -9035,7 +8931,7 @@ var AcpManager = class extends CodingAgentManager {
9035
8931
  });
9036
8932
  const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
9037
8933
  this.assertSession(params.sessionId);
9038
- const content = await readFile11(params.path, "utf8");
8934
+ const content = await readFile13(params.path, "utf8");
9039
8935
  if (params.line === void 0 && params.limit === void 0) return { content };
9040
8936
  const start = Math.max(0, (params.line ?? 1) - 1);
9041
8937
  const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
@@ -9043,7 +8939,7 @@ var AcpManager = class extends CodingAgentManager {
9043
8939
  }).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
9044
8940
  this.assertSession(params.sessionId);
9045
8941
  if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
9046
- await writeFile6(params.path, params.content, "utf8");
8942
+ await writeFile8(params.path, params.content, "utf8");
9047
8943
  return {};
9048
8944
  }).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
9049
8945
  this.assertSession(params.sessionId);
@@ -9410,7 +9306,7 @@ var AcpManager = class extends CodingAgentManager {
9410
9306
 
9411
9307
  // src/managers/fx-manager.ts
9412
9308
  async function ensureResolvableDnsConfig() {
9413
- const contents = await readFile12("/etc/resolv.conf");
9309
+ const contents = await readFile14("/etc/resolv.conf");
9414
9310
  if (contents.length === 0 || contents.at(-1) === 10) return;
9415
9311
  await new Promise((resolve5, reject) => {
9416
9312
  const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
@@ -9477,6 +9373,7 @@ var KimiManager = class extends AcpManager {
9477
9373
  };
9478
9374
  },
9479
9375
  defaultModel: DEFAULT_KIMI_MODEL,
9376
+ prepare: ensureKimiCommandProtectionHook,
9480
9377
  restartOnThinkingChange: true
9481
9378
  }
9482
9379
  });
@@ -9484,11 +9381,11 @@ var KimiManager = class extends AcpManager {
9484
9381
  };
9485
9382
 
9486
9383
  // src/managers/opencode-manager.ts
9487
- import { existsSync as existsSync7 } from "fs";
9488
- import { mkdir as mkdir14, readFile as readFile13 } from "fs/promises";
9489
- import { delimiter, dirname as dirname8, join as join22 } from "path";
9384
+ import { existsSync as existsSync8 } from "fs";
9385
+ import { copyFile, mkdir as mkdir15, readFile as readFile15, rm as rm2 } from "fs/promises";
9386
+ import { delimiter, dirname as dirname9, join as join21 } from "path";
9490
9387
  import { randomBytes as randomBytes2 } from "crypto";
9491
- import { fileURLToPath as fileURLToPath2 } from "url";
9388
+ import { fileURLToPath as fileURLToPath3 } from "url";
9492
9389
  import { Agent } from "undici";
9493
9390
  import { z as z3 } from "zod";
9494
9391
 
@@ -9545,27 +9442,14 @@ async function getAllowedOpenRouterModels() {
9545
9442
  }
9546
9443
 
9547
9444
  // src/managers/opencode-manager.ts
9548
- var OPENCODE_SHIM_DIR = dirname8(fileURLToPath2(new URL("../../scripts/opencode", import.meta.url)));
9549
- var OPENCODE_CONFIG_PATH = join22(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
9445
+ var OPENCODE_SHIM_DIR = dirname9(fileURLToPath3(new URL("../../scripts/opencode", import.meta.url)));
9446
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join21(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
9447
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
9448
+ var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
9449
+ var OPENCODE_CONFIG_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
9550
9450
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
9551
9451
  var OPENCODE_SERVER_STARTUP_TIMEOUT_MS = 3e4;
9552
- var OPENCODE_WORKSPACE_PERMISSION = {
9553
- read: "allow",
9554
- edit: "allow",
9555
- glob: "allow",
9556
- grep: "allow",
9557
- list: "allow",
9558
- bash: "ask",
9559
- task: "allow",
9560
- external_directory: "allow",
9561
- todowrite: "allow",
9562
- question: "allow",
9563
- webfetch: "allow",
9564
- websearch: "allow",
9565
- lsp: "allow",
9566
- doom_loop: "allow",
9567
- skill: "allow"
9568
- };
9452
+ var OPENCODE_WORKSPACE_PERMISSION = "allow";
9569
9453
  var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
9570
9454
  var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
9571
9455
  low: ["low"],
@@ -9581,9 +9465,9 @@ var opencodeAuthSchema = z3.record(z3.string(), z3.object({
9581
9465
  key: z3.string().optional()
9582
9466
  }));
9583
9467
  async function hasOpencodeCredentials(provider) {
9584
- if (!existsSync7(OPENCODE_AUTH_PATH)) return false;
9468
+ if (!existsSync8(OPENCODE_AUTH_PATH)) return false;
9585
9469
  try {
9586
- const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile13(OPENCODE_AUTH_PATH, "utf8")));
9470
+ const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile15(OPENCODE_AUTH_PATH, "utf8")));
9587
9471
  return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
9588
9472
  } catch {
9589
9473
  return false;
@@ -9688,7 +9572,7 @@ function isOpencodeMcpEntry(value) {
9688
9572
  async function readProvisionedOpencodeMcpConfig() {
9689
9573
  let raw;
9690
9574
  try {
9691
- raw = await readFile13(OPENCODE_CONFIG_PATH, "utf8");
9575
+ raw = await readFile15(OPENCODE_CONFIG_PATH, "utf8");
9692
9576
  } catch (error) {
9693
9577
  if (isRecord2(error) && error.code === "ENOENT") return void 0;
9694
9578
  console.error("[OpencodeManager] Failed to read Opencode config:", error);
@@ -9746,27 +9630,6 @@ function opencodeFetch(input, init) {
9746
9630
  const requestInit = { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER };
9747
9631
  return fetch(input, requestInit);
9748
9632
  }
9749
- function parseOpencodeToolInputText(text) {
9750
- try {
9751
- return JSON.parse(text);
9752
- } catch {
9753
- return { command: text };
9754
- }
9755
- }
9756
- function opencodePermissionCommand(payload, toolInput) {
9757
- const toolCommand = extractToolCommand(toolInput);
9758
- if (toolCommand) return toolCommand;
9759
- const payloadCommand = extractToolCommand(payload);
9760
- if (payloadCommand) return payloadCommand;
9761
- const resources = Array.isArray(payload.resources) ? payload.resources.filter((item) => typeof item === "string") : [];
9762
- if (resources.length > 0) return resources.join(" ");
9763
- const metadata = isRecord2(payload.metadata) ? payload.metadata : null;
9764
- for (const key of ["command", "cmd", "pattern", "resource"]) {
9765
- const value = metadata?.[key];
9766
- if (typeof value === "string" && value.trim()) return value;
9767
- }
9768
- return null;
9769
- }
9770
9633
  var OpencodeManager = class extends CodingAgentManager {
9771
9634
  client = null;
9772
9635
  server = null;
@@ -9787,22 +9650,17 @@ var OpencodeManager = class extends CodingAgentManager {
9787
9650
  forwardedLinearPartKeys = /* @__PURE__ */ new Set();
9788
9651
  handledAccountRateLimit = false;
9789
9652
  modelVariants = /* @__PURE__ */ new Map();
9790
- handledPermissionRequestIds = /* @__PURE__ */ new Set();
9791
- pendingPermissionRequestIds = /* @__PURE__ */ new Set();
9792
- handledToolCallIds = /* @__PURE__ */ new Set();
9793
- pendingToolCallIds = /* @__PURE__ */ new Set();
9794
- opencodeToolInputs = /* @__PURE__ */ new Map();
9795
9653
  slashCommandsCache = null;
9796
9654
  slashCommandsRequest = null;
9797
9655
  constructor(options) {
9798
9656
  super(options);
9799
9657
  this.sessionId = options.initialSessionId;
9800
- this.historyFilePath = options.historyFilePath ?? join22(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
9658
+ this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
9801
9659
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
9802
9660
  this.initializeManager(this.processMessageInternal.bind(this));
9803
9661
  }
9804
9662
  async initialize() {
9805
- await mkdir14(dirname8(this.historyFilePath), { recursive: true });
9663
+ await mkdir15(dirname9(this.historyFilePath), { recursive: true });
9806
9664
  }
9807
9665
  getHistorySink() {
9808
9666
  return this.historyFile;
@@ -9899,6 +9757,15 @@ var OpencodeManager = class extends CodingAgentManager {
9899
9757
  if (!await hasOpencodeCredentials(providerId)) {
9900
9758
  throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
9901
9759
  }
9760
+ if (!existsSync8(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE)) {
9761
+ throw new Error("Opencode command protection plugin is missing from the engine package.");
9762
+ }
9763
+ await mkdir15(dirname9(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
9764
+ await copyFile(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE, OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH);
9765
+ await rm2(OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH, { force: true });
9766
+ process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
9767
+ process.env.REPLICAS_POST_TOOL_PR_HOOK_PATH = getPostToolPrHookPath();
9768
+ process.env.REPLICAS_COMMAND_PROTECTION_RUNTIME_PATH = process.execPath;
9902
9769
  this.eventAbortController?.abort();
9903
9770
  this.server?.close();
9904
9771
  const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
@@ -10032,11 +9899,6 @@ var OpencodeManager = class extends CodingAgentManager {
10032
9899
  this.activeAbortController = null;
10033
9900
  this.activeLinearForwarder = null;
10034
9901
  this.forwardedLinearPartKeys.clear();
10035
- this.handledPermissionRequestIds.clear();
10036
- this.pendingPermissionRequestIds.clear();
10037
- this.handledToolCallIds.clear();
10038
- this.pendingToolCallIds.clear();
10039
- this.opencodeToolInputs.clear();
10040
9902
  await this.historyFile.flush();
10041
9903
  await this.onTurnComplete();
10042
9904
  }
@@ -10074,9 +9936,6 @@ var OpencodeManager = class extends CodingAgentManager {
10074
9936
  if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
10075
9937
  if (event.type === "message.part.updated" && isOpencodePart(part)) {
10076
9938
  if (this.shouldRecordOpencodePart(part)) this.recordOpencodePart(part);
10077
- this.handleOpencodePartProtection(part).catch((error) => {
10078
- console.error("[OpencodeManager] Failed to evaluate Opencode tool part:", error);
10079
- });
10080
9939
  return;
10081
9940
  }
10082
9941
  if (event.type === "message.updated") this.recordOpencodeMessageRole(payload);
@@ -10085,22 +9944,6 @@ var OpencodeManager = class extends CodingAgentManager {
10085
9944
  console.error("[OpencodeManager] Failed to stop account-rate-limited turn:", error);
10086
9945
  });
10087
9946
  }
10088
- if (event.type === "session.next.tool.input.ended") {
10089
- const callId = typeof payload.callID === "string" ? payload.callID : void 0;
10090
- if (callId && typeof payload.text === "string") {
10091
- this.opencodeToolInputs.set(callId, parseOpencodeToolInputText(payload.text));
10092
- }
10093
- }
10094
- if (event.type === "permission.v2.asked" || event.type === "permission.asked") {
10095
- this.respondToOpencodePermission(event.type, payload).catch((error) => {
10096
- console.error("[OpencodeManager] Failed to respond to Opencode permission:", error);
10097
- });
10098
- }
10099
- if (event.type === "session.next.tool.called") {
10100
- this.handleOpencodeToolCallProtection(payload).catch((error) => {
10101
- console.error("[OpencodeManager] Failed to evaluate Opencode tool call:", error);
10102
- });
10103
- }
10104
9947
  if (this.recordOpencodeNextPart(event.type, payload)) return;
10105
9948
  this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
10106
9949
  }
@@ -10113,110 +9956,6 @@ var OpencodeManager = class extends CodingAgentManager {
10113
9956
  this.recordHistoryEvent("opencode-error", { message, code: "account_rate_limit" }, this.historyFile);
10114
9957
  await this.abortActiveTurn();
10115
9958
  }
10116
- async respondToOpencodePermission(type, payload) {
10117
- if (!this.client) return;
10118
- const requestId = typeof payload.id === "string" ? payload.id : void 0;
10119
- const sessionId = typeof payload.sessionID === "string" ? payload.sessionID : this.sessionId ?? void 0;
10120
- if (!requestId || !sessionId || this.handledPermissionRequestIds.has(requestId) || this.pendingPermissionRequestIds.has(requestId)) return;
10121
- this.pendingPermissionRequestIds.add(requestId);
10122
- try {
10123
- const source = isRecord2(payload.source) ? payload.source : null;
10124
- const callId = typeof source?.callID === "string" ? source.callID : isRecord2(payload.tool) && typeof payload.tool.callID === "string" ? payload.tool.callID : void 0;
10125
- const toolInput = callId ? this.opencodeToolInputs.get(callId) ?? payload : payload;
10126
- const command = opencodePermissionCommand(payload, toolInput);
10127
- const result = await evaluateCommandProtection({
10128
- provider: "opencode",
10129
- source: "opencode_permission_request",
10130
- toolName: typeof payload.action === "string" ? payload.action : typeof payload.permission === "string" ? payload.permission : "permission",
10131
- toolInput,
10132
- command,
10133
- cwd: this.workingDirectory,
10134
- toolUseId: requestId
10135
- });
10136
- const reply = result.allowed ? "once" : "reject";
10137
- const message = result.allowed ? void 0 : reportCommandProtectionBlock({
10138
- managerName: "OpencodeManager",
10139
- action: "permission",
10140
- eventType: "opencode-command-protection.blocked",
10141
- result,
10142
- command,
10143
- context: {
10144
- requestId
10145
- },
10146
- historyFile: this.historyFile,
10147
- recordHistoryEvent: this.recordHistoryEvent.bind(this)
10148
- });
10149
- if (type === "permission.v2.asked") {
10150
- await this.client.v2.session.permission.reply({
10151
- sessionID: sessionId,
10152
- requestID: requestId,
10153
- reply,
10154
- ...message ? { message } : {}
10155
- }, { throwOnError: true });
10156
- } else {
10157
- await this.client.permission.reply({
10158
- requestID: requestId,
10159
- directory: this.workingDirectory,
10160
- reply,
10161
- ...message ? { message } : {}
10162
- }, { throwOnError: true });
10163
- }
10164
- this.handledPermissionRequestIds.add(requestId);
10165
- } finally {
10166
- this.pendingPermissionRequestIds.delete(requestId);
10167
- }
10168
- }
10169
- async handleOpencodeToolCallProtection(payload) {
10170
- const callId = typeof payload.callID === "string" ? payload.callID : void 0;
10171
- if (!callId || this.handledToolCallIds.has(callId)) return;
10172
- const toolName = typeof payload.tool === "string" ? payload.tool : "tool";
10173
- const input = isRecord2(payload.input) ? payload.input : {};
10174
- this.opencodeToolInputs.set(callId, input);
10175
- const command = extractToolCommand(input);
10176
- await this.evaluateOpencodeToolProtection(toolName, input, command, callId);
10177
- }
10178
- async handleOpencodePartProtection(part) {
10179
- if (part.type !== "tool") return;
10180
- const state = part.state;
10181
- if (state.status !== "running" && state.status !== "pending") return;
10182
- await this.evaluateOpencodeToolProtection(part.tool, state.input, extractToolCommand(state.input), part.id);
10183
- }
10184
- async evaluateOpencodeToolProtection(toolName, toolInput, command, toolUseId) {
10185
- if (this.handledToolCallIds.has(toolUseId) || this.pendingToolCallIds.has(toolUseId)) return;
10186
- this.pendingToolCallIds.add(toolUseId);
10187
- try {
10188
- const result = await evaluateCommandProtection({
10189
- provider: "opencode",
10190
- source: "opencode_tool_call",
10191
- toolName,
10192
- toolInput,
10193
- command,
10194
- cwd: this.workingDirectory,
10195
- toolUseId
10196
- });
10197
- if (result.allowed) {
10198
- this.handledToolCallIds.add(toolUseId);
10199
- return;
10200
- }
10201
- const message = reportCommandProtectionBlock({
10202
- managerName: "OpencodeManager",
10203
- action: "tool call",
10204
- eventType: "opencode-command-protection.blocked",
10205
- result,
10206
- command,
10207
- context: {
10208
- toolName,
10209
- toolUseId
10210
- },
10211
- historyFile: this.historyFile,
10212
- recordHistoryEvent: this.recordHistoryEvent.bind(this)
10213
- });
10214
- await this.abortActiveTurn();
10215
- this.handledToolCallIds.add(toolUseId);
10216
- } finally {
10217
- this.pendingToolCallIds.delete(toolUseId);
10218
- }
10219
- }
10220
9959
  recordOpencodeMessageRole(payload) {
10221
9960
  const info = isRecord2(payload.info) ? payload.info : null;
10222
9961
  const id = typeof info?.id === "string" ? info.id : void 0;
@@ -10316,8 +10055,8 @@ var OpencodeManager = class extends CodingAgentManager {
10316
10055
  };
10317
10056
 
10318
10057
  // src/managers/pi-manager.ts
10319
- import { mkdir as mkdir15 } from "fs/promises";
10320
- import { dirname as dirname9, join as join23 } from "path";
10058
+ import { mkdir as mkdir16 } from "fs/promises";
10059
+ import { dirname as dirname10, join as join22 } from "path";
10321
10060
  import {
10322
10061
  AuthStorage,
10323
10062
  createAgentSession,
@@ -10413,6 +10152,12 @@ function registerCommandProtection(cwd, historyFile, recordHistoryEvent) {
10413
10152
  }
10414
10153
  return void 0;
10415
10154
  });
10155
+ pi.on("tool_result", (event) => {
10156
+ if (!event.isError && mayCreatePullRequest(event.toolName, event.input)) {
10157
+ pullRequestCheckService.check().catch(() => {
10158
+ });
10159
+ }
10160
+ });
10416
10161
  };
10417
10162
  }
10418
10163
  var PiManager = class extends CodingAgentManager {
@@ -10425,12 +10170,12 @@ var PiManager = class extends CodingAgentManager {
10425
10170
  providerApiKey = null;
10426
10171
  constructor(options) {
10427
10172
  super(options);
10428
- this.historyFilePath = options.historyFilePath ?? join23(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
10173
+ this.historyFilePath = options.historyFilePath ?? join22(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
10429
10174
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
10430
10175
  this.initializeManager(this.processMessageInternal.bind(this));
10431
10176
  }
10432
10177
  async initialize() {
10433
- await mkdir15(dirname9(this.historyFilePath), { recursive: true });
10178
+ await mkdir16(dirname10(this.historyFilePath), { recursive: true });
10434
10179
  }
10435
10180
  getHistorySink() {
10436
10181
  return this.historyFile;
@@ -10530,7 +10275,7 @@ var PiManager = class extends CodingAgentManager {
10530
10275
  const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
10531
10276
  const resourceLoader = new DefaultResourceLoader({
10532
10277
  cwd: this.workingDirectory,
10533
- agentDir: join23(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
10278
+ agentDir: join22(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
10534
10279
  extensionFactories: [registerCommandProtection(
10535
10280
  this.workingDirectory,
10536
10281
  this.historyFile,
@@ -11261,14 +11006,14 @@ async function uploadAnalytics(endpoint, body) {
11261
11006
  // src/analytics/analytics-record-buffer.ts
11262
11007
  import {
11263
11008
  appendFile as appendFile3,
11264
- mkdir as mkdir16,
11265
- readFile as readFile14,
11009
+ mkdir as mkdir17,
11010
+ readFile as readFile16,
11266
11011
  readdir as readdir6,
11267
11012
  rename as rename2,
11268
11013
  unlink as unlink3
11269
11014
  } from "fs/promises";
11270
- import { existsSync as existsSync8, readFileSync as readFileSync7, readdirSync } from "fs";
11271
- import { join as join24 } from "path";
11015
+ import { existsSync as existsSync9, readFileSync as readFileSync5, readdirSync } from "fs";
11016
+ import { join as join23 } from "path";
11272
11017
  var MAX_FAILED_RECORDS = 500;
11273
11018
  var FLUSH_DEBOUNCE_MS = 15e3;
11274
11019
  var MAX_UPLOADED_SEGMENTS = 50;
@@ -11280,7 +11025,7 @@ var AnalyticsRecordBuffer = class {
11280
11025
  options.storageName,
11281
11026
  ...options.legacyStorageNames ?? []
11282
11027
  ];
11283
- this.liveFile = join24(ENGINE_DIR2, `${options.storageName}.jsonl`);
11028
+ this.liveFile = join23(ENGINE_DIR2, `${options.storageName}.jsonl`);
11284
11029
  this.segmentFilePatterns = this.storageNames.map(
11285
11030
  (storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
11286
11031
  );
@@ -11303,7 +11048,7 @@ var AnalyticsRecordBuffer = class {
11303
11048
  if (this.recordIds.has(recordId)) return;
11304
11049
  this.recordIds.add(recordId);
11305
11050
  }
11306
- const pending = mkdir16(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
11051
+ const pending = mkdir17(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
11307
11052
  `, "utf-8")).catch((error) => {
11308
11053
  console.error(
11309
11054
  `[${this.options.storageName}] Append failed, will retry on flush:`,
@@ -11315,10 +11060,10 @@ var AnalyticsRecordBuffer = class {
11315
11060
  void pending.finally(() => this.pendingAppends.delete(pending));
11316
11061
  }
11317
11062
  readStoredRecords() {
11318
- if (!existsSync8(ENGINE_DIR2)) return [];
11063
+ if (!existsSync9(ENGINE_DIR2)) return [];
11319
11064
  return readdirSync(ENGINE_DIR2, { withFileTypes: true }).filter((entry) => entry.isFile() && this.storageNames.some(
11320
11065
  (storageName) => entry.name === `${storageName}.jsonl` || entry.name.startsWith(`${storageName}.`) && (entry.name.endsWith(".jsonl") || entry.name.endsWith(`.jsonl${UPLOADED_SUFFIX}`))
11321
- )).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync7(join24(ENGINE_DIR2, name), "utf-8"))).map((record) => {
11066
+ )).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync5(join23(ENGINE_DIR2, name), "utf-8"))).map((record) => {
11322
11067
  const recordId = this.options.getRecordId?.(record);
11323
11068
  if (recordId) this.recordIds.add(recordId);
11324
11069
  return record;
@@ -11353,8 +11098,8 @@ var AnalyticsRecordBuffer = class {
11353
11098
  }
11354
11099
  for (const storageName of this.storageNames) {
11355
11100
  await rename2(
11356
- join24(ENGINE_DIR2, `${storageName}.jsonl`),
11357
- join24(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
11101
+ join23(ENGINE_DIR2, `${storageName}.jsonl`),
11102
+ join23(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
11358
11103
  ).catch(() => {
11359
11104
  });
11360
11105
  }
@@ -11364,7 +11109,7 @@ var AnalyticsRecordBuffer = class {
11364
11109
  for (const entry of entries) {
11365
11110
  if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry))) continue;
11366
11111
  try {
11367
- await this.uploadSegment(join24(ENGINE_DIR2, entry));
11112
+ await this.uploadSegment(join23(ENGINE_DIR2, entry));
11368
11113
  flushed++;
11369
11114
  } catch (error) {
11370
11115
  failed++;
@@ -11393,14 +11138,14 @@ var AnalyticsRecordBuffer = class {
11393
11138
  (storageName) => entry.startsWith(`${storageName}.`)
11394
11139
  ) && entry.endsWith(UPLOADED_SUFFIX)).sort();
11395
11140
  for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
11396
- await unlink3(join24(ENGINE_DIR2, entry)).catch(() => {
11141
+ await unlink3(join23(ENGINE_DIR2, entry)).catch(() => {
11397
11142
  });
11398
11143
  }
11399
11144
  }
11400
11145
  return { flushed, failed };
11401
11146
  }
11402
11147
  async uploadSegment(filePath) {
11403
- const records = this.parseRecords(await readFile14(filePath, "utf-8"));
11148
+ const records = this.parseRecords(await readFile16(filePath, "utf-8"));
11404
11149
  if (records.length > 0) await this.options.upload(records);
11405
11150
  await rename2(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
11406
11151
  });
@@ -12186,19 +11931,19 @@ var KeepAliveService = class _KeepAliveService {
12186
11931
  var keepAliveService = new KeepAliveService();
12187
11932
 
12188
11933
  // src/services/canvas-service.ts
12189
- import { readdir as readdir7, readFile as readFile15, stat as stat3 } from "fs/promises";
12190
- import { homedir as homedir14 } from "os";
12191
- import { join as join25 } from "path";
11934
+ import { readdir as readdir7, readFile as readFile17, stat as stat3 } from "fs/promises";
11935
+ import { homedir as homedir12 } from "os";
11936
+ import { join as join24 } from "path";
12192
11937
  var GLOBAL_CANVAS_DIRECTORIES = [
12193
- join25(homedir14(), ".claude", "plans"),
12194
- join25(process.env.XDG_DATA_HOME ?? join25(homedir14(), ".local", "share"), "opencode", "plans"),
12195
- join25(homedir14(), ".replicas", "canvas")
11938
+ join24(homedir12(), ".claude", "plans"),
11939
+ join24(process.env.XDG_DATA_HOME ?? join24(homedir12(), ".local", "share"), "opencode", "plans"),
11940
+ join24(homedir12(), ".replicas", "canvas")
12196
11941
  ];
12197
11942
  async function canvasDirectories() {
12198
11943
  const repositories = await gitService.listRepositories().catch(() => []);
12199
11944
  return [
12200
11945
  ...GLOBAL_CANVAS_DIRECTORIES,
12201
- ...repositories.map((repository) => join25(repository.path, ".opencode", "plans"))
11946
+ ...repositories.map((repository) => join24(repository.path, ".opencode", "plans"))
12202
11947
  ];
12203
11948
  }
12204
11949
  var CanvasService = class {
@@ -12222,7 +11967,7 @@ var CanvasService = class {
12222
11967
  for (const entry of entries) {
12223
11968
  if (entry.name.startsWith(".")) continue;
12224
11969
  const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
12225
- const filePath = join25(current.directory, entry.name);
11970
+ const filePath = join24(current.directory, entry.name);
12226
11971
  if (entry.isDirectory()) {
12227
11972
  pending.push({ directory: filePath, relativePath: filename });
12228
11973
  continue;
@@ -12247,7 +11992,7 @@ var CanvasService = class {
12247
11992
  if (!safe) return null;
12248
11993
  const { kind, mimeType } = classifyCanvasFilename(safe);
12249
11994
  for (const directory of await this.directories()) {
12250
- const filePath = join25(directory, safe);
11995
+ const filePath = join24(directory, safe);
12251
11996
  let sizeBytes = 0;
12252
11997
  let updatedAt = "";
12253
11998
  try {
@@ -12268,7 +12013,7 @@ var CanvasService = class {
12268
12013
  };
12269
12014
  }
12270
12015
  try {
12271
- const bytes = await readFile15(filePath);
12016
+ const bytes = await readFile17(filePath);
12272
12017
  return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
12273
12018
  } catch {
12274
12019
  continue;
@@ -12418,14 +12163,14 @@ async function reconcileCanvasItems(filenames) {
12418
12163
  // src/services/upload-chat-transcripts.ts
12419
12164
  import { createReadStream } from "fs";
12420
12165
  import { createHash as createHash3 } from "crypto";
12421
- import { readFile as readFile16, readdir as readdir8, stat as stat4 } from "fs/promises";
12422
- import { basename as basename2, join as join27 } from "path";
12166
+ import { readFile as readFile18, readdir as readdir8, stat as stat4 } from "fs/promises";
12167
+ import { basename as basename3, join as join26 } from "path";
12423
12168
 
12424
12169
  // src/services/chat/chat-senders.ts
12425
- import { join as join26 } from "path";
12426
- var CHAT_SENDERS_DIR = join26(ENGINE_DIR2, "chat-senders");
12170
+ import { join as join25 } from "path";
12171
+ var CHAT_SENDERS_DIR = join25(ENGINE_DIR2, "chat-senders");
12427
12172
  function chatMessageSendersFilePath(chatId) {
12428
- return join26(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
12173
+ return join25(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
12429
12174
  }
12430
12175
  function parseChatMessageSendersJsonl(content) {
12431
12176
  return content.split("\n").flatMap((line) => {
@@ -12441,9 +12186,9 @@ function parseChatMessageSendersJsonl(content) {
12441
12186
 
12442
12187
  // src/services/upload-chat-transcripts.ts
12443
12188
  var HISTORY_DIRS = [
12444
- join27(ENGINE_DIR2, "claude-histories"),
12445
- join27(ENGINE_DIR2, "relay-histories"),
12446
- join27(ENGINE_DIR2, "codex-histories")
12189
+ join26(ENGINE_DIR2, "claude-histories"),
12190
+ join26(ENGINE_DIR2, "relay-histories"),
12191
+ join26(ENGINE_DIR2, "codex-histories")
12447
12192
  ];
12448
12193
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
12449
12194
  let flushed = 0;
@@ -12459,9 +12204,9 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
12459
12204
  }
12460
12205
  for (const entry of entries) {
12461
12206
  if (!entry.endsWith(".jsonl")) continue;
12462
- const chatId = basename2(entry, ".jsonl");
12207
+ const chatId = basename3(entry, ".jsonl");
12463
12208
  tasks.push(
12464
- uploadChatTranscript(chatId, join27(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
12209
+ uploadChatTranscript(chatId, join26(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
12465
12210
  flushed++;
12466
12211
  if (artifact && capture) revisions.push(artifact);
12467
12212
  }).catch((err) => {
@@ -12477,7 +12222,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
12477
12222
  async function uploadChatTranscript(chatId, filePath, chat, capture) {
12478
12223
  const { size } = await stat4(filePath);
12479
12224
  if (size === 0) return null;
12480
- const historyPages = createChatTranscriptPages(await readFile16(filePath));
12225
+ const historyPages = createChatTranscriptPages(await readFile18(filePath));
12481
12226
  const metadata = chat ? {
12482
12227
  provider: chat.provider,
12483
12228
  credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
@@ -12496,7 +12241,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
12496
12241
  }
12497
12242
  try {
12498
12243
  metadata.senders = parseChatMessageSendersJsonl(
12499
- await readFile16(chatMessageSendersFilePath(chatId), "utf-8")
12244
+ await readFile18(chatMessageSendersFilePath(chatId), "utf-8")
12500
12245
  );
12501
12246
  } catch (error) {
12502
12247
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
@@ -12543,43 +12288,10 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
12543
12288
  return artifact;
12544
12289
  }
12545
12290
 
12546
- // src/services/upload-repo-state.ts
12547
- var uploadQueue = Promise.resolve();
12548
- async function sendRepoState(repos) {
12549
- const form = new FormData();
12550
- form.append(
12551
- "file",
12552
- new Blob([JSON.stringify({ repos })], { type: "application/json" }),
12553
- "repo-state.json"
12554
- );
12555
- const response = await monolithRequest("/v1/engine/repo-state", { body: form });
12556
- if (!response.ok) {
12557
- const errorText = await response.text();
12558
- throw new Error(`upload failed: ${response.status} ${errorText}`);
12559
- }
12560
- }
12561
- function uploadRepoState(repos) {
12562
- const upload = uploadQueue.then(() => sendRepoState(repos));
12563
- uploadQueue = upload.catch(() => {
12564
- });
12565
- return upload;
12566
- }
12567
- async function flushRepoState() {
12568
- try {
12569
- const { repos, complete } = await gitService.listReposWithCompleteness({ includeDiffs: true });
12570
- if (!complete) return { flushed: 0, skipped: 1, failed: 0 };
12571
- await uploadRepoState(repos);
12572
- return { flushed: 1, skipped: 0, failed: 0 };
12573
- } catch (err) {
12574
- console.error("[RepoStateUploader] upload failed:", err);
12575
- return { flushed: 0, skipped: 0, failed: 1 };
12576
- }
12577
- }
12578
-
12579
12291
  // src/services/upload-engine-logs.ts
12580
12292
  import { createReadStream as createReadStream2 } from "fs";
12581
12293
  import { readdir as readdir9, stat as stat5 } from "fs/promises";
12582
- import { join as join28 } from "path";
12294
+ import { join as join27 } from "path";
12583
12295
  var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
12584
12296
  var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
12585
12297
  var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
@@ -12608,7 +12320,7 @@ async function flushAllEngineLogs() {
12608
12320
  const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
12609
12321
  try {
12610
12322
  const sessionId = filename.slice(0, -".log".length);
12611
- const filePath = join28(LOG_DIR, filename);
12323
+ const filePath = join27(LOG_DIR, filename);
12612
12324
  const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
12613
12325
  if (!fileStat.isFile()) {
12614
12326
  skipped++;
@@ -12709,7 +12421,7 @@ function isCodexAvailable() {
12709
12421
  return detectCodexAuthMethod() !== "none";
12710
12422
  }
12711
12423
  function isOpencodeAvailable() {
12712
- return existsSync9(OPENCODE_AUTH_PATH);
12424
+ return existsSync10(OPENCODE_AUTH_PATH);
12713
12425
  }
12714
12426
  function isDeepseekAvailable() {
12715
12427
  return Boolean(getDeepseekApiKey());
@@ -12721,7 +12433,7 @@ function isKimiAvailable() {
12721
12433
  return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
12722
12434
  }
12723
12435
  function isPiAvailable() {
12724
- return existsSync9(PI_AUTH_PATH);
12436
+ return existsSync10(PI_AUTH_PATH);
12725
12437
  }
12726
12438
  function isCursorAvailable() {
12727
12439
  return Boolean(ENGINE_ENV.CURSOR_API_KEY);
@@ -12860,13 +12572,13 @@ var ChatService = class {
12860
12572
  persistPromise = null;
12861
12573
  persistQueued = false;
12862
12574
  async initialize() {
12863
- await mkdir17(ENGINE_DIR2, { recursive: true });
12864
- await mkdir17(CLAUDE_HISTORY_DIR, { recursive: true });
12865
- await mkdir17(RELAY_HISTORY_DIR, { recursive: true });
12866
- await mkdir17(CODEX_HISTORY_DIR, { recursive: true });
12867
- await mkdir17(CURSOR_HISTORY_DIR, { recursive: true });
12868
- await mkdir17(OPENCODE_HISTORY_DIR, { recursive: true });
12869
- await mkdir17(CHAT_SENDERS_DIR, { recursive: true });
12575
+ await mkdir18(ENGINE_DIR2, { recursive: true });
12576
+ await mkdir18(CLAUDE_HISTORY_DIR, { recursive: true });
12577
+ await mkdir18(RELAY_HISTORY_DIR, { recursive: true });
12578
+ await mkdir18(CODEX_HISTORY_DIR, { recursive: true });
12579
+ await mkdir18(CURSOR_HISTORY_DIR, { recursive: true });
12580
+ await mkdir18(OPENCODE_HISTORY_DIR, { recursive: true });
12581
+ await mkdir18(CHAT_SENDERS_DIR, { recursive: true });
12870
12582
  const persisted = await this.loadChats();
12871
12583
  for (const chat of persisted) {
12872
12584
  const runtime = this.createRuntimeChat(chat);
@@ -13066,7 +12778,7 @@ var ChatService = class {
13066
12778
  }
13067
12779
  }
13068
12780
  async readSenders(chatId) {
13069
- return readFile17(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
12781
+ return readFile19(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
13070
12782
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
13071
12783
  console.error("[ChatService] Failed to read sender records:", error);
13072
12784
  return [];
@@ -13305,13 +13017,13 @@ var ChatService = class {
13305
13017
  return descendants;
13306
13018
  }
13307
13019
  async deleteHistoryFile(persisted) {
13308
- const historyPath = join29(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
13020
+ const historyPath = join28(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
13309
13021
  await Promise.all([
13310
13022
  historyPath,
13311
13023
  `${historyPath}.pages.jsonl`,
13312
13024
  `${historyPath}.pages.index.json`
13313
- ].map((path6) => rm2(path6, { force: true })));
13314
- await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
13025
+ ].map((path6) => rm3(path6, { force: true })));
13026
+ await rm3(chatMessageSendersFilePath(persisted.id), { force: true });
13315
13027
  }
13316
13028
  async getChatHistory(chatId, page = {}) {
13317
13029
  const chat = this.requireChat(chatId);
@@ -13436,7 +13148,7 @@ var ChatService = class {
13436
13148
  if (persisted.provider === "claude") {
13437
13149
  provider = new ClaudeManager({
13438
13150
  workingDirectory: this.workingDirectory,
13439
- historyFilePath: join29(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13151
+ historyFilePath: join28(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13440
13152
  initialSessionId: persisted.providerSessionId,
13441
13153
  onSaveSessionId: saveSession,
13442
13154
  onTurnComplete: onProviderTurnComplete,
@@ -13456,7 +13168,7 @@ var ChatService = class {
13456
13168
  });
13457
13169
  provider = new RelayManager({
13458
13170
  workingDirectory: this.workingDirectory,
13459
- historyFilePath: join29(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13171
+ historyFilePath: join28(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13460
13172
  initialSessionId: persisted.providerSessionId,
13461
13173
  onSaveSessionId: saveSession,
13462
13174
  onTurnComplete: onProviderTurnComplete,
@@ -13470,7 +13182,7 @@ var ChatService = class {
13470
13182
  } else if (persisted.provider === "cursor") {
13471
13183
  provider = new CursorManager({
13472
13184
  workingDirectory: this.workingDirectory,
13473
- historyFilePath: join29(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13185
+ historyFilePath: join28(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13474
13186
  initialSessionId: persisted.providerSessionId,
13475
13187
  onSaveSessionId: saveSession,
13476
13188
  onTurnComplete: onProviderTurnComplete,
@@ -13482,7 +13194,7 @@ var ChatService = class {
13482
13194
  } else if (persisted.provider === "deepseek") {
13483
13195
  provider = new DeepseekManager({
13484
13196
  workingDirectory: this.workingDirectory,
13485
- historyFilePath: join29(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
13197
+ historyFilePath: join28(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
13486
13198
  initialSessionId: persisted.providerSessionId,
13487
13199
  onSaveSessionId: saveSession,
13488
13200
  onTurnComplete: onProviderTurnComplete,
@@ -13494,7 +13206,7 @@ var ChatService = class {
13494
13206
  } else if (persisted.provider === "fx") {
13495
13207
  provider = new FxManager({
13496
13208
  workingDirectory: this.workingDirectory,
13497
- historyFilePath: join29(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
13209
+ historyFilePath: join28(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
13498
13210
  initialSessionId: persisted.providerSessionId,
13499
13211
  onSaveSessionId: saveSession,
13500
13212
  onTurnComplete: onProviderTurnComplete,
@@ -13505,7 +13217,7 @@ var ChatService = class {
13505
13217
  } else if (persisted.provider === "kimi") {
13506
13218
  provider = new KimiManager({
13507
13219
  workingDirectory: this.workingDirectory,
13508
- historyFilePath: join29(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
13220
+ historyFilePath: join28(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
13509
13221
  initialSessionId: persisted.providerSessionId,
13510
13222
  onSaveSessionId: saveSession,
13511
13223
  onTurnComplete: onProviderTurnComplete,
@@ -13516,7 +13228,7 @@ var ChatService = class {
13516
13228
  } else if (persisted.provider === "opencode") {
13517
13229
  provider = new OpencodeManager({
13518
13230
  workingDirectory: this.workingDirectory,
13519
- historyFilePath: join29(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13231
+ historyFilePath: join28(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13520
13232
  initialSessionId: persisted.providerSessionId,
13521
13233
  onSaveSessionId: saveSession,
13522
13234
  onTurnComplete: onProviderTurnComplete,
@@ -13528,7 +13240,7 @@ var ChatService = class {
13528
13240
  } else if (persisted.provider === "pi") {
13529
13241
  provider = new PiManager({
13530
13242
  workingDirectory: this.workingDirectory,
13531
- historyFilePath: join29(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
13243
+ historyFilePath: join28(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
13532
13244
  initialSessionId: persisted.providerSessionId,
13533
13245
  onSaveSessionId: saveSession,
13534
13246
  onTurnComplete: onProviderTurnComplete,
@@ -13545,7 +13257,7 @@ var ChatService = class {
13545
13257
  );
13546
13258
  provider = new CodexAspManager({
13547
13259
  workingDirectory: this.workingDirectory,
13548
- historyFilePath: join29(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13260
+ historyFilePath: join28(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13549
13261
  initialSessionId: persisted.providerSessionId,
13550
13262
  onSaveSessionId: saveSession,
13551
13263
  onTurnComplete: onProviderTurnComplete,
@@ -13753,7 +13465,7 @@ var ChatService = class {
13753
13465
  });
13754
13466
  uploadChatTranscript(
13755
13467
  chatId,
13756
- join29(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13468
+ join28(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13757
13469
  this.toSummary(chat)
13758
13470
  ).catch((err) => {
13759
13471
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -13769,7 +13481,7 @@ var ChatService = class {
13769
13481
  }
13770
13482
  async loadChats() {
13771
13483
  try {
13772
- const content = await readFile17(CHATS_FILE, "utf-8");
13484
+ const content = await readFile19(CHATS_FILE, "utf-8");
13773
13485
  return parsePersistedChatsContent(content);
13774
13486
  } catch (error) {
13775
13487
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -13784,7 +13496,7 @@ var ChatService = class {
13784
13496
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
13785
13497
  }
13786
13498
  try {
13787
- const backupContent = await readFile17(CHATS_BACKUP_FILE, "utf-8");
13499
+ const backupContent = await readFile19(CHATS_BACKUP_FILE, "utf-8");
13788
13500
  return parsePersistedChatsContent(backupContent);
13789
13501
  } catch (backupError) {
13790
13502
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -13805,7 +13517,7 @@ var ChatService = class {
13805
13517
  this.persistQueued = false;
13806
13518
  const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
13807
13519
  try {
13808
- await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
13520
+ await copyFile2(CHATS_FILE, CHATS_BACKUP_FILE);
13809
13521
  } catch (error) {
13810
13522
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
13811
13523
  console.error("[ChatService] Failed to update chats backup:", error);
@@ -13842,12 +13554,6 @@ var ChatService = class {
13842
13554
  }
13843
13555
  }
13844
13556
  async publishAgentTurnCompleteWebhook(chat, completedAt, processing = false) {
13845
- await Promise.all([
13846
- githubTokenManager.refreshOnce().catch(() => {
13847
- }),
13848
- gitlabTokenManager.refreshOnce().catch(() => {
13849
- })
13850
- ]);
13851
13557
  const linearSessionId = ENGINE_ENV.REPLICAS_LINEAR_SESSION_ID;
13852
13558
  const observedBranches = chat.observedBranchesByRepo;
13853
13559
  chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
@@ -13856,20 +13562,12 @@ var ChatService = class {
13856
13562
  const errorNotificationTarget = chat.activeErrorNotificationTarget;
13857
13563
  chat.activeErrorNotificationTarget = null;
13858
13564
  let repoStatuses = [];
13859
- let repoStateComplete = false;
13860
13565
  try {
13861
- const refreshed = await gitService.refreshReposWithCompleteness(observedBranches, { includeDiffs: true });
13862
- repoStatuses = refreshed.repos;
13863
- repoStateComplete = refreshed.complete;
13566
+ repoStatuses = await refreshRepoStatuses(observedBranches, true);
13864
13567
  console.log(`Repository Statuses Refreshed: `, repoStatuses);
13865
13568
  } catch (error) {
13866
13569
  console.error("[ChatService] Failed to refresh repo statuses:", error);
13867
13570
  }
13868
- if (repoStateComplete) {
13869
- uploadRepoState(repoStatuses).catch((err) => {
13870
- console.error("[ChatService] Failed to upload repository state:", err);
13871
- });
13872
- }
13873
13571
  try {
13874
13572
  const payload = {
13875
13573
  ...linearSessionId ? { linearSessionId } : {},
@@ -13888,8 +13586,8 @@ var ChatService = class {
13888
13586
 
13889
13587
  // src/services/repo-file-service.ts
13890
13588
  import { execFile } from "child_process";
13891
- import { readFile as readFile18, realpath, stat as stat6 } from "fs/promises";
13892
- import { join as join30, resolve as resolve2, extname as extname2 } from "path";
13589
+ import { readFile as readFile20, realpath, stat as stat6 } from "fs/promises";
13590
+ import { join as join29, resolve as resolve2, extname as extname2 } from "path";
13893
13591
  var CACHE_TTL_MS = 3e4;
13894
13592
  var SEARCH_TIMEOUT_MS = 15e3;
13895
13593
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -13941,11 +13639,11 @@ function scoreMatch(query2, filePath) {
13941
13639
  const lowerQuery = query2.toLowerCase();
13942
13640
  const lowerPath = filePath.toLowerCase();
13943
13641
  const segments = lowerPath.split("/");
13944
- const basename3 = segments[segments.length - 1] ?? "";
13945
- if (basename3 === lowerQuery) return 100;
13946
- if (basename3.startsWith(lowerQuery)) return 90;
13642
+ const basename4 = segments[segments.length - 1] ?? "";
13643
+ if (basename4 === lowerQuery) return 100;
13644
+ if (basename4.startsWith(lowerQuery)) return 90;
13947
13645
  if (segments.some((seg) => seg === lowerQuery)) return 80;
13948
- if (basename3.includes(lowerQuery)) return 70;
13646
+ if (basename4.includes(lowerQuery)) return 70;
13949
13647
  if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
13950
13648
  if (lowerPath.includes(lowerQuery)) return 50;
13951
13649
  return 0;
@@ -14041,7 +13739,7 @@ var RepoFileService = class {
14041
13739
  const repo = repos.find((r) => r.name === repoName);
14042
13740
  if (!repo) return null;
14043
13741
  try {
14044
- const fullPath = await realpath(resolve2(join30(repo.path, filePath)));
13742
+ const fullPath = await realpath(resolve2(join29(repo.path, filePath)));
14045
13743
  const repoRoot = await realpath(repo.path);
14046
13744
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
14047
13745
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
@@ -14072,7 +13770,7 @@ var RepoFileService = class {
14072
13770
  sizeBytes,
14073
13771
  binary: true,
14074
13772
  tooLarge: false,
14075
- base64: (await readFile18(fullPath)).toString("base64"),
13773
+ base64: (await readFile20(fullPath)).toString("base64"),
14076
13774
  mimeType
14077
13775
  };
14078
13776
  }
@@ -14097,7 +13795,7 @@ var RepoFileService = class {
14097
13795
  tooLarge: true
14098
13796
  };
14099
13797
  }
14100
- const content = await readFile18(fullPath, "utf-8");
13798
+ const content = await readFile20(fullPath, "utf-8");
14101
13799
  return {
14102
13800
  repoName,
14103
13801
  path: filePath,
@@ -14175,21 +13873,21 @@ var RepoFileService = class {
14175
13873
  // src/v1-routes.ts
14176
13874
  import { Hono } from "hono";
14177
13875
  import { z as z5 } from "zod";
14178
- import { readdir as readdir11, stat as stat7, readFile as readFile21 } from "fs/promises";
14179
- import { join as join33, resolve as resolve3 } from "path";
13876
+ import { readdir as readdir11, stat as stat7, readFile as readFile23 } from "fs/promises";
13877
+ import { join as join32, resolve as resolve3 } from "path";
14180
13878
 
14181
13879
  // src/services/warm-hooks-service.ts
14182
13880
  import { spawn as spawn6 } from "child_process";
14183
- import { readFile as readFile20 } from "fs/promises";
14184
- import { existsSync as existsSync10 } from "fs";
14185
- import { join as join32 } from "path";
13881
+ import { readFile as readFile22 } from "fs/promises";
13882
+ import { existsSync as existsSync11 } from "fs";
13883
+ import { join as join31 } from "path";
14186
13884
 
14187
13885
  // src/services/warm-hook-logs-service.ts
14188
- import { mkdir as mkdir18, readFile as readFile19, writeFile as writeFile7, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
14189
- import { homedir as homedir15 } from "os";
14190
- import { join as join31 } from "path";
14191
- var LOGS_DIR2 = join31(homedir15(), ".replicas", "warm-hook-logs");
14192
- var CURRENT_RUN_LOG = join31(LOGS_DIR2, "current-run.log");
13886
+ import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile9, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
13887
+ import { homedir as homedir13 } from "os";
13888
+ import { join as join30 } from "path";
13889
+ var LOGS_DIR2 = join30(homedir13(), ".replicas", "warm-hook-logs");
13890
+ var CURRENT_RUN_LOG = join30(LOGS_DIR2, "current-run.log");
14193
13891
  var GLOBAL_FILENAME = "global.json";
14194
13892
  function withPreview2(stored) {
14195
13893
  const preview = buildHookOutputPreview(stored.output);
@@ -14197,7 +13895,7 @@ function withPreview2(stored) {
14197
13895
  }
14198
13896
  var WarmHookLogsService = class {
14199
13897
  async ensureDir() {
14200
- await mkdir18(LOGS_DIR2, { recursive: true });
13898
+ await mkdir19(LOGS_DIR2, { recursive: true });
14201
13899
  }
14202
13900
  async saveGlobalHookLog(entry) {
14203
13901
  await this.ensureDir();
@@ -14206,7 +13904,7 @@ var WarmHookLogsService = class {
14206
13904
  hookName: "organization",
14207
13905
  ...entry
14208
13906
  };
14209
- await writeFile7(join31(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
13907
+ await writeFile9(join30(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
14210
13908
  `, "utf-8");
14211
13909
  }
14212
13910
  async saveEnvironmentHookLog(entry) {
@@ -14216,7 +13914,7 @@ var WarmHookLogsService = class {
14216
13914
  hookName: "environment",
14217
13915
  ...entry
14218
13916
  };
14219
- await writeFile7(join31(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
13917
+ await writeFile9(join30(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
14220
13918
  `, "utf-8");
14221
13919
  }
14222
13920
  async saveRepoHookLog(repoName, entry) {
@@ -14226,7 +13924,7 @@ var WarmHookLogsService = class {
14226
13924
  hookName: repoName,
14227
13925
  ...entry
14228
13926
  };
14229
- await writeFile7(join31(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
13927
+ await writeFile9(join30(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
14230
13928
  `, "utf-8");
14231
13929
  }
14232
13930
  async getAllLogs() {
@@ -14245,7 +13943,7 @@ var WarmHookLogsService = class {
14245
13943
  continue;
14246
13944
  }
14247
13945
  try {
14248
- const raw = await readFile19(join31(LOGS_DIR2, file), "utf-8");
13946
+ const raw = await readFile21(join30(LOGS_DIR2, file), "utf-8");
14249
13947
  const stored = JSON.parse(raw);
14250
13948
  logs.push(withPreview2(stored));
14251
13949
  } catch {
@@ -14274,7 +13972,7 @@ var WarmHookLogsService = class {
14274
13972
  }
14275
13973
  async getCurrentRunLog() {
14276
13974
  try {
14277
- return await readFile19(CURRENT_RUN_LOG, "utf-8");
13975
+ return await readFile21(CURRENT_RUN_LOG, "utf-8");
14278
13976
  } catch (err) {
14279
13977
  if (err.code === "ENOENT") return null;
14280
13978
  throw err;
@@ -14283,7 +13981,7 @@ var WarmHookLogsService = class {
14283
13981
  async getFullOutput(hookType, hookName) {
14284
13982
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
14285
13983
  try {
14286
- const raw = await readFile19(join31(LOGS_DIR2, filename), "utf-8");
13984
+ const raw = await readFile21(join30(LOGS_DIR2, filename), "utf-8");
14287
13985
  const stored = JSON.parse(raw);
14288
13986
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
14289
13987
  return null;
@@ -14302,12 +14000,12 @@ var warmHookLogsService = new WarmHookLogsService();
14302
14000
  // src/services/warm-hooks-service.ts
14303
14001
  async function readRepoWarmHook(repoPath) {
14304
14002
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
14305
- const configPath = join32(repoPath, filename);
14306
- if (!existsSync10(configPath)) {
14003
+ const configPath = join31(repoPath, filename);
14004
+ if (!existsSync11(configPath)) {
14307
14005
  continue;
14308
14006
  }
14309
14007
  try {
14310
- const raw = await readFile20(configPath, "utf-8");
14008
+ const raw = await readFile22(configPath, "utf-8");
14311
14009
  const config = parseReplicasConfigString(raw, filename);
14312
14010
  if (!config.warmHook) {
14313
14011
  return null;
@@ -14564,7 +14262,7 @@ ${combinedScript}` : combinedScript;
14564
14262
 
14565
14263
  // src/services/terminal-service.ts
14566
14264
  import { randomUUID as randomUUID10 } from "crypto";
14567
- import { existsSync as existsSync11 } from "fs";
14265
+ import { existsSync as existsSync12 } from "fs";
14568
14266
  import { spawn as spawn7 } from "node-pty";
14569
14267
  var MAX_REPLAY_CHARS = 1024 * 1024;
14570
14268
  var MAX_TERMINAL_SESSIONS = 8;
@@ -14583,7 +14281,7 @@ var TerminalService = class {
14583
14281
  });
14584
14282
  }
14585
14283
  const id = randomUUID10();
14586
- const shell = process.env.SHELL && existsSync11(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
14284
+ const shell = process.env.SHELL && existsSync12(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
14587
14285
  const pty = spawn7(shell, ["-l"], {
14588
14286
  name: "xterm-256color",
14589
14287
  cols,
@@ -14710,6 +14408,11 @@ function jsonError(message, details) {
14710
14408
  }
14711
14409
  function createV1Routes(deps) {
14712
14410
  const app2 = new Hono();
14411
+ app2.post("/pull-requests/check", (c) => {
14412
+ pullRequestCheckService.check().catch(() => {
14413
+ });
14414
+ return c.json({ accepted: true }, 202);
14415
+ });
14713
14416
  const loadChatHistory = async (c, chatId, page, failureMessage, unexpectedStatus, legacy = false) => {
14714
14417
  try {
14715
14418
  const history = await deps.chatService.getChatHistory(chatId, legacy && page.limit !== void 0 ? {} : page);
@@ -15514,7 +15217,7 @@ data: ${JSON.stringify("Terminal session not found")}
15514
15217
  const logFiles = files.filter((f) => f.endsWith(".log"));
15515
15218
  const sessions = await Promise.all(
15516
15219
  logFiles.map(async (filename) => {
15517
- const filePath = join33(LOG_DIR, filename);
15220
+ const filePath = join32(LOG_DIR, filename);
15518
15221
  const fileStat = await stat7(filePath);
15519
15222
  const sessionId = filename.replace(/\.log$/, "");
15520
15223
  return {
@@ -15549,7 +15252,7 @@ data: ${JSON.stringify("Terminal session not found")}
15549
15252
  }
15550
15253
  let content;
15551
15254
  try {
15552
- content = await readFile21(filePath, "utf-8");
15255
+ content = await readFile23(filePath, "utf-8");
15553
15256
  } catch {
15554
15257
  return c.json(jsonError("Log session not found"), 404);
15555
15258
  }
@@ -15620,17 +15323,17 @@ var HeartbeatService = class _HeartbeatService {
15620
15323
  var heartbeatService = new HeartbeatService();
15621
15324
 
15622
15325
  // src/services/workspace-sdk-service.ts
15623
- import { cp, mkdir as mkdir19 } from "fs/promises";
15624
- import { dirname as dirname10, resolve as resolve4 } from "path";
15625
- import { fileURLToPath as fileURLToPath3 } from "url";
15326
+ import { cp, mkdir as mkdir20 } from "fs/promises";
15327
+ import { dirname as dirname11, resolve as resolve4 } from "path";
15328
+ import { fileURLToPath as fileURLToPath4 } from "url";
15626
15329
  async function installWorkspaceSdk() {
15627
- const source = resolve4(dirname10(fileURLToPath3(import.meta.url)), "../../workspace-sdk");
15330
+ const source = resolve4(dirname11(fileURLToPath4(import.meta.url)), "../../workspace-sdk");
15628
15331
  const targets = [
15629
15332
  resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
15630
15333
  "/tmp/node_modules/@replicas/sdk"
15631
15334
  ];
15632
15335
  await Promise.all(targets.map(async (target) => {
15633
- await mkdir19(target, { recursive: true });
15336
+ await mkdir20(target, { recursive: true });
15634
15337
  await cp(source, target, { recursive: true, force: true });
15635
15338
  }));
15636
15339
  }
@@ -15658,7 +15361,7 @@ async function timeStartupStep(name, fn) {
15658
15361
  async function waitForInitializationGate() {
15659
15362
  if (!ENGINE_ENV.REPLICAS_ENGINE_DEFER_INITIALIZATION) return;
15660
15363
  const deadline = Date.now() + ENGINE_INIT_GATE_TIMEOUT_MS;
15661
- while (!existsSync12(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
15364
+ while (!existsSync13(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
15662
15365
  if (Date.now() >= deadline) {
15663
15366
  throw new Error("Timed out waiting for deferred engine initialization gate");
15664
15367
  }