replicas-engine 0.1.702 → 0.1.703

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,45 @@
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
+ isRecord as isRecord2,
8
+ monolithRequest,
9
+ monolithService,
10
+ reportCommandProtectionBlock,
11
+ setAgentCredentialSnapshot
12
+ } from "./chunk-RVDZXLBY.js";
13
+ import {
4
14
  ACCOUNT_RATE_LIMITS_UPDATED_METHOD,
5
- AGENT,
6
15
  AGENT_MESSAGE_DELTA_METHOD,
16
+ AppServerProcess,
17
+ COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
18
+ FILE_CHANGE_OUTPUT_DELTA_METHOD,
19
+ ITEM_COMPLETED_METHOD,
20
+ ITEM_STARTED_METHOD,
21
+ MODEL_REROUTED_METHOD,
22
+ REASONING_SUMMARY_PART_ADDED_METHOD,
23
+ REASONING_SUMMARY_TEXT_DELTA_METHOD,
24
+ REASONING_TEXT_DELTA_METHOD,
25
+ SUBPROCESS_MAX_BUFFER,
26
+ THREAD_COMPACTED_METHOD,
27
+ THREAD_GOAL_CLEARED_METHOD,
28
+ THREAD_GOAL_UPDATED_METHOD,
29
+ THREAD_TOKEN_USAGE_UPDATED_METHOD,
30
+ TURN_COMPLETED_METHOD,
31
+ TURN_PLAN_UPDATED_METHOD,
32
+ TURN_STARTED_METHOD,
33
+ buildCodexAgentEnv,
34
+ dispatchAspNotification,
35
+ execAsync,
36
+ execFileAsync,
37
+ putPresignedFile,
38
+ recoverCompletedTurn
39
+ } from "./chunk-U3XQBTVU.js";
40
+ import {
41
+ ACCEPTED_USER_MESSAGE_SOURCE,
42
+ AGENT,
7
43
  AGENT_MODELS,
8
44
  AI_GATEWAY_BASE_URL,
9
45
  ASTER_BASE_URL,
@@ -12,7 +48,6 @@ import {
12
48
  ASTER_PROVIDER,
13
49
  AUTH_FALLBACK_EVENT_TYPE,
14
50
  AUTH_RETRY_STATUS_EVENT_TYPE,
15
- AppServerProcess,
16
51
  CHAT_GOAL_EVENT_TYPE,
17
52
  CHAT_INTERRUPTED_EVENT_TYPE,
18
53
  CLAUDE_AUTH_ENV_KEYS,
@@ -32,7 +67,6 @@ import {
32
67
  CODEX_AUTH_ENV_KEYS,
33
68
  CODEX_AUTH_ENV_KEYS_BY_METHOD,
34
69
  CODEX_QUOTA_STATUS_EVENT_TYPE,
35
- COMMAND_EXECUTION_OUTPUT_DELTA_METHOD,
36
70
  COMPACTION_STATUS_EVENT_TYPE,
37
71
  CONTEXT_USAGE_EVENT_TYPE,
38
72
  CURSOR_CONNECTION_STATUS_EVENT_TYPE,
@@ -52,14 +86,11 @@ import {
52
86
  ENGINE_HEALTH_MAX_WAIT_MS,
53
87
  ENGINE_HEALTH_WAIT_HEADER,
54
88
  ENGINE_HEALTH_WAIT_QUERY_PARAM,
55
- FILE_CHANGE_OUTPUT_DELTA_METHOD,
56
89
  GITHUB_CREDENTIAL_REFRESH_PATH,
57
90
  GITLAB_CREDENTIAL_REFRESH_PATH,
58
91
  GIT_CREDENTIAL_HELPER_FILENAME,
59
92
  HOOK_EXEC_MAX_BUFFER_BYTES,
60
93
  IMAGE_MEDIA_TYPES,
61
- ITEM_COMPLETED_METHOD,
62
- ITEM_STARTED_METHOD,
63
94
  InFlightMap,
64
95
  MAX_CANVAS_FILE_BYTES,
65
96
  MAX_CANVAS_PREVIEW_BYTES,
@@ -68,34 +99,19 @@ import {
68
99
  MEMORY_ROOT,
69
100
  MEMORY_SUMMARY_FILENAME,
70
101
  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
102
  REPLICAS_CONFIG_FILENAMES,
76
- REPLICAS_RUNTIME_ENV_ALIASES,
77
103
  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
104
  TIMEOUT,
84
105
  TOKEN_USAGE_PATH,
85
- TURN_COMPLETED_METHOD,
86
- TURN_PLAN_UPDATED_METHOD,
87
- TURN_STARTED_METHOD,
88
106
  TaskAccumulator,
89
107
  USER_MESSAGE_ID_PAYLOAD_KEY,
90
108
  VALID_AGENT_PROVIDERS,
91
109
  VALID_RELAY_SUBAGENT_PROVIDERS,
92
110
  VALID_THINKING_LEVELS,
93
- agentCredentialSnapshotSchema,
94
111
  applyCodexAspTranscriptDelta,
95
112
  areSameUserMessageEvents,
96
113
  areUserMessagesWithinMatchWindow,
97
114
  autoCompactThresholdField,
98
- buildCodexAgentEnv,
99
115
  buildCodexTokenUsageContextUsagePayload,
100
116
  buildGitCredentialHelperScript,
101
117
  buildHookOutputPreview,
@@ -122,16 +138,10 @@ import {
122
138
  decodePathSegments,
123
139
  detectAgentQuotaLimit,
124
140
  detectLanguageByPath,
125
- dispatchAspNotification,
126
- execAsync,
127
- execFileAsync,
128
- extractCommandProtectionCommandText,
129
141
  extractErrorText,
130
142
  extractToolResultText,
131
143
  fetchAiGatewayModels,
132
144
  fetchModelsDevCatalog,
133
- findGitCommitSignals,
134
- findPrMergeSignals,
135
145
  getChatHistoryPageSenders,
136
146
  getChatHistoryPageWindow,
137
147
  getChatTranscriptHistorySize,
@@ -187,14 +197,10 @@ import {
187
197
  parseGitRemote,
188
198
  parseLatestCodexAspTranscriptTurn,
189
199
  parseMcpToolName,
190
- parsePosixEnvFile,
191
200
  parseReplicasConfigString,
192
201
  percentage,
193
- putPresignedFile,
194
202
  raceWithTimeout,
195
203
  readJsonlPage,
196
- readReplicasRuntimeEnv,
197
- recoverCompletedTurn,
198
204
  renumberCodexAspTranscriptItems,
199
205
  resolveWarmHookConfig,
200
206
  sanitizeCanvasFilename,
@@ -202,164 +208,18 @@ import {
202
208
  serializeCanvasContentResponse,
203
209
  shellQuotePosix,
204
210
  stripAgentDiagnosticErrors
205
- } from "./chunk-UNES5UCP.js";
211
+ } from "./chunk-LPJ6WYD4.js";
206
212
 
207
213
  // src/index.ts
208
214
  import { serve } from "@hono/node-server";
209
215
  import { Hono as Hono2 } from "hono";
210
- import { existsSync as existsSync12 } from "fs";
216
+ import { existsSync as existsSync13 } from "fs";
211
217
  import { randomUUID as randomUUID11 } from "crypto";
212
218
  import { connect } from "net";
213
219
 
214
220
  // src/managers/github-token-manager.ts
215
221
  import path from "path";
216
222
 
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
223
  // src/services/credential-fallbacks.ts
364
224
  var fallbacksByAgent = /* @__PURE__ */ new Map();
365
225
  var exhaustedByAgent = /* @__PURE__ */ new Map();
@@ -519,69 +379,10 @@ var BaseRefreshManager = class {
519
379
  }
520
380
  };
521
381
 
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
382
  // src/utils/file.ts
582
383
  import { randomUUID } from "crypto";
583
384
  import { mkdir, readFile, rename, unlink, writeFile } from "fs/promises";
584
- import { dirname, join as join3 } from "path";
385
+ import { dirname, join } from "path";
585
386
 
586
387
  // src/utils/async-lock.ts
587
388
  var AsyncLock = class {
@@ -614,8 +415,8 @@ async function writeSecureCredentialFile(filePath, content, options) {
614
415
  await atomicWriteFile(filePath, content, { mode: 384 });
615
416
  }
616
417
  async function configureManagedGitCredentialHelper(homeDir) {
617
- const credentialsPath = join3(homeDir, ".git-credentials");
618
- const helperPath = join3(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
418
+ const credentialsPath = join(homeDir, ".git-credentials");
419
+ const helperPath = join(homeDir, GIT_CREDENTIAL_HELPER_FILENAME);
619
420
  await atomicWriteFile(helperPath, buildGitCredentialHelperScript(credentialsPath), { mode: 448 });
620
421
  await execFileAsync("git", ["config", "--global", "credential.helper", helperPath], {
621
422
  env: { ...process.env, HOME: homeDir }
@@ -650,15 +451,15 @@ function removeCredentialFileLines(filePath, shouldRemove) {
650
451
  // src/git/service.ts
651
452
  import { readdir, readFile as readFile3, stat } from "fs/promises";
652
453
  import { spawn } from "child_process";
653
- import { join as join6 } from "path";
454
+ import { join as join4 } from "path";
654
455
 
655
456
  // src/utils/state.ts
656
457
  import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
657
458
  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");
459
+ import { join as join2 } from "path";
460
+ import { homedir } from "os";
461
+ var STATE_DIR = join2(homedir(), ".replicas");
462
+ var STATE_FILE = join2(STATE_DIR, "engine-state.json");
662
463
  var DEFAULT_STATE = {
663
464
  repos: {}
664
465
  };
@@ -755,8 +556,8 @@ async function saveRepoState(repoName, state, fallbackState) {
755
556
  }
756
557
 
757
558
  // src/git/commands.ts
758
- import { readFileSync as readFileSync3 } from "fs";
759
- import { join as join5 } from "path";
559
+ import { readFileSync } from "fs";
560
+ import { join as join3 } from "path";
760
561
  async function runGitCommand(args, cwd, options = {}) {
761
562
  const { stdout } = await execFileAsync("git", args, {
762
563
  cwd,
@@ -767,7 +568,7 @@ async function runGitCommand(args, cwd, options = {}) {
767
568
  }
768
569
  function readRepoHeadBranch(repoPath) {
769
570
  try {
770
- const contents = readFileSync3(join5(repoPath, ".git", "HEAD"), "utf-8").trim();
571
+ const contents = readFileSync(join3(repoPath, ".git", "HEAD"), "utf-8").trim();
771
572
  const match = contents.match(/^ref:\s+refs\/heads\/(.+)$/);
772
573
  return match ? match[1] : null;
773
574
  } catch {
@@ -829,13 +630,13 @@ var GitService = class {
829
630
  const repos = [];
830
631
  let complete = true;
831
632
  for (const entry of entries) {
832
- const fullPath = join6(root, entry);
633
+ const fullPath = join4(root, entry);
833
634
  try {
834
635
  const entryStat = await stat(fullPath);
835
636
  if (!entryStat.isDirectory()) {
836
637
  continue;
837
638
  }
838
- const hasGit = Boolean(await this.safeStat(join6(fullPath, ".git")));
639
+ const hasGit = Boolean(await this.safeStat(join4(fullPath, ".git")));
839
640
  if (!hasGit) {
840
641
  continue;
841
642
  }
@@ -1026,7 +827,7 @@ var GitService = class {
1026
827
  let total = 0;
1027
828
  for (const path6 of paths) {
1028
829
  try {
1029
- const contents = await readFile3(join6(repoPath, path6));
830
+ const contents = await readFile3(join4(repoPath, path6));
1030
831
  if (contents.length === 0 || contents.includes(0)) {
1031
832
  continue;
1032
833
  }
@@ -1214,7 +1015,7 @@ var GitService = class {
1214
1015
  }
1215
1016
  async getGitLabAccessToken(host) {
1216
1017
  try {
1217
- const credentials = await readFile3(join6(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
1018
+ const credentials = await readFile3(join4(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
1218
1019
  for (const line of credentials.split("\n")) {
1219
1020
  const trimmed = line.trim();
1220
1021
  if (!trimmed) continue;
@@ -1761,8 +1562,8 @@ var infisicalTokenManager = new InfisicalTokenManager();
1761
1562
 
1762
1563
  // src/utils/logger.ts
1763
1564
  import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
1764
- import { homedir as homedir4 } from "os";
1765
- import { join as join7 } from "path";
1565
+ import { homedir as homedir2 } from "os";
1566
+ import { join as join5 } from "path";
1766
1567
  import { format } from "util";
1767
1568
  import { randomBytes } from "crypto";
1768
1569
 
@@ -1837,7 +1638,7 @@ var StreamWriter = class {
1837
1638
  };
1838
1639
 
1839
1640
  // src/utils/logger.ts
1840
- var LOG_DIR = join7(homedir4(), ".replicas", "logs");
1641
+ var LOG_DIR = join5(homedir2(), ".replicas", "logs");
1841
1642
  var EngineLogger = class {
1842
1643
  _sessionId = null;
1843
1644
  patched = false;
@@ -1848,7 +1649,7 @@ var EngineLogger = class {
1848
1649
  async initialize() {
1849
1650
  await mkdir3(LOG_DIR, { recursive: true });
1850
1651
  this._sessionId = this.createSessionId();
1851
- const logPath = join7(LOG_DIR, `${this._sessionId}.log`);
1652
+ const logPath = join5(LOG_DIR, `${this._sessionId}.log`);
1852
1653
  await writeFile2(logPath, `=== Replicas Engine Session ${this._sessionId} ===
1853
1654
  `, "utf-8");
1854
1655
  this.writer.open(logPath);
@@ -1895,32 +1696,32 @@ var engineLogger = new EngineLogger();
1895
1696
  // src/services/replicas-config-service.ts
1896
1697
  import { readFile as readFile6, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
1897
1698
  import { existsSync as existsSync3 } from "fs";
1898
- import { join as join11 } from "path";
1899
- import { homedir as homedir8 } from "os";
1699
+ import { join as join9 } from "path";
1700
+ import { homedir as homedir6 } from "os";
1900
1701
  import { spawn as spawn2 } from "child_process";
1901
1702
 
1902
1703
  // src/services/environment-details-service.ts
1903
1704
  import { mkdir as mkdir4, readFile as readFile4 } from "fs/promises";
1904
1705
  import { existsSync as existsSync2 } from "fs";
1905
- import { homedir as homedir6 } from "os";
1906
- import { join as join9 } from "path";
1706
+ import { homedir as homedir4 } from "os";
1707
+ import { join as join7 } from "path";
1907
1708
 
1908
1709
  // 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");
1710
+ import { homedir as homedir3 } from "os";
1711
+ import { join as join6 } from "path";
1712
+ var OPENCODE_AUTH_PATH = join6(homedir3(), ".local", "share", "opencode", "auth.json");
1713
+ var PI_AUTH_PATH = join6(homedir3(), ".pi", "agent", "auth.json");
1714
+ var DEEPSEEK_AUTH_PATH = join6(homedir3(), ".replicas", "deepseek-auth.json");
1914
1715
 
1915
1716
  // src/managers/deepseek-auth.ts
1916
- import { readFileSync as readFileSync4 } from "fs";
1717
+ import { readFileSync as readFileSync2 } from "fs";
1917
1718
  import { z } from "zod";
1918
1719
  var authSchema = z.object({
1919
1720
  openrouter: z.object({ type: z.literal("api"), key: z.string().min(1) })
1920
1721
  });
1921
1722
  function getDeepseekApiKey() {
1922
1723
  try {
1923
- const parsed = authSchema.safeParse(JSON.parse(readFileSync4(DEEPSEEK_AUTH_PATH, "utf8")));
1724
+ const parsed = authSchema.safeParse(JSON.parse(readFileSync2(DEEPSEEK_AUTH_PATH, "utf8")));
1924
1725
  return parsed.success ? parsed.data.openrouter.key : null;
1925
1726
  } catch {
1926
1727
  return null;
@@ -1933,11 +1734,11 @@ function detectCodexAuthMethod() {
1933
1734
  }
1934
1735
 
1935
1736
  // 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");
1737
+ var REPLICAS_DIR = join7(homedir4(), ".replicas");
1738
+ var DETAILS_FILE = join7(REPLICAS_DIR, "environment-details.json");
1739
+ var CLAUDE_CREDENTIALS_PATH = join7(homedir4(), ".claude", ".credentials.json");
1740
+ var GH_HOSTS_PATH = join7(homedir4(), ".config", "gh", "hosts.yml");
1741
+ var GIT_CREDENTIALS_PATH = join7(homedir4(), ".git-credentials");
1941
1742
  function detectClaudeAuthMethod() {
1942
1743
  if (existsSync2(CLAUDE_CREDENTIALS_PATH)) {
1943
1744
  return "oauth";
@@ -2149,8 +1950,8 @@ var environmentDetailsService = new EnvironmentDetailsService();
2149
1950
 
2150
1951
  // src/services/start-hook-logs-service.ts
2151
1952
  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";
1953
+ import { homedir as homedir5 } from "os";
1954
+ import { join as join8 } from "path";
2154
1955
 
2155
1956
  // src/services/hook-log-files.ts
2156
1957
  import { createHash } from "crypto";
@@ -2162,7 +1963,7 @@ function repoHookLogFilename(repoName) {
2162
1963
  }
2163
1964
 
2164
1965
  // src/services/start-hook-logs-service.ts
2165
- var LOGS_DIR = join10(homedir7(), ".replicas", "start-hook-logs");
1966
+ var LOGS_DIR = join8(homedir5(), ".replicas", "start-hook-logs");
2166
1967
  function withPreview(stored) {
2167
1968
  const preview = buildHookOutputPreview(stored.output);
2168
1969
  return { ...stored, ...preview };
@@ -2200,7 +2001,7 @@ var StartHookLogsService = class {
2200
2001
  await this.ensureDir();
2201
2002
  const log = { hookType, hookName, repoName: hookName, ...entry };
2202
2003
  const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
2203
- await writeFile3(join10(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
2004
+ await writeFile3(join8(LOGS_DIR, filename), `${JSON.stringify(log, null, 2)}
2204
2005
  `, "utf-8");
2205
2006
  }
2206
2007
  async saveEnvironmentLog(entry) {
@@ -2225,7 +2026,7 @@ var StartHookLogsService = class {
2225
2026
  continue;
2226
2027
  }
2227
2028
  try {
2228
- const raw = await readFile5(join10(LOGS_DIR, file), "utf-8");
2029
+ const raw = await readFile5(join8(LOGS_DIR, file), "utf-8");
2229
2030
  const stored = normalizeStored(JSON.parse(raw));
2230
2031
  if (stored) {
2231
2032
  logs.push(withPreview(stored));
@@ -2244,7 +2045,7 @@ var StartHookLogsService = class {
2244
2045
  async getFullOutput(hookType, hookName) {
2245
2046
  const filename = hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
2246
2047
  try {
2247
- const raw = await readFile5(join10(LOGS_DIR, filename), "utf-8");
2048
+ const raw = await readFile5(join8(LOGS_DIR, filename), "utf-8");
2248
2049
  const stored = normalizeStored(JSON.parse(raw));
2249
2050
  if (!stored || stored.hookType !== hookType || stored.hookName !== hookName) {
2250
2051
  return null;
@@ -2261,7 +2062,7 @@ var StartHookLogsService = class {
2261
2062
  var startHookLogsService = new StartHookLogsService();
2262
2063
 
2263
2064
  // src/services/replicas-config-service.ts
2264
- var START_HOOKS_LOG = join11(homedir8(), ".replicas", "startHooks.log");
2065
+ var START_HOOKS_LOG = join9(homedir6(), ".replicas", "startHooks.log");
2265
2066
  var START_HOOKS_RUNNING_PROMPT = `IMPORTANT - Start Hooks Running:
2266
2067
  Start hooks are shell commands/scripts set by repository owners that run on workspace startup.
2267
2068
  These hooks are currently executing in the background. You can:
@@ -2272,7 +2073,7 @@ The start hooks may install dependencies, build projects, or perform other setup
2272
2073
  If your task depends on setup being complete, check the log file before proceeding.`;
2273
2074
  async function readReplicasConfigFromDir(dirPath) {
2274
2075
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
2275
- const configPath = join11(dirPath, filename);
2076
+ const configPath = join9(dirPath, filename);
2276
2077
  if (!existsSync3(configPath)) {
2277
2078
  continue;
2278
2079
  }
@@ -2342,7 +2143,7 @@ var ReplicasConfigService = class {
2342
2143
  const logLine = `[${timestamp}] ${message}
2343
2144
  `;
2344
2145
  try {
2345
- await mkdir6(join11(homedir8(), ".replicas"), { recursive: true });
2146
+ await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
2346
2147
  await appendFile(START_HOOKS_LOG, logLine, "utf-8");
2347
2148
  } catch (error) {
2348
2149
  console.error("Failed to write to start hooks log:", error);
@@ -2354,7 +2155,7 @@ var ReplicasConfigService = class {
2354
2155
  command: params.content,
2355
2156
  label: "env start hook",
2356
2157
  repoName: "environment",
2357
- cwd: homedir8(),
2158
+ cwd: homedir6(),
2358
2159
  timeout: params.timeoutMs ?? DEFAULT_START_HOOK_TIMEOUT_MS,
2359
2160
  onOutputChunk: params.onOutputChunk
2360
2161
  });
@@ -2477,7 +2278,7 @@ var ReplicasConfigService = class {
2477
2278
  this.hooksCompleted = false;
2478
2279
  this.hooksFailed = false;
2479
2280
  try {
2480
- await mkdir6(join11(homedir8(), ".replicas"), { recursive: true });
2281
+ await mkdir6(join9(homedir6(), ".replicas"), { recursive: true });
2481
2282
  await writeFile4(
2482
2283
  START_HOOKS_LOG,
2483
2284
  `=== Start Hooks Execution Log ===
@@ -2672,11 +2473,11 @@ var replicasConfigService = new ReplicasConfigService();
2672
2473
 
2673
2474
  // src/services/event-service.ts
2674
2475
  import { mkdir as mkdir7 } from "fs/promises";
2675
- import { homedir as homedir9 } from "os";
2676
- import { join as join12 } from "path";
2476
+ import { homedir as homedir7 } from "os";
2477
+ import { join as join10 } from "path";
2677
2478
  import { randomUUID as randomUUID2 } from "crypto";
2678
- var ENGINE_DIR = join12(homedir9(), ".replicas", "engine");
2679
- var EVENTS_FILE = join12(ENGINE_DIR, "events.jsonl");
2479
+ var ENGINE_DIR = join10(homedir7(), ".replicas", "engine");
2480
+ var EVENTS_FILE = join10(ENGINE_DIR, "events.jsonl");
2680
2481
  var EventService = class {
2681
2482
  subscribers = /* @__PURE__ */ new Map();
2682
2483
  writer = new StreamWriter();
@@ -2710,9 +2511,9 @@ var eventService = new EventService();
2710
2511
  import { mkdir as mkdir8, readFile as readFile7 } from "fs/promises";
2711
2512
  import { existsSync as existsSync4 } from "fs";
2712
2513
  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");
2514
+ import { homedir as homedir8 } from "os";
2515
+ import { dirname as dirname2, join as join11 } from "path";
2516
+ var PREVIEW_PORTS_FILE = join11(homedir8(), ".replicas", "preview-ports.json");
2716
2517
  async function readPreviewsFile() {
2717
2518
  try {
2718
2519
  if (!existsSync4(PREVIEW_PORTS_FILE)) {
@@ -2822,9 +2623,9 @@ async function registerDesktopPreview() {
2822
2623
  }
2823
2624
 
2824
2625
  // 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";
2626
+ import { existsSync as existsSync10 } from "fs";
2627
+ import { appendFile as appendFile4, copyFile as copyFile2, mkdir as mkdir18, readFile as readFile19, rename as rename3, rm as rm3 } from "fs/promises";
2628
+ import { join as join28 } from "path";
2828
2629
  import { randomUUID as randomUUID9 } from "crypto";
2829
2630
 
2830
2631
  // ../shared/src/workspace-chat.ts
@@ -2957,9 +2758,9 @@ import {
2957
2758
  query
2958
2759
  } from "@anthropic-ai/claude-agent-sdk";
2959
2760
  import { randomUUID as randomUUID5 } from "crypto";
2960
- import { dirname as dirname4, join as join17 } from "path";
2761
+ import { dirname as dirname4, join as join15 } from "path";
2961
2762
  import { mkdir as mkdir10 } from "fs/promises";
2962
- import { homedir as homedir12 } from "os";
2763
+ import { homedir as homedir10 } from "os";
2963
2764
 
2964
2765
  // src/utils/linear-converter.ts
2965
2766
  function linearThoughtToResponse(thought) {
@@ -3344,8 +3145,8 @@ function extractPlanFromCodexAspNotification(notification) {
3344
3145
  // src/utils/image-utils.ts
3345
3146
  import { randomUUID as randomUUID4 } from "crypto";
3346
3147
  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";
3148
+ import { homedir as homedir9 } from "os";
3149
+ import { join as join12 } from "path";
3349
3150
  function isImageMediaType(value) {
3350
3151
  return IMAGE_MEDIA_TYPES.includes(value);
3351
3152
  }
@@ -3445,14 +3246,14 @@ async function normalizeImages(images) {
3445
3246
  }
3446
3247
  return normalized;
3447
3248
  }
3448
- async function saveNormalizedImagesToTempFiles(images, tempImageDir = join14(homedir11(), ".replicas", "codex", "temp-images")) {
3249
+ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(homedir9(), ".replicas", "codex", "temp-images")) {
3449
3250
  await mkdir9(tempImageDir, { recursive: true });
3450
3251
  const tempPaths = [];
3451
3252
  try {
3452
3253
  for (const image of images) {
3453
3254
  const ext = image.source.media_type.split("/")[1] || "png";
3454
3255
  const filename = `img_${randomUUID4()}.${ext}`;
3455
- const filepath = join14(tempImageDir, filename);
3256
+ const filepath = join12(tempImageDir, filename);
3456
3257
  await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
3457
3258
  tempPaths.push(filepath);
3458
3259
  }
@@ -3467,8 +3268,8 @@ async function removeTempImageFiles(paths) {
3467
3268
  }
3468
3269
 
3469
3270
  // src/managers/coding-agent-manager.ts
3470
- import { readFileSync as readFileSync5 } from "fs";
3471
- import { join as join15 } from "path";
3271
+ import { readFileSync as readFileSync3 } from "fs";
3272
+ import { join as join13 } from "path";
3472
3273
 
3473
3274
  // src/managers/auth-fallback.ts
3474
3275
  var SWAPPABLE_AGENTS = {
@@ -4012,7 +3813,7 @@ var CodingAgentManager = class {
4012
3813
  buildMemoryInstruction() {
4013
3814
  const readSummary = (scope) => {
4014
3815
  try {
4015
- return readFileSync5(join15(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
3816
+ return readFileSync3(join13(MEMORY_ROOT, scope, MEMORY_SUMMARY_FILENAME), "utf8").trim();
4016
3817
  } catch {
4017
3818
  return "";
4018
3819
  }
@@ -4023,10 +3824,10 @@ var CodingAgentManager = class {
4023
3824
  const parts = [
4024
3825
  `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
3826
  ];
4026
- if (organization) parts.push(`<replicas-organization-memory root="${join15(MEMORY_ROOT, "organization")}">
3827
+ if (organization) parts.push(`<replicas-organization-memory root="${join13(MEMORY_ROOT, "organization")}">
4027
3828
  ${organization}
4028
3829
  </replicas-organization-memory>`);
4029
- if (seat) parts.push(`<replicas-personal-memory root="${join15(MEMORY_ROOT, "seat")}">
3830
+ if (seat) parts.push(`<replicas-personal-memory root="${join13(MEMORY_ROOT, "seat")}">
4030
3831
  ${seat}
4031
3832
  </replicas-personal-memory>`);
4032
3833
  return parts.join("\n\n");
@@ -4148,67 +3949,9 @@ var LinearEventForwarder = class {
4148
3949
  }
4149
3950
  };
4150
3951
 
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;
4157
- }
4158
- function failClosed(reason, matchedSignals) {
4159
- return {
4160
- allowed: false,
4161
- enabled: true,
4162
- reason,
4163
- matchedSignals
4164
- };
4165
- }
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;
4172
- try {
4173
- response = await monolithRequest("/v1/engine/command-protection/evaluate", {
4174
- body: request,
4175
- signal
4176
- });
4177
- } catch (error) {
4178
- const detail = error instanceof Error ? error.message : String(error);
4179
- return failClosed(`Command protection failed closed: ${detail}`, matchedSignals);
4180
- }
4181
- if (!response.ok) {
4182
- const detail = await response.text().catch(() => "");
4183
- return failClosed(`Command protection failed closed: ${response.status} ${detail}`, matchedSignals);
4184
- }
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);
4191
- }
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
- }
4208
-
4209
3952
  // src/services/skill-registry-service.ts
4210
3953
  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";
3954
+ import { dirname as dirname3, isAbsolute, join as join14, relative, resolve } from "path";
4212
3955
  var REGISTRY_ROOT_DIR = ".replicas/skill-registries";
4213
3956
  var REGISTRY_MANIFEST = "manifest.json";
4214
3957
  async function getSkillRegistryInventory(homeDir) {
@@ -4269,10 +4012,10 @@ async function scanRegistry(registryDir) {
4269
4012
  }
4270
4013
  async function findSkillCollections(registryDir) {
4271
4014
  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") }
4015
+ { source: "skills", root: join14(registryDir, "skills") },
4016
+ { source: "agents", root: join14(registryDir, ".agents", "skills") },
4017
+ { source: "codex", root: join14(registryDir, ".codex", "skills") },
4018
+ { source: "claude", root: join14(registryDir, ".claude", "skills") }
4276
4019
  ];
4277
4020
  const collections = [];
4278
4021
  for (const { source, root } of candidateRoots) {
@@ -4286,9 +4029,9 @@ async function findSkillCollections(registryDir) {
4286
4029
  async function findSkillDirsInRoot(root) {
4287
4030
  const skillDirs = [];
4288
4031
  for (const dirent of await safeReadDir(root)) {
4289
- const skillDir = join16(root, dirent.name);
4032
+ const skillDir = join14(root, dirent.name);
4290
4033
  if (!await isDirectoryDirent(dirent, skillDir)) continue;
4291
- if (await fileExists(join16(skillDir, "SKILL.md"))) {
4034
+ if (await fileExists(join14(skillDir, "SKILL.md"))) {
4292
4035
  skillDirs.push(skillDir);
4293
4036
  }
4294
4037
  }
@@ -4298,9 +4041,9 @@ async function findTopLevelSkills(registryDir) {
4298
4041
  const skills = [];
4299
4042
  for (const dirent of await safeReadDir(registryDir)) {
4300
4043
  if (dirent.name.startsWith(".") || dirent.name === "skills" || dirent.name === "plugins") continue;
4301
- const skillDir = join16(registryDir, dirent.name);
4044
+ const skillDir = join14(registryDir, dirent.name);
4302
4045
  if (!await isDirectoryDirent(dirent, skillDir)) continue;
4303
- if (await fileExists(join16(skillDir, "SKILL.md"))) {
4046
+ if (await fileExists(join14(skillDir, "SKILL.md"))) {
4304
4047
  skills.push({ source: "root", skillDir });
4305
4048
  }
4306
4049
  }
@@ -4311,10 +4054,10 @@ async function findClaudePluginRoots(registryDir) {
4311
4054
  async function walk(dir) {
4312
4055
  for (const dirent of await safeReadDir(dir)) {
4313
4056
  if (dirent.name === ".git") continue;
4314
- const child = join16(dir, dirent.name);
4057
+ const child = join14(dir, dirent.name);
4315
4058
  if (!await isDirectoryDirent(dirent, child)) continue;
4316
4059
  if (dirent.name === ".claude-plugin") {
4317
- if (await fileExists(join16(child, "plugin.json"))) {
4060
+ if (await fileExists(join14(child, "plugin.json"))) {
4318
4061
  roots.push(dirname3(child));
4319
4062
  }
4320
4063
  continue;
@@ -4326,7 +4069,7 @@ async function findClaudePluginRoots(registryDir) {
4326
4069
  return uniqueStrings(roots);
4327
4070
  }
4328
4071
  async function hasCodexMarketplace(registryDir) {
4329
- return await fileExists(join16(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join16(registryDir, ".codex", "plugins", "marketplace.json"));
4072
+ return await fileExists(join14(registryDir, ".agents", "plugins", "marketplace.json")) || await fileExists(join14(registryDir, ".codex", "plugins", "marketplace.json"));
4330
4073
  }
4331
4074
  async function installCodexRegistryPlugins(client2, inventory) {
4332
4075
  const cwds = inventory.codexMarketplaceCwds;
@@ -4368,10 +4111,10 @@ async function readManifest(homeDir) {
4368
4111
  }
4369
4112
  }
4370
4113
  function getRegistryRoot(homeDir) {
4371
- return join16(homeDir, REGISTRY_ROOT_DIR);
4114
+ return join14(homeDir, REGISTRY_ROOT_DIR);
4372
4115
  }
4373
4116
  function getManifestPath(homeDir) {
4374
- return join16(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
4117
+ return join14(getRegistryRoot(homeDir), REGISTRY_MANIFEST);
4375
4118
  }
4376
4119
  function emptyInventory(registryRoot) {
4377
4120
  return {
@@ -4819,7 +4562,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
4819
4562
  authRetrying = false;
4820
4563
  constructor(options) {
4821
4564
  super({ ...options, provider: AGENT.CLAUDE });
4822
- this.historyFilePath = options.historyFilePath ?? join17(homedir12(), ".replicas", "claude", "history.jsonl");
4565
+ this.historyFilePath = options.historyFilePath ?? join15(homedir10(), ".replicas", "claude", "history.jsonl");
4823
4566
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
4824
4567
  this.systemPromptOverride = options.systemPromptOverride;
4825
4568
  this.toolsOverride = options.tools;
@@ -5873,7 +5616,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5873
5616
 
5874
5617
  // src/managers/codex-asp/codex-asp-manager.ts
5875
5618
  import { readdir as readdir4 } from "fs/promises";
5876
- import { join as join19 } from "path";
5619
+ import { join as join17 } from "path";
5877
5620
 
5878
5621
  // src/managers/codex-asp/asp-host.ts
5879
5622
  var hostPromise = null;
@@ -5988,7 +5731,7 @@ var CodexQuotaStatusTracker = class {
5988
5731
  };
5989
5732
 
5990
5733
  // src/managers/codex-asp/mappers.ts
5991
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
5734
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
5992
5735
  var localImageCache = /* @__PURE__ */ new Map();
5993
5736
  var DEFAULT_MODEL = DEFAULT_CODEX_MODEL;
5994
5737
  var THREAD_START_METHOD = "thread/start";
@@ -6116,7 +5859,7 @@ function userImageForLocalPath(path6) {
6116
5859
  const image = {
6117
5860
  type: "image",
6118
5861
  mediaType: inferMediaType(path6),
6119
- data: readFileSync6(path6).toString("base64")
5862
+ data: readFileSync4(path6).toString("base64")
6120
5863
  };
6121
5864
  if (image.data.length > 0) localImageCache.set(path6, image);
6122
5865
  return image;
@@ -6607,19 +6350,19 @@ var TranscriptUpdateCoalescer = class {
6607
6350
  };
6608
6351
 
6609
6352
  // 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");
6353
+ import { homedir as homedir11 } from "os";
6354
+ import { join as join16 } from "path";
6355
+ var ENGINE_DIR2 = join16(homedir11(), ".replicas", "engine");
6356
+ var CHATS_FILE = join16(ENGINE_DIR2, "chats.json");
6357
+ var CLAUDE_HISTORY_DIR = join16(ENGINE_DIR2, "claude-histories");
6358
+ var RELAY_HISTORY_DIR = join16(ENGINE_DIR2, "relay-histories");
6359
+ var CODEX_HISTORY_DIR = join16(ENGINE_DIR2, "codex-histories");
6360
+ var CURSOR_HISTORY_DIR = join16(ENGINE_DIR2, "cursor-histories");
6361
+ var OPENCODE_HISTORY_DIR = join16(ENGINE_DIR2, "opencode-histories");
6362
+ var PI_HISTORY_DIR = join16(ENGINE_DIR2, "pi-histories");
6363
+ var DEEPSEEK_HISTORY_DIR = join16(ENGINE_DIR2, "deepseek-histories");
6364
+ var FX_HISTORY_DIR = join16(ENGINE_DIR2, "fx-histories");
6365
+ var KIMI_HISTORY_DIR = join16(ENGINE_DIR2, "kimi-histories");
6623
6366
  var HISTORY_DIR_BY_PROVIDER = {
6624
6367
  claude: CLAUDE_HISTORY_DIR,
6625
6368
  relay: RELAY_HISTORY_DIR,
@@ -6685,7 +6428,7 @@ async function readCodexAspThreadHistory(threadId) {
6685
6428
  }
6686
6429
  for (const entry of entries) {
6687
6430
  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();
6431
+ const history = await new CodexHistoryFile(join17(CODEX_HISTORY_DIR, entry.name)).load();
6689
6432
  const transcript = history.transcriptsByThreadId.get(threadId);
6690
6433
  if (transcript) {
6691
6434
  return {
@@ -8065,10 +7808,75 @@ var CodexAspManager = class extends CodingAgentManager {
8065
7808
  };
8066
7809
 
8067
7810
  // 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";
7811
+ import { mkdir as mkdir12, readFile as readFile11, readdir as readdir5 } from "fs/promises";
7812
+ import { basename as basename2, dirname as dirname6, extname, join as join19 } from "path";
8070
7813
  import { parse as parseYaml } from "yaml";
8071
7814
  import { Agent as CursorAgent } from "@cursor/sdk";
7815
+
7816
+ // src/services/native-command-protection-hooks.ts
7817
+ import { existsSync as existsSync6 } from "fs";
7818
+ import { mkdir as mkdir11, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
7819
+ import { basename, dirname as dirname5, join as join18 } from "path";
7820
+ import { fileURLToPath } from "url";
7821
+ var moduleDirectory = dirname5(fileURLToPath(import.meta.url));
7822
+ var hookPath = [
7823
+ join18(moduleDirectory, "command-protection-hook.js"),
7824
+ join18(moduleDirectory, "..", "command-protection-hook.ts")
7825
+ ].find(existsSync6);
7826
+ function shellCommand(provider) {
7827
+ if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
7828
+ const args = hookPath.endsWith(".ts") && basename(process.execPath) !== "bun" ? [process.execPath, "--import", "tsx", hookPath, provider] : [process.execPath, hookPath, provider];
7829
+ return args.map((value) => `'${value.replaceAll("'", "'\\''")}'`).join(" ");
7830
+ }
7831
+ function getCommandProtectionHookPath() {
7832
+ if (!hookPath) throw new Error("Replicas command protection hook executable is missing.");
7833
+ return hookPath;
7834
+ }
7835
+ async function ensureCursorCommandProtectionHook() {
7836
+ const path6 = join18(ENGINE_ENV.HOME_DIR, ".cursor", "hooks.json");
7837
+ let config = {};
7838
+ try {
7839
+ const parsed = JSON.parse(await readFile10(path6, "utf8"));
7840
+ if (!isRecord2(parsed)) throw new Error("Invalid Cursor hooks configuration.");
7841
+ config = parsed;
7842
+ } catch (error) {
7843
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
7844
+ }
7845
+ const hooks = isRecord2(config.hooks) ? config.hooks : {};
7846
+ const existing = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
7847
+ hooks.preToolUse = [
7848
+ ...existing.filter((entry) => !isRecord2(entry) || typeof entry.command !== "string" || !entry.command.includes("command-protection-hook")),
7849
+ { command: shellCommand("cursor") }
7850
+ ];
7851
+ await mkdir11(dirname5(path6), { recursive: true });
7852
+ await writeFile6(path6, `${JSON.stringify({ ...config, version: 1, hooks }, null, 2)}
7853
+ `, "utf8");
7854
+ }
7855
+ async function ensureKimiCommandProtectionHook() {
7856
+ const path6 = join18(ENGINE_ENV.HOME_DIR, ".kimi-code", "config.toml");
7857
+ const start = "# Replicas command protection hook";
7858
+ const end = "# End Replicas command protection hook";
7859
+ let config = "";
7860
+ try {
7861
+ config = await readFile10(path6, "utf8");
7862
+ } catch (error) {
7863
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
7864
+ }
7865
+ const block = `${start}
7866
+ [[hooks]]
7867
+ event = "PreToolUse"
7868
+ command = ${JSON.stringify(shellCommand("kimi"))}
7869
+ timeout = 30
7870
+ ${end}`;
7871
+ const markerStart = config.indexOf(start);
7872
+ const markerEnd = config.indexOf(end, markerStart + start.length);
7873
+ config = markerStart >= 0 && markerEnd >= 0 ? `${config.slice(0, markerStart)}${block}${config.slice(markerEnd + end.length)}` : `${config.trimEnd()}${config.trim() ? "\n\n" : ""}${block}
7874
+ `;
7875
+ await mkdir11(dirname5(path6), { recursive: true });
7876
+ await writeFile6(path6, config, "utf8");
7877
+ }
7878
+
7879
+ // src/managers/cursor-manager.ts
8072
7880
  var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
8073
7881
  var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
8074
7882
  var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
@@ -8108,24 +7916,6 @@ function extractCursorCommandDescription(content) {
8108
7916
  }
8109
7917
  return void 0;
8110
7918
  }
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
7919
  async function listCursorCommandsInDirectory(directory) {
8130
7920
  let entries;
8131
7921
  try {
@@ -8136,10 +7926,10 @@ async function listCursorCommandsInDirectory(directory) {
8136
7926
  return [];
8137
7927
  }
8138
7928
  const commands = await Promise.all(entries.filter((entry) => entry.isFile() && extname(entry.name) === ".md").map(async (entry) => {
8139
- const name = basename(entry.name, ".md");
7929
+ const name = basename2(entry.name, ".md");
8140
7930
  let description;
8141
7931
  try {
8142
- description = extractCursorCommandDescription(await readFile10(join20(directory, entry.name), "utf8"));
7932
+ description = extractCursorCommandDescription(await readFile11(join19(directory, entry.name), "utf8"));
8143
7933
  } catch (error) {
8144
7934
  console.warn("[CursorManager] Failed to read slash command file:", error);
8145
7935
  }
@@ -8152,8 +7942,6 @@ var CursorManager = class extends CodingAgentManager {
8152
7942
  activeRun = null;
8153
7943
  activeModel = null;
8154
7944
  abortActiveTurn = null;
8155
- blockedToolCallIds = /* @__PURE__ */ new Set();
8156
- pendingCursorBlockReason = null;
8157
7945
  reconnectNoticeVisible = false;
8158
7946
  instructionsDelivered = false;
8159
7947
  resumeSessionId = null;
@@ -8163,12 +7951,13 @@ var CursorManager = class extends CodingAgentManager {
8163
7951
  slashCommandsRequest = null;
8164
7952
  constructor(options) {
8165
7953
  super(options);
8166
- this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
7954
+ this.historyFilePath = options.historyFilePath ?? join19(ENGINE_ENV.HOME_DIR, ".replicas", "cursor", "history.jsonl");
8167
7955
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
8168
7956
  this.initializeManager(this.processMessageInternal.bind(this));
8169
7957
  }
8170
7958
  async initialize() {
8171
- await mkdir11(dirname5(this.historyFilePath), { recursive: true });
7959
+ await mkdir12(dirname6(this.historyFilePath), { recursive: true });
7960
+ await ensureCursorCommandProtectionHook();
8172
7961
  }
8173
7962
  getHistorySink() {
8174
7963
  return this.historyFile;
@@ -8237,7 +8026,7 @@ var CursorManager = class extends CodingAgentManager {
8237
8026
  this.slashCommandsRequest ??= (async () => {
8238
8027
  try {
8239
8028
  const repoDirectories = await getAgentAdditionalDirectories();
8240
- const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join20(directory, ".cursor", "commands"));
8029
+ const commandDirectories = [this.workingDirectory, ...repoDirectories, ENGINE_ENV.HOME_DIR].map((directory) => join19(directory, ".cursor", "commands"));
8241
8030
  const commands = mergeSlashCommands(
8242
8031
  ...await Promise.all(commandDirectories.map(listCursorCommandsInDirectory))
8243
8032
  );
@@ -8308,17 +8097,12 @@ var CursorManager = class extends CodingAgentManager {
8308
8097
  run = await agent.send(message, {
8309
8098
  model: { id: model },
8310
8099
  mode: request.planMode ? "plan" : "agent",
8311
- onDelta: async (args) => {
8100
+ onDelta: (args) => {
8312
8101
  markCursorActivity();
8313
8102
  this.recordCursorContextUsage(args.update);
8314
- await this.handleCursorToolCall(cursorToolInputFromDelta(args.update));
8315
8103
  }
8316
8104
  });
8317
8105
  this.activeRun = run;
8318
- if (this.pendingCursorBlockReason) {
8319
- await run.cancel();
8320
- throw new Error(this.pendingCursorBlockReason);
8321
- }
8322
8106
  const events = run.stream()[Symbol.asyncIterator]();
8323
8107
  let interrupted = false;
8324
8108
  while (true) {
@@ -8340,9 +8124,6 @@ var CursorManager = class extends CodingAgentManager {
8340
8124
  markCursorActivity();
8341
8125
  const event = next.value;
8342
8126
  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
8127
  if (linearSessionId) {
8347
8128
  linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
8348
8129
  }
@@ -8413,8 +8194,6 @@ var CursorManager = class extends CodingAgentManager {
8413
8194
  this.abortActiveTurn = null;
8414
8195
  this.activeRun = null;
8415
8196
  this.activeModel = null;
8416
- this.blockedToolCallIds.clear();
8417
- this.pendingCursorBlockReason = null;
8418
8197
  await this.historyFile.flush();
8419
8198
  await this.onTurnComplete();
8420
8199
  }
@@ -8434,39 +8213,6 @@ var CursorManager = class extends CodingAgentManager {
8434
8213
  return null;
8435
8214
  }
8436
8215
  }
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
8216
  async toCursorMessage(request, includeInstructions) {
8471
8217
  const instructions = includeInstructions ? this.buildCombinedInstructions(request.customInstructions) : void 0;
8472
8218
  const text = [
@@ -8553,10 +8299,10 @@ ${instructions}
8553
8299
  import { spawn as spawn3 } from "child_process";
8554
8300
  import { randomUUID as randomUUID6 } from "crypto";
8555
8301
  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";
8302
+ import { dirname as dirname7, join as join20 } from "path";
8303
+ import { fileURLToPath as fileURLToPath2 } from "url";
8304
+ import { existsSync as existsSync7 } from "fs";
8305
+ import { mkdir as mkdir13, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
8560
8306
  import { z as z2 } from "zod";
8561
8307
  import WebSocket from "ws";
8562
8308
  import { AbstractApiClient } from "@deepseek-ai/dsh-host-apiproxy/client";
@@ -8568,12 +8314,16 @@ var questionSelectionSchema = z2.record(z2.string(), z2.object({
8568
8314
  custom: z2.string().optional()
8569
8315
  }));
8570
8316
  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));
8317
+ var DSH_BIN = join20(dirname7(require2.resolve("@deepseek-ai/dsh/package.json")), "lib", "bin.js");
8318
+ var MANAGER_DIR = dirname7(fileURLToPath2(import.meta.url));
8573
8319
  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) ?? "";
8320
+ join20(MANAGER_DIR, "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml"),
8321
+ join20(MANAGER_DIR, "..", "..", "..", "scripts", "deepseek", "replicas.cordis.patch.yml")
8322
+ ].find(existsSync7) ?? "";
8323
+ var DSH_COMMAND_PROTECTION_PLUGIN = [
8324
+ join20(MANAGER_DIR, "deepseek-command-protection-plugin.js"),
8325
+ join20(MANAGER_DIR, "..", "deepseek-command-protection-plugin.ts")
8326
+ ].find(existsSync7) ?? "";
8577
8327
  var DeepseekApiClient = class extends AbstractApiClient {
8578
8328
  constructor(baseUrl) {
8579
8329
  super(3e4);
@@ -8686,12 +8436,12 @@ var DeepseekManager = class extends CodingAgentManager {
8686
8436
  constructor(options) {
8687
8437
  super(options);
8688
8438
  this.sessionId = options.initialSessionId ? sessionIdSchema.parse(options.initialSessionId) : null;
8689
- this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
8439
+ this.historyFilePath = options.historyFilePath ?? join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "history.jsonl");
8690
8440
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
8691
8441
  this.initializeManager(this.processMessageInternal.bind(this));
8692
8442
  }
8693
8443
  async initialize() {
8694
- await mkdir12(dirname6(this.historyFilePath), { recursive: true });
8444
+ await mkdir13(dirname7(this.historyFilePath), { recursive: true });
8695
8445
  }
8696
8446
  getHistorySink() {
8697
8447
  return this.historyFile;
@@ -8765,15 +8515,32 @@ var DeepseekManager = class extends CodingAgentManager {
8765
8515
  }
8766
8516
  async ensureClient() {
8767
8517
  if (this.client) return this.client;
8768
- if (!DSH_PATCH) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
8518
+ if (!DSH_PATCH || !DSH_COMMAND_PROTECTION_PLUGIN) throw new Error("DeepSeek Harness configuration is missing from the engine package.");
8519
+ const runtimePatch = join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek", "replicas.cordis.patch.yml");
8520
+ await mkdir13(dirname7(runtimePatch), { recursive: true });
8521
+ await writeFile7(runtimePatch, (await readFile12(DSH_PATCH, "utf8")).replace(
8522
+ "__REPLICAS_DSH_COMMAND_PROTECTION_PLUGIN__",
8523
+ JSON.stringify(DSH_COMMAND_PROTECTION_PLUGIN)
8524
+ ));
8769
8525
  const apiKey = await getOpenRouterKey();
8770
- const child = spawn3(process.execPath, [DSH_BIN, "--profile", "web", "--patch", DSH_PATCH, "--host", "127.0.0.1", "--port", "0"], {
8526
+ const child = spawn3(process.execPath, [
8527
+ ...DSH_COMMAND_PROTECTION_PLUGIN.endsWith(".ts") ? ["--import", "tsx"] : [],
8528
+ DSH_BIN,
8529
+ "--profile",
8530
+ "web",
8531
+ "--patch",
8532
+ runtimePatch,
8533
+ "--host",
8534
+ "127.0.0.1",
8535
+ "--port",
8536
+ "0"
8537
+ ], {
8771
8538
  cwd: this.workingDirectory,
8772
8539
  env: {
8773
8540
  ...process.env,
8774
8541
  DEEPSEEK_API_KEY: apiKey,
8775
8542
  DEEPSEEK_BASE_URL: "https://openrouter.ai/api/v1",
8776
- DSH_HOME: join21(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
8543
+ DSH_HOME: join20(ENGINE_ENV.HOME_DIR, ".replicas", "deepseek"),
8777
8544
  DSH_PERMISSION_MODE: "danger-full-access",
8778
8545
  DSH_TELEMETRY_DISABLED: "true"
8779
8546
  },
@@ -8958,14 +8725,14 @@ ${request.message}` : request.message;
8958
8725
  };
8959
8726
 
8960
8727
  // src/managers/fx-manager.ts
8961
- import { readFile as readFile12 } from "fs/promises";
8728
+ import { readFile as readFile14 } from "fs/promises";
8962
8729
  import { spawn as spawn5 } from "child_process";
8963
8730
 
8964
8731
  // src/managers/acp-manager.ts
8965
8732
  import { spawn as spawn4 } from "child_process";
8966
8733
  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";
8734
+ import { mkdir as mkdir14, readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
8735
+ import { dirname as dirname8 } from "path";
8969
8736
  import { Writable } from "stream";
8970
8737
  import {
8971
8738
  PROTOCOL_VERSION,
@@ -9004,7 +8771,7 @@ var AcpManager = class extends CodingAgentManager {
9004
8771
  this.initializeManager(this.processMessageInternal.bind(this));
9005
8772
  }
9006
8773
  async initialize() {
9007
- await mkdir13(dirname7(this.historyFilePath), { recursive: true });
8774
+ await mkdir14(dirname8(this.historyFilePath), { recursive: true });
9008
8775
  await this.acp.prepare?.();
9009
8776
  }
9010
8777
  getHistorySink() {
@@ -9035,7 +8802,7 @@ var AcpManager = class extends CodingAgentManager {
9035
8802
  });
9036
8803
  const app2 = client({ name: "Replicas" }).onNotification(methods.client.session.update, ({ params }) => this.recordUpdate(params)).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
9037
8804
  this.assertSession(params.sessionId);
9038
- const content = await readFile11(params.path, "utf8");
8805
+ const content = await readFile13(params.path, "utf8");
9039
8806
  if (params.line === void 0 && params.limit === void 0) return { content };
9040
8807
  const start = Math.max(0, (params.line ?? 1) - 1);
9041
8808
  const end = params.limit === null || params.limit === void 0 ? void 0 : start + params.limit;
@@ -9043,7 +8810,7 @@ var AcpManager = class extends CodingAgentManager {
9043
8810
  }).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
9044
8811
  this.assertSession(params.sessionId);
9045
8812
  if (this.planMode) throw new Error("ACP file writes are disabled in plan mode.");
9046
- await writeFile6(params.path, params.content, "utf8");
8813
+ await writeFile8(params.path, params.content, "utf8");
9047
8814
  return {};
9048
8815
  }).onRequest(methods.client.terminal.create, ({ params }) => this.createTerminal(params)).onRequest(methods.client.terminal.output, ({ params }) => {
9049
8816
  this.assertSession(params.sessionId);
@@ -9410,7 +9177,7 @@ var AcpManager = class extends CodingAgentManager {
9410
9177
 
9411
9178
  // src/managers/fx-manager.ts
9412
9179
  async function ensureResolvableDnsConfig() {
9413
- const contents = await readFile12("/etc/resolv.conf");
9180
+ const contents = await readFile14("/etc/resolv.conf");
9414
9181
  if (contents.length === 0 || contents.at(-1) === 10) return;
9415
9182
  await new Promise((resolve5, reject) => {
9416
9183
  const child = spawn5("sudo", ["tee", "-a", "/etc/resolv.conf"], { stdio: ["pipe", "ignore", "pipe"] });
@@ -9477,6 +9244,7 @@ var KimiManager = class extends AcpManager {
9477
9244
  };
9478
9245
  },
9479
9246
  defaultModel: DEFAULT_KIMI_MODEL,
9247
+ prepare: ensureKimiCommandProtectionHook,
9480
9248
  restartOnThinkingChange: true
9481
9249
  }
9482
9250
  });
@@ -9484,11 +9252,11 @@ var KimiManager = class extends AcpManager {
9484
9252
  };
9485
9253
 
9486
9254
  // 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";
9255
+ import { existsSync as existsSync8 } from "fs";
9256
+ import { copyFile, mkdir as mkdir15, readFile as readFile15, rm as rm2 } from "fs/promises";
9257
+ import { delimiter, dirname as dirname9, join as join21 } from "path";
9490
9258
  import { randomBytes as randomBytes2 } from "crypto";
9491
- import { fileURLToPath as fileURLToPath2 } from "url";
9259
+ import { fileURLToPath as fileURLToPath3 } from "url";
9492
9260
  import { Agent } from "undici";
9493
9261
  import { z as z3 } from "zod";
9494
9262
 
@@ -9545,27 +9313,14 @@ async function getAllowedOpenRouterModels() {
9545
9313
  }
9546
9314
 
9547
9315
  // 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");
9316
+ var OPENCODE_SHIM_DIR = dirname9(fileURLToPath3(new URL("../../scripts/opencode", import.meta.url)));
9317
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE = join21(OPENCODE_SHIM_DIR, "opencode-command-protection-plugin.ts");
9318
+ var OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.ts");
9319
+ var OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "plugins", "replicas-command-protection.mjs");
9320
+ var OPENCODE_CONFIG_PATH = join21(ENGINE_ENV.HOME_DIR, ".config", "opencode", "opencode.json");
9550
9321
  var OPENCODE_FETCH_DISPATCHER = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
9551
9322
  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
- };
9323
+ var OPENCODE_WORKSPACE_PERMISSION = "allow";
9569
9324
  var OPENCODE_SLASH_COMMANDS_CACHE_MS = 3e4;
9570
9325
  var OPENCODE_VARIANT_CANDIDATES_BY_THINKING_LEVEL = {
9571
9326
  low: ["low"],
@@ -9581,9 +9336,9 @@ var opencodeAuthSchema = z3.record(z3.string(), z3.object({
9581
9336
  key: z3.string().optional()
9582
9337
  }));
9583
9338
  async function hasOpencodeCredentials(provider) {
9584
- if (!existsSync7(OPENCODE_AUTH_PATH)) return false;
9339
+ if (!existsSync8(OPENCODE_AUTH_PATH)) return false;
9585
9340
  try {
9586
- const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile13(OPENCODE_AUTH_PATH, "utf8")));
9341
+ const auth = opencodeAuthSchema.safeParse(JSON.parse(await readFile15(OPENCODE_AUTH_PATH, "utf8")));
9587
9342
  return auth.success && auth.data[provider]?.type === "api" && Boolean(auth.data[provider]?.key);
9588
9343
  } catch {
9589
9344
  return false;
@@ -9688,7 +9443,7 @@ function isOpencodeMcpEntry(value) {
9688
9443
  async function readProvisionedOpencodeMcpConfig() {
9689
9444
  let raw;
9690
9445
  try {
9691
- raw = await readFile13(OPENCODE_CONFIG_PATH, "utf8");
9446
+ raw = await readFile15(OPENCODE_CONFIG_PATH, "utf8");
9692
9447
  } catch (error) {
9693
9448
  if (isRecord2(error) && error.code === "ENOENT") return void 0;
9694
9449
  console.error("[OpencodeManager] Failed to read Opencode config:", error);
@@ -9746,27 +9501,6 @@ function opencodeFetch(input, init) {
9746
9501
  const requestInit = { ...init, dispatcher: OPENCODE_FETCH_DISPATCHER };
9747
9502
  return fetch(input, requestInit);
9748
9503
  }
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
9504
  var OpencodeManager = class extends CodingAgentManager {
9771
9505
  client = null;
9772
9506
  server = null;
@@ -9787,22 +9521,17 @@ var OpencodeManager = class extends CodingAgentManager {
9787
9521
  forwardedLinearPartKeys = /* @__PURE__ */ new Set();
9788
9522
  handledAccountRateLimit = false;
9789
9523
  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
9524
  slashCommandsCache = null;
9796
9525
  slashCommandsRequest = null;
9797
9526
  constructor(options) {
9798
9527
  super(options);
9799
9528
  this.sessionId = options.initialSessionId;
9800
- this.historyFilePath = options.historyFilePath ?? join22(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
9529
+ this.historyFilePath = options.historyFilePath ?? join21(ENGINE_ENV.HOME_DIR, ".replicas", "opencode", "history.jsonl");
9801
9530
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
9802
9531
  this.initializeManager(this.processMessageInternal.bind(this));
9803
9532
  }
9804
9533
  async initialize() {
9805
- await mkdir14(dirname8(this.historyFilePath), { recursive: true });
9534
+ await mkdir15(dirname9(this.historyFilePath), { recursive: true });
9806
9535
  }
9807
9536
  getHistorySink() {
9808
9537
  return this.historyFile;
@@ -9899,6 +9628,14 @@ var OpencodeManager = class extends CodingAgentManager {
9899
9628
  if (!await hasOpencodeCredentials(providerId)) {
9900
9629
  throw new Error("OpenCode Go, Aster, or OpenRouter credentials are not configured for Opencode in this workspace.");
9901
9630
  }
9631
+ if (!existsSync8(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE)) {
9632
+ throw new Error("Opencode command protection plugin is missing from the engine package.");
9633
+ }
9634
+ await mkdir15(dirname9(OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH), { recursive: true });
9635
+ await copyFile(OPENCODE_COMMAND_PROTECTION_PLUGIN_SOURCE, OPENCODE_COMMAND_PROTECTION_PLUGIN_PATH);
9636
+ await rm2(OPENCODE_LEGACY_COMMAND_PROTECTION_PLUGIN_PATH, { force: true });
9637
+ process.env.REPLICAS_COMMAND_PROTECTION_HOOK_PATH = getCommandProtectionHookPath();
9638
+ process.env.REPLICAS_COMMAND_PROTECTION_RUNTIME_PATH = process.execPath;
9902
9639
  this.eventAbortController?.abort();
9903
9640
  this.server?.close();
9904
9641
  const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
@@ -10032,11 +9769,6 @@ var OpencodeManager = class extends CodingAgentManager {
10032
9769
  this.activeAbortController = null;
10033
9770
  this.activeLinearForwarder = null;
10034
9771
  this.forwardedLinearPartKeys.clear();
10035
- this.handledPermissionRequestIds.clear();
10036
- this.pendingPermissionRequestIds.clear();
10037
- this.handledToolCallIds.clear();
10038
- this.pendingToolCallIds.clear();
10039
- this.opencodeToolInputs.clear();
10040
9772
  await this.historyFile.flush();
10041
9773
  await this.onTurnComplete();
10042
9774
  }
@@ -10074,9 +9806,6 @@ var OpencodeManager = class extends CodingAgentManager {
10074
9806
  if (typeof partSessionID === "string" && this.sessionId && partSessionID !== this.sessionId) return;
10075
9807
  if (event.type === "message.part.updated" && isOpencodePart(part)) {
10076
9808
  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
9809
  return;
10081
9810
  }
10082
9811
  if (event.type === "message.updated") this.recordOpencodeMessageRole(payload);
@@ -10085,22 +9814,6 @@ var OpencodeManager = class extends CodingAgentManager {
10085
9814
  console.error("[OpencodeManager] Failed to stop account-rate-limited turn:", error);
10086
9815
  });
10087
9816
  }
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
9817
  if (this.recordOpencodeNextPart(event.type, payload)) return;
10105
9818
  this.recordHistoryEvent(`opencode-${event.type}`, payload, this.historyFile);
10106
9819
  }
@@ -10113,110 +9826,6 @@ var OpencodeManager = class extends CodingAgentManager {
10113
9826
  this.recordHistoryEvent("opencode-error", { message, code: "account_rate_limit" }, this.historyFile);
10114
9827
  await this.abortActiveTurn();
10115
9828
  }
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
9829
  recordOpencodeMessageRole(payload) {
10221
9830
  const info = isRecord2(payload.info) ? payload.info : null;
10222
9831
  const id = typeof info?.id === "string" ? info.id : void 0;
@@ -10316,8 +9925,8 @@ var OpencodeManager = class extends CodingAgentManager {
10316
9925
  };
10317
9926
 
10318
9927
  // src/managers/pi-manager.ts
10319
- import { mkdir as mkdir15 } from "fs/promises";
10320
- import { dirname as dirname9, join as join23 } from "path";
9928
+ import { mkdir as mkdir16 } from "fs/promises";
9929
+ import { dirname as dirname10, join as join22 } from "path";
10321
9930
  import {
10322
9931
  AuthStorage,
10323
9932
  createAgentSession,
@@ -10425,12 +10034,12 @@ var PiManager = class extends CodingAgentManager {
10425
10034
  providerApiKey = null;
10426
10035
  constructor(options) {
10427
10036
  super(options);
10428
- this.historyFilePath = options.historyFilePath ?? join23(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
10037
+ this.historyFilePath = options.historyFilePath ?? join22(PI_HISTORY_DIR, `${Date.now()}.jsonl`);
10429
10038
  this.historyFile = new CodexHistoryFile(this.historyFilePath);
10430
10039
  this.initializeManager(this.processMessageInternal.bind(this));
10431
10040
  }
10432
10041
  async initialize() {
10433
- await mkdir15(dirname9(this.historyFilePath), { recursive: true });
10042
+ await mkdir16(dirname10(this.historyFilePath), { recursive: true });
10434
10043
  }
10435
10044
  getHistorySink() {
10436
10045
  return this.historyFile;
@@ -10530,7 +10139,7 @@ var PiManager = class extends CodingAgentManager {
10530
10139
  const sessionManager = this.initialSessionId ? SessionManager.open(this.initialSessionId, PI_HISTORY_DIR, this.workingDirectory) : SessionManager.create(this.workingDirectory, PI_HISTORY_DIR);
10531
10140
  const resourceLoader = new DefaultResourceLoader({
10532
10141
  cwd: this.workingDirectory,
10533
- agentDir: join23(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
10142
+ agentDir: join22(ENGINE_ENV.HOME_DIR, ".pi", "agent"),
10534
10143
  extensionFactories: [registerCommandProtection(
10535
10144
  this.workingDirectory,
10536
10145
  this.historyFile,
@@ -11261,14 +10870,14 @@ async function uploadAnalytics(endpoint, body) {
11261
10870
  // src/analytics/analytics-record-buffer.ts
11262
10871
  import {
11263
10872
  appendFile as appendFile3,
11264
- mkdir as mkdir16,
11265
- readFile as readFile14,
10873
+ mkdir as mkdir17,
10874
+ readFile as readFile16,
11266
10875
  readdir as readdir6,
11267
10876
  rename as rename2,
11268
10877
  unlink as unlink3
11269
10878
  } from "fs/promises";
11270
- import { existsSync as existsSync8, readFileSync as readFileSync7, readdirSync } from "fs";
11271
- import { join as join24 } from "path";
10879
+ import { existsSync as existsSync9, readFileSync as readFileSync5, readdirSync } from "fs";
10880
+ import { join as join23 } from "path";
11272
10881
  var MAX_FAILED_RECORDS = 500;
11273
10882
  var FLUSH_DEBOUNCE_MS = 15e3;
11274
10883
  var MAX_UPLOADED_SEGMENTS = 50;
@@ -11280,7 +10889,7 @@ var AnalyticsRecordBuffer = class {
11280
10889
  options.storageName,
11281
10890
  ...options.legacyStorageNames ?? []
11282
10891
  ];
11283
- this.liveFile = join24(ENGINE_DIR2, `${options.storageName}.jsonl`);
10892
+ this.liveFile = join23(ENGINE_DIR2, `${options.storageName}.jsonl`);
11284
10893
  this.segmentFilePatterns = this.storageNames.map(
11285
10894
  (storageName) => new RegExp(`^${storageName}\\.(\\d+)\\.jsonl$`)
11286
10895
  );
@@ -11303,7 +10912,7 @@ var AnalyticsRecordBuffer = class {
11303
10912
  if (this.recordIds.has(recordId)) return;
11304
10913
  this.recordIds.add(recordId);
11305
10914
  }
11306
- const pending = mkdir16(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
10915
+ const pending = mkdir17(ENGINE_DIR2, { recursive: true }).then(() => appendFile3(this.liveFile, `${JSON.stringify(record)}
11307
10916
  `, "utf-8")).catch((error) => {
11308
10917
  console.error(
11309
10918
  `[${this.options.storageName}] Append failed, will retry on flush:`,
@@ -11315,10 +10924,10 @@ var AnalyticsRecordBuffer = class {
11315
10924
  void pending.finally(() => this.pendingAppends.delete(pending));
11316
10925
  }
11317
10926
  readStoredRecords() {
11318
- if (!existsSync8(ENGINE_DIR2)) return [];
10927
+ if (!existsSync9(ENGINE_DIR2)) return [];
11319
10928
  return readdirSync(ENGINE_DIR2, { withFileTypes: true }).filter((entry) => entry.isFile() && this.storageNames.some(
11320
10929
  (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) => {
10930
+ )).map(({ name }) => name).sort().flatMap((name) => this.parseRecords(readFileSync5(join23(ENGINE_DIR2, name), "utf-8"))).map((record) => {
11322
10931
  const recordId = this.options.getRecordId?.(record);
11323
10932
  if (recordId) this.recordIds.add(recordId);
11324
10933
  return record;
@@ -11353,8 +10962,8 @@ var AnalyticsRecordBuffer = class {
11353
10962
  }
11354
10963
  for (const storageName of this.storageNames) {
11355
10964
  await rename2(
11356
- join24(ENGINE_DIR2, `${storageName}.jsonl`),
11357
- join24(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
10965
+ join23(ENGINE_DIR2, `${storageName}.jsonl`),
10966
+ join23(ENGINE_DIR2, `${storageName}.${Date.now()}.jsonl`)
11358
10967
  ).catch(() => {
11359
10968
  });
11360
10969
  }
@@ -11364,7 +10973,7 @@ var AnalyticsRecordBuffer = class {
11364
10973
  for (const entry of entries) {
11365
10974
  if (!this.segmentFilePatterns.some((pattern) => pattern.test(entry))) continue;
11366
10975
  try {
11367
- await this.uploadSegment(join24(ENGINE_DIR2, entry));
10976
+ await this.uploadSegment(join23(ENGINE_DIR2, entry));
11368
10977
  flushed++;
11369
10978
  } catch (error) {
11370
10979
  failed++;
@@ -11393,14 +11002,14 @@ var AnalyticsRecordBuffer = class {
11393
11002
  (storageName) => entry.startsWith(`${storageName}.`)
11394
11003
  ) && entry.endsWith(UPLOADED_SUFFIX)).sort();
11395
11004
  for (const entry of uploaded.slice(0, -MAX_UPLOADED_SEGMENTS)) {
11396
- await unlink3(join24(ENGINE_DIR2, entry)).catch(() => {
11005
+ await unlink3(join23(ENGINE_DIR2, entry)).catch(() => {
11397
11006
  });
11398
11007
  }
11399
11008
  }
11400
11009
  return { flushed, failed };
11401
11010
  }
11402
11011
  async uploadSegment(filePath) {
11403
- const records = this.parseRecords(await readFile14(filePath, "utf-8"));
11012
+ const records = this.parseRecords(await readFile16(filePath, "utf-8"));
11404
11013
  if (records.length > 0) await this.options.upload(records);
11405
11014
  await rename2(filePath, `${filePath}${UPLOADED_SUFFIX}`).catch(() => {
11406
11015
  });
@@ -12186,19 +11795,19 @@ var KeepAliveService = class _KeepAliveService {
12186
11795
  var keepAliveService = new KeepAliveService();
12187
11796
 
12188
11797
  // 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";
11798
+ import { readdir as readdir7, readFile as readFile17, stat as stat3 } from "fs/promises";
11799
+ import { homedir as homedir12 } from "os";
11800
+ import { join as join24 } from "path";
12192
11801
  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")
11802
+ join24(homedir12(), ".claude", "plans"),
11803
+ join24(process.env.XDG_DATA_HOME ?? join24(homedir12(), ".local", "share"), "opencode", "plans"),
11804
+ join24(homedir12(), ".replicas", "canvas")
12196
11805
  ];
12197
11806
  async function canvasDirectories() {
12198
11807
  const repositories = await gitService.listRepositories().catch(() => []);
12199
11808
  return [
12200
11809
  ...GLOBAL_CANVAS_DIRECTORIES,
12201
- ...repositories.map((repository) => join25(repository.path, ".opencode", "plans"))
11810
+ ...repositories.map((repository) => join24(repository.path, ".opencode", "plans"))
12202
11811
  ];
12203
11812
  }
12204
11813
  var CanvasService = class {
@@ -12222,7 +11831,7 @@ var CanvasService = class {
12222
11831
  for (const entry of entries) {
12223
11832
  if (entry.name.startsWith(".")) continue;
12224
11833
  const filename = current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name;
12225
- const filePath = join25(current.directory, entry.name);
11834
+ const filePath = join24(current.directory, entry.name);
12226
11835
  if (entry.isDirectory()) {
12227
11836
  pending.push({ directory: filePath, relativePath: filename });
12228
11837
  continue;
@@ -12247,7 +11856,7 @@ var CanvasService = class {
12247
11856
  if (!safe) return null;
12248
11857
  const { kind, mimeType } = classifyCanvasFilename(safe);
12249
11858
  for (const directory of await this.directories()) {
12250
- const filePath = join25(directory, safe);
11859
+ const filePath = join24(directory, safe);
12251
11860
  let sizeBytes = 0;
12252
11861
  let updatedAt = "";
12253
11862
  try {
@@ -12268,7 +11877,7 @@ var CanvasService = class {
12268
11877
  };
12269
11878
  }
12270
11879
  try {
12271
- const bytes = await readFile15(filePath);
11880
+ const bytes = await readFile17(filePath);
12272
11881
  return { filename: safe, kind, sizeBytes, mimeType, updatedAt, bytes };
12273
11882
  } catch {
12274
11883
  continue;
@@ -12418,14 +12027,14 @@ async function reconcileCanvasItems(filenames) {
12418
12027
  // src/services/upload-chat-transcripts.ts
12419
12028
  import { createReadStream } from "fs";
12420
12029
  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";
12030
+ import { readFile as readFile18, readdir as readdir8, stat as stat4 } from "fs/promises";
12031
+ import { basename as basename3, join as join26 } from "path";
12423
12032
 
12424
12033
  // src/services/chat/chat-senders.ts
12425
- import { join as join26 } from "path";
12426
- var CHAT_SENDERS_DIR = join26(ENGINE_DIR2, "chat-senders");
12034
+ import { join as join25 } from "path";
12035
+ var CHAT_SENDERS_DIR = join25(ENGINE_DIR2, "chat-senders");
12427
12036
  function chatMessageSendersFilePath(chatId) {
12428
- return join26(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
12037
+ return join25(CHAT_SENDERS_DIR, `${chatId}.jsonl`);
12429
12038
  }
12430
12039
  function parseChatMessageSendersJsonl(content) {
12431
12040
  return content.split("\n").flatMap((line) => {
@@ -12441,9 +12050,9 @@ function parseChatMessageSendersJsonl(content) {
12441
12050
 
12442
12051
  // src/services/upload-chat-transcripts.ts
12443
12052
  var HISTORY_DIRS = [
12444
- join27(ENGINE_DIR2, "claude-histories"),
12445
- join27(ENGINE_DIR2, "relay-histories"),
12446
- join27(ENGINE_DIR2, "codex-histories")
12053
+ join26(ENGINE_DIR2, "claude-histories"),
12054
+ join26(ENGINE_DIR2, "relay-histories"),
12055
+ join26(ENGINE_DIR2, "codex-histories")
12447
12056
  ];
12448
12057
  async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
12449
12058
  let flushed = 0;
@@ -12459,9 +12068,9 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
12459
12068
  }
12460
12069
  for (const entry of entries) {
12461
12070
  if (!entry.endsWith(".jsonl")) continue;
12462
- const chatId = basename2(entry, ".jsonl");
12071
+ const chatId = basename3(entry, ".jsonl");
12463
12072
  tasks.push(
12464
- uploadChatTranscript(chatId, join27(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
12073
+ uploadChatTranscript(chatId, join26(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
12465
12074
  flushed++;
12466
12075
  if (artifact && capture) revisions.push(artifact);
12467
12076
  }).catch((err) => {
@@ -12477,7 +12086,7 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), ca
12477
12086
  async function uploadChatTranscript(chatId, filePath, chat, capture) {
12478
12087
  const { size } = await stat4(filePath);
12479
12088
  if (size === 0) return null;
12480
- const historyPages = createChatTranscriptPages(await readFile16(filePath));
12089
+ const historyPages = createChatTranscriptPages(await readFile18(filePath));
12481
12090
  const metadata = chat ? {
12482
12091
  provider: chat.provider,
12483
12092
  credential: ENGINE_ENV.REPLICAS_AGENT_CREDENTIALS[chat.provider],
@@ -12496,7 +12105,7 @@ async function uploadChatTranscript(chatId, filePath, chat, capture) {
12496
12105
  }
12497
12106
  try {
12498
12107
  metadata.senders = parseChatMessageSendersJsonl(
12499
- await readFile16(chatMessageSendersFilePath(chatId), "utf-8")
12108
+ await readFile18(chatMessageSendersFilePath(chatId), "utf-8")
12500
12109
  );
12501
12110
  } catch (error) {
12502
12111
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) throw error;
@@ -12579,7 +12188,7 @@ async function flushRepoState() {
12579
12188
  // src/services/upload-engine-logs.ts
12580
12189
  import { createReadStream as createReadStream2 } from "fs";
12581
12190
  import { readdir as readdir9, stat as stat5 } from "fs/promises";
12582
- import { join as join28 } from "path";
12191
+ import { join as join27 } from "path";
12583
12192
  var MAX_ENGINE_LOG_FLUSH_SESSIONS = 10;
12584
12193
  var MAX_ENGINE_LOG_FLUSH_BYTES = 5 * 1024 * 1024;
12585
12194
  var ENGINE_LOG_FLUSH_TIMEOUT_MS = 2e4;
@@ -12608,7 +12217,7 @@ async function flushAllEngineLogs() {
12608
12217
  const candidates = (await Promise.all(filenames.slice(0, MAX_ENGINE_LOG_FLUSH_SESSIONS).map(async (filename) => {
12609
12218
  try {
12610
12219
  const sessionId = filename.slice(0, -".log".length);
12611
- const filePath = join28(LOG_DIR, filename);
12220
+ const filePath = join27(LOG_DIR, filename);
12612
12221
  const fileStat = await runBeforeDeadline(() => stat5(filePath), deadline);
12613
12222
  if (!fileStat.isFile()) {
12614
12223
  skipped++;
@@ -12709,7 +12318,7 @@ function isCodexAvailable() {
12709
12318
  return detectCodexAuthMethod() !== "none";
12710
12319
  }
12711
12320
  function isOpencodeAvailable() {
12712
- return existsSync9(OPENCODE_AUTH_PATH);
12321
+ return existsSync10(OPENCODE_AUTH_PATH);
12713
12322
  }
12714
12323
  function isDeepseekAvailable() {
12715
12324
  return Boolean(getDeepseekApiKey());
@@ -12721,7 +12330,7 @@ function isKimiAvailable() {
12721
12330
  return Boolean(ENGINE_ENV.AI_GATEWAY_API_KEY);
12722
12331
  }
12723
12332
  function isPiAvailable() {
12724
- return existsSync9(PI_AUTH_PATH);
12333
+ return existsSync10(PI_AUTH_PATH);
12725
12334
  }
12726
12335
  function isCursorAvailable() {
12727
12336
  return Boolean(ENGINE_ENV.CURSOR_API_KEY);
@@ -12860,13 +12469,13 @@ var ChatService = class {
12860
12469
  persistPromise = null;
12861
12470
  persistQueued = false;
12862
12471
  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 });
12472
+ await mkdir18(ENGINE_DIR2, { recursive: true });
12473
+ await mkdir18(CLAUDE_HISTORY_DIR, { recursive: true });
12474
+ await mkdir18(RELAY_HISTORY_DIR, { recursive: true });
12475
+ await mkdir18(CODEX_HISTORY_DIR, { recursive: true });
12476
+ await mkdir18(CURSOR_HISTORY_DIR, { recursive: true });
12477
+ await mkdir18(OPENCODE_HISTORY_DIR, { recursive: true });
12478
+ await mkdir18(CHAT_SENDERS_DIR, { recursive: true });
12870
12479
  const persisted = await this.loadChats();
12871
12480
  for (const chat of persisted) {
12872
12481
  const runtime = this.createRuntimeChat(chat);
@@ -13066,7 +12675,7 @@ var ChatService = class {
13066
12675
  }
13067
12676
  }
13068
12677
  async readSenders(chatId) {
13069
- return readFile17(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
12678
+ return readFile19(chatMessageSendersFilePath(chatId), "utf-8").then(parseChatMessageSendersJsonl).catch((error) => {
13070
12679
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
13071
12680
  console.error("[ChatService] Failed to read sender records:", error);
13072
12681
  return [];
@@ -13305,13 +12914,13 @@ var ChatService = class {
13305
12914
  return descendants;
13306
12915
  }
13307
12916
  async deleteHistoryFile(persisted) {
13308
- const historyPath = join29(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
12917
+ const historyPath = join28(HISTORY_DIR_BY_PROVIDER[persisted.provider], `${persisted.id}.jsonl`);
13309
12918
  await Promise.all([
13310
12919
  historyPath,
13311
12920
  `${historyPath}.pages.jsonl`,
13312
12921
  `${historyPath}.pages.index.json`
13313
- ].map((path6) => rm2(path6, { force: true })));
13314
- await rm2(chatMessageSendersFilePath(persisted.id), { force: true });
12922
+ ].map((path6) => rm3(path6, { force: true })));
12923
+ await rm3(chatMessageSendersFilePath(persisted.id), { force: true });
13315
12924
  }
13316
12925
  async getChatHistory(chatId, page = {}) {
13317
12926
  const chat = this.requireChat(chatId);
@@ -13436,7 +13045,7 @@ var ChatService = class {
13436
13045
  if (persisted.provider === "claude") {
13437
13046
  provider = new ClaudeManager({
13438
13047
  workingDirectory: this.workingDirectory,
13439
- historyFilePath: join29(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13048
+ historyFilePath: join28(CLAUDE_HISTORY_DIR, `${persisted.id}.jsonl`),
13440
13049
  initialSessionId: persisted.providerSessionId,
13441
13050
  onSaveSessionId: saveSession,
13442
13051
  onTurnComplete: onProviderTurnComplete,
@@ -13456,7 +13065,7 @@ var ChatService = class {
13456
13065
  });
13457
13066
  provider = new RelayManager({
13458
13067
  workingDirectory: this.workingDirectory,
13459
- historyFilePath: join29(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13068
+ historyFilePath: join28(RELAY_HISTORY_DIR, `${persisted.id}.jsonl`),
13460
13069
  initialSessionId: persisted.providerSessionId,
13461
13070
  onSaveSessionId: saveSession,
13462
13071
  onTurnComplete: onProviderTurnComplete,
@@ -13470,7 +13079,7 @@ var ChatService = class {
13470
13079
  } else if (persisted.provider === "cursor") {
13471
13080
  provider = new CursorManager({
13472
13081
  workingDirectory: this.workingDirectory,
13473
- historyFilePath: join29(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13082
+ historyFilePath: join28(CURSOR_HISTORY_DIR, `${persisted.id}.jsonl`),
13474
13083
  initialSessionId: persisted.providerSessionId,
13475
13084
  onSaveSessionId: saveSession,
13476
13085
  onTurnComplete: onProviderTurnComplete,
@@ -13482,7 +13091,7 @@ var ChatService = class {
13482
13091
  } else if (persisted.provider === "deepseek") {
13483
13092
  provider = new DeepseekManager({
13484
13093
  workingDirectory: this.workingDirectory,
13485
- historyFilePath: join29(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
13094
+ historyFilePath: join28(DEEPSEEK_HISTORY_DIR, `${persisted.id}.jsonl`),
13486
13095
  initialSessionId: persisted.providerSessionId,
13487
13096
  onSaveSessionId: saveSession,
13488
13097
  onTurnComplete: onProviderTurnComplete,
@@ -13494,7 +13103,7 @@ var ChatService = class {
13494
13103
  } else if (persisted.provider === "fx") {
13495
13104
  provider = new FxManager({
13496
13105
  workingDirectory: this.workingDirectory,
13497
- historyFilePath: join29(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
13106
+ historyFilePath: join28(FX_HISTORY_DIR, `${persisted.id}.jsonl`),
13498
13107
  initialSessionId: persisted.providerSessionId,
13499
13108
  onSaveSessionId: saveSession,
13500
13109
  onTurnComplete: onProviderTurnComplete,
@@ -13505,7 +13114,7 @@ var ChatService = class {
13505
13114
  } else if (persisted.provider === "kimi") {
13506
13115
  provider = new KimiManager({
13507
13116
  workingDirectory: this.workingDirectory,
13508
- historyFilePath: join29(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
13117
+ historyFilePath: join28(KIMI_HISTORY_DIR, `${persisted.id}.jsonl`),
13509
13118
  initialSessionId: persisted.providerSessionId,
13510
13119
  onSaveSessionId: saveSession,
13511
13120
  onTurnComplete: onProviderTurnComplete,
@@ -13516,7 +13125,7 @@ var ChatService = class {
13516
13125
  } else if (persisted.provider === "opencode") {
13517
13126
  provider = new OpencodeManager({
13518
13127
  workingDirectory: this.workingDirectory,
13519
- historyFilePath: join29(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13128
+ historyFilePath: join28(OPENCODE_HISTORY_DIR, `${persisted.id}.jsonl`),
13520
13129
  initialSessionId: persisted.providerSessionId,
13521
13130
  onSaveSessionId: saveSession,
13522
13131
  onTurnComplete: onProviderTurnComplete,
@@ -13528,7 +13137,7 @@ var ChatService = class {
13528
13137
  } else if (persisted.provider === "pi") {
13529
13138
  provider = new PiManager({
13530
13139
  workingDirectory: this.workingDirectory,
13531
- historyFilePath: join29(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
13140
+ historyFilePath: join28(PI_HISTORY_DIR, `${persisted.id}.jsonl`),
13532
13141
  initialSessionId: persisted.providerSessionId,
13533
13142
  onSaveSessionId: saveSession,
13534
13143
  onTurnComplete: onProviderTurnComplete,
@@ -13545,7 +13154,7 @@ var ChatService = class {
13545
13154
  );
13546
13155
  provider = new CodexAspManager({
13547
13156
  workingDirectory: this.workingDirectory,
13548
- historyFilePath: join29(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13157
+ historyFilePath: join28(CODEX_HISTORY_DIR, `${persisted.id}.jsonl`),
13549
13158
  initialSessionId: persisted.providerSessionId,
13550
13159
  onSaveSessionId: saveSession,
13551
13160
  onTurnComplete: onProviderTurnComplete,
@@ -13753,7 +13362,7 @@ var ChatService = class {
13753
13362
  });
13754
13363
  uploadChatTranscript(
13755
13364
  chatId,
13756
- join29(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13365
+ join28(HISTORY_DIR_BY_PROVIDER[chat.persisted.provider], `${chatId}.jsonl`),
13757
13366
  this.toSummary(chat)
13758
13367
  ).catch((err) => {
13759
13368
  console.error("[ChatService] Failed to upload chat transcript:", { chatId, err });
@@ -13769,7 +13378,7 @@ var ChatService = class {
13769
13378
  }
13770
13379
  async loadChats() {
13771
13380
  try {
13772
- const content = await readFile17(CHATS_FILE, "utf-8");
13381
+ const content = await readFile19(CHATS_FILE, "utf-8");
13773
13382
  return parsePersistedChatsContent(content);
13774
13383
  } catch (error) {
13775
13384
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -13784,7 +13393,7 @@ var ChatService = class {
13784
13393
  console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
13785
13394
  }
13786
13395
  try {
13787
- const backupContent = await readFile17(CHATS_BACKUP_FILE, "utf-8");
13396
+ const backupContent = await readFile19(CHATS_BACKUP_FILE, "utf-8");
13788
13397
  return parsePersistedChatsContent(backupContent);
13789
13398
  } catch (backupError) {
13790
13399
  if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
@@ -13805,7 +13414,7 @@ var ChatService = class {
13805
13414
  this.persistQueued = false;
13806
13415
  const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
13807
13416
  try {
13808
- await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
13417
+ await copyFile2(CHATS_FILE, CHATS_BACKUP_FILE);
13809
13418
  } catch (error) {
13810
13419
  if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
13811
13420
  console.error("[ChatService] Failed to update chats backup:", error);
@@ -13888,8 +13497,8 @@ var ChatService = class {
13888
13497
 
13889
13498
  // src/services/repo-file-service.ts
13890
13499
  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";
13500
+ import { readFile as readFile20, realpath, stat as stat6 } from "fs/promises";
13501
+ import { join as join29, resolve as resolve2, extname as extname2 } from "path";
13893
13502
  var CACHE_TTL_MS = 3e4;
13894
13503
  var SEARCH_TIMEOUT_MS = 15e3;
13895
13504
  var MAX_CONTENT_BYTES = 256 * 1024;
@@ -13941,11 +13550,11 @@ function scoreMatch(query2, filePath) {
13941
13550
  const lowerQuery = query2.toLowerCase();
13942
13551
  const lowerPath = filePath.toLowerCase();
13943
13552
  const segments = lowerPath.split("/");
13944
- const basename3 = segments[segments.length - 1] ?? "";
13945
- if (basename3 === lowerQuery) return 100;
13946
- if (basename3.startsWith(lowerQuery)) return 90;
13553
+ const basename4 = segments[segments.length - 1] ?? "";
13554
+ if (basename4 === lowerQuery) return 100;
13555
+ if (basename4.startsWith(lowerQuery)) return 90;
13947
13556
  if (segments.some((seg) => seg === lowerQuery)) return 80;
13948
- if (basename3.includes(lowerQuery)) return 70;
13557
+ if (basename4.includes(lowerQuery)) return 70;
13949
13558
  if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
13950
13559
  if (lowerPath.includes(lowerQuery)) return 50;
13951
13560
  return 0;
@@ -14041,7 +13650,7 @@ var RepoFileService = class {
14041
13650
  const repo = repos.find((r) => r.name === repoName);
14042
13651
  if (!repo) return null;
14043
13652
  try {
14044
- const fullPath = await realpath(resolve2(join30(repo.path, filePath)));
13653
+ const fullPath = await realpath(resolve2(join29(repo.path, filePath)));
14045
13654
  const repoRoot = await realpath(repo.path);
14046
13655
  const repoPrefix = repoRoot.endsWith("/") ? repoRoot : repoRoot + "/";
14047
13656
  if (!fullPath.startsWith(repoPrefix) && fullPath !== repoRoot) return null;
@@ -14072,7 +13681,7 @@ var RepoFileService = class {
14072
13681
  sizeBytes,
14073
13682
  binary: true,
14074
13683
  tooLarge: false,
14075
- base64: (await readFile18(fullPath)).toString("base64"),
13684
+ base64: (await readFile20(fullPath)).toString("base64"),
14076
13685
  mimeType
14077
13686
  };
14078
13687
  }
@@ -14097,7 +13706,7 @@ var RepoFileService = class {
14097
13706
  tooLarge: true
14098
13707
  };
14099
13708
  }
14100
- const content = await readFile18(fullPath, "utf-8");
13709
+ const content = await readFile20(fullPath, "utf-8");
14101
13710
  return {
14102
13711
  repoName,
14103
13712
  path: filePath,
@@ -14175,21 +13784,21 @@ var RepoFileService = class {
14175
13784
  // src/v1-routes.ts
14176
13785
  import { Hono } from "hono";
14177
13786
  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";
13787
+ import { readdir as readdir11, stat as stat7, readFile as readFile23 } from "fs/promises";
13788
+ import { join as join32, resolve as resolve3 } from "path";
14180
13789
 
14181
13790
  // src/services/warm-hooks-service.ts
14182
13791
  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";
13792
+ import { readFile as readFile22 } from "fs/promises";
13793
+ import { existsSync as existsSync11 } from "fs";
13794
+ import { join as join31 } from "path";
14186
13795
 
14187
13796
  // 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");
13797
+ import { mkdir as mkdir19, readFile as readFile21, writeFile as writeFile9, readdir as readdir10, appendFile as appendFile5, unlink as unlink4 } from "fs/promises";
13798
+ import { homedir as homedir13 } from "os";
13799
+ import { join as join30 } from "path";
13800
+ var LOGS_DIR2 = join30(homedir13(), ".replicas", "warm-hook-logs");
13801
+ var CURRENT_RUN_LOG = join30(LOGS_DIR2, "current-run.log");
14193
13802
  var GLOBAL_FILENAME = "global.json";
14194
13803
  function withPreview2(stored) {
14195
13804
  const preview = buildHookOutputPreview(stored.output);
@@ -14197,7 +13806,7 @@ function withPreview2(stored) {
14197
13806
  }
14198
13807
  var WarmHookLogsService = class {
14199
13808
  async ensureDir() {
14200
- await mkdir18(LOGS_DIR2, { recursive: true });
13809
+ await mkdir19(LOGS_DIR2, { recursive: true });
14201
13810
  }
14202
13811
  async saveGlobalHookLog(entry) {
14203
13812
  await this.ensureDir();
@@ -14206,7 +13815,7 @@ var WarmHookLogsService = class {
14206
13815
  hookName: "organization",
14207
13816
  ...entry
14208
13817
  };
14209
- await writeFile7(join31(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
13818
+ await writeFile9(join30(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
14210
13819
  `, "utf-8");
14211
13820
  }
14212
13821
  async saveEnvironmentHookLog(entry) {
@@ -14216,7 +13825,7 @@ var WarmHookLogsService = class {
14216
13825
  hookName: "environment",
14217
13826
  ...entry
14218
13827
  };
14219
- await writeFile7(join31(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
13828
+ await writeFile9(join30(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
14220
13829
  `, "utf-8");
14221
13830
  }
14222
13831
  async saveRepoHookLog(repoName, entry) {
@@ -14226,7 +13835,7 @@ var WarmHookLogsService = class {
14226
13835
  hookName: repoName,
14227
13836
  ...entry
14228
13837
  };
14229
- await writeFile7(join31(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
13838
+ await writeFile9(join30(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
14230
13839
  `, "utf-8");
14231
13840
  }
14232
13841
  async getAllLogs() {
@@ -14245,7 +13854,7 @@ var WarmHookLogsService = class {
14245
13854
  continue;
14246
13855
  }
14247
13856
  try {
14248
- const raw = await readFile19(join31(LOGS_DIR2, file), "utf-8");
13857
+ const raw = await readFile21(join30(LOGS_DIR2, file), "utf-8");
14249
13858
  const stored = JSON.parse(raw);
14250
13859
  logs.push(withPreview2(stored));
14251
13860
  } catch {
@@ -14274,7 +13883,7 @@ var WarmHookLogsService = class {
14274
13883
  }
14275
13884
  async getCurrentRunLog() {
14276
13885
  try {
14277
- return await readFile19(CURRENT_RUN_LOG, "utf-8");
13886
+ return await readFile21(CURRENT_RUN_LOG, "utf-8");
14278
13887
  } catch (err) {
14279
13888
  if (err.code === "ENOENT") return null;
14280
13889
  throw err;
@@ -14283,7 +13892,7 @@ var WarmHookLogsService = class {
14283
13892
  async getFullOutput(hookType, hookName) {
14284
13893
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
14285
13894
  try {
14286
- const raw = await readFile19(join31(LOGS_DIR2, filename), "utf-8");
13895
+ const raw = await readFile21(join30(LOGS_DIR2, filename), "utf-8");
14287
13896
  const stored = JSON.parse(raw);
14288
13897
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
14289
13898
  return null;
@@ -14302,12 +13911,12 @@ var warmHookLogsService = new WarmHookLogsService();
14302
13911
  // src/services/warm-hooks-service.ts
14303
13912
  async function readRepoWarmHook(repoPath) {
14304
13913
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
14305
- const configPath = join32(repoPath, filename);
14306
- if (!existsSync10(configPath)) {
13914
+ const configPath = join31(repoPath, filename);
13915
+ if (!existsSync11(configPath)) {
14307
13916
  continue;
14308
13917
  }
14309
13918
  try {
14310
- const raw = await readFile20(configPath, "utf-8");
13919
+ const raw = await readFile22(configPath, "utf-8");
14311
13920
  const config = parseReplicasConfigString(raw, filename);
14312
13921
  if (!config.warmHook) {
14313
13922
  return null;
@@ -14564,7 +14173,7 @@ ${combinedScript}` : combinedScript;
14564
14173
 
14565
14174
  // src/services/terminal-service.ts
14566
14175
  import { randomUUID as randomUUID10 } from "crypto";
14567
- import { existsSync as existsSync11 } from "fs";
14176
+ import { existsSync as existsSync12 } from "fs";
14568
14177
  import { spawn as spawn7 } from "node-pty";
14569
14178
  var MAX_REPLAY_CHARS = 1024 * 1024;
14570
14179
  var MAX_TERMINAL_SESSIONS = 8;
@@ -14583,7 +14192,7 @@ var TerminalService = class {
14583
14192
  });
14584
14193
  }
14585
14194
  const id = randomUUID10();
14586
- const shell = process.env.SHELL && existsSync11(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
14195
+ const shell = process.env.SHELL && existsSync12(process.env.SHELL) ? process.env.SHELL : "/bin/bash";
14587
14196
  const pty = spawn7(shell, ["-l"], {
14588
14197
  name: "xterm-256color",
14589
14198
  cols,
@@ -15514,7 +15123,7 @@ data: ${JSON.stringify("Terminal session not found")}
15514
15123
  const logFiles = files.filter((f) => f.endsWith(".log"));
15515
15124
  const sessions = await Promise.all(
15516
15125
  logFiles.map(async (filename) => {
15517
- const filePath = join33(LOG_DIR, filename);
15126
+ const filePath = join32(LOG_DIR, filename);
15518
15127
  const fileStat = await stat7(filePath);
15519
15128
  const sessionId = filename.replace(/\.log$/, "");
15520
15129
  return {
@@ -15549,7 +15158,7 @@ data: ${JSON.stringify("Terminal session not found")}
15549
15158
  }
15550
15159
  let content;
15551
15160
  try {
15552
- content = await readFile21(filePath, "utf-8");
15161
+ content = await readFile23(filePath, "utf-8");
15553
15162
  } catch {
15554
15163
  return c.json(jsonError("Log session not found"), 404);
15555
15164
  }
@@ -15620,17 +15229,17 @@ var HeartbeatService = class _HeartbeatService {
15620
15229
  var heartbeatService = new HeartbeatService();
15621
15230
 
15622
15231
  // 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";
15232
+ import { cp, mkdir as mkdir20 } from "fs/promises";
15233
+ import { dirname as dirname11, resolve as resolve4 } from "path";
15234
+ import { fileURLToPath as fileURLToPath4 } from "url";
15626
15235
  async function installWorkspaceSdk() {
15627
- const source = resolve4(dirname10(fileURLToPath3(import.meta.url)), "../../workspace-sdk");
15236
+ const source = resolve4(dirname11(fileURLToPath4(import.meta.url)), "../../workspace-sdk");
15628
15237
  const targets = [
15629
15238
  resolve4(ENGINE_ENV.HOME_DIR, "node_modules/@replicas/sdk"),
15630
15239
  "/tmp/node_modules/@replicas/sdk"
15631
15240
  ];
15632
15241
  await Promise.all(targets.map(async (target) => {
15633
- await mkdir19(target, { recursive: true });
15242
+ await mkdir20(target, { recursive: true });
15634
15243
  await cp(source, target, { recursive: true, force: true });
15635
15244
  }));
15636
15245
  }
@@ -15658,7 +15267,7 @@ async function timeStartupStep(name, fn) {
15658
15267
  async function waitForInitializationGate() {
15659
15268
  if (!ENGINE_ENV.REPLICAS_ENGINE_DEFER_INITIALIZATION) return;
15660
15269
  const deadline = Date.now() + ENGINE_INIT_GATE_TIMEOUT_MS;
15661
- while (!existsSync12(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
15270
+ while (!existsSync13(SANDBOX_PATHS.ENGINE_INIT_GATE)) {
15662
15271
  if (Date.now() >= deadline) {
15663
15272
  throw new Error("Timed out waiting for deferred engine initialization gate");
15664
15273
  }