replicas-engine 0.1.231 → 0.1.232

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.
Files changed (2) hide show
  1. package/dist/src/index.js +103 -46
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -1683,17 +1683,25 @@ function clampWarmHookTimeoutMs(timeoutMs) {
1683
1683
  }
1684
1684
  return Math.min(timeoutMs, MAX_WARM_HOOK_TIMEOUT_MS);
1685
1685
  }
1686
- function buildHookOutputPreview(text, maxChars = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS) {
1686
+ function buildOutputPreview(text, {
1687
+ maxChars = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS,
1688
+ truncateFrom = "head",
1689
+ marker = "\n...[truncated \u2014 download the full log to see the rest]"
1690
+ } = {}) {
1687
1691
  if (text.length <= maxChars) {
1688
1692
  return { output: text, outputTruncated: false, outputTotalChars: text.length };
1689
1693
  }
1694
+ const previewChars = Math.max(0, maxChars - marker.length);
1695
+ const output = previewChars === 0 ? marker.slice(0, maxChars) : truncateFrom === "tail" ? `${marker}${text.slice(-previewChars)}` : `${text.slice(0, previewChars)}${marker}`;
1690
1696
  return {
1691
- output: `${text.slice(0, maxChars)}
1692
- ...[truncated \u2014 download the full log to see the rest]`,
1697
+ output,
1693
1698
  outputTruncated: true,
1694
1699
  outputTotalChars: text.length
1695
1700
  };
1696
1701
  }
1702
+ function buildHookOutputPreview(text, maxChars = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS) {
1703
+ return buildOutputPreview(text, { maxChars });
1704
+ }
1697
1705
  function parseWarmHookConfig(value, filename = "replicas.json") {
1698
1706
  if (typeof value === "string") {
1699
1707
  return value;
@@ -1784,7 +1792,7 @@ function isClaudeAuthErrorText(text) {
1784
1792
  }
1785
1793
 
1786
1794
  // ../shared/src/engine/environment.ts
1787
- var DAYTONA_SNAPSHOT_ID = "29-05-2026-royal-york-v2";
1795
+ var DAYTONA_SNAPSHOT_ID = "29-05-2026-royal-york-v3";
1788
1796
 
1789
1797
  // ../shared/src/engine/types.ts
1790
1798
  var DEFAULT_CHAT_TITLES = {
@@ -2435,11 +2443,24 @@ import { execFileSync as execFileSync2, spawnSync } from "child_process";
2435
2443
  import { join as join5 } from "path";
2436
2444
 
2437
2445
  // src/utils/state.ts
2438
- import { readFile, writeFile, mkdir, rename, unlink } from "fs/promises";
2446
+ import { readFile, mkdir } from "fs/promises";
2439
2447
  import { existsSync } from "fs";
2440
2448
  import { join as join3 } from "path";
2441
2449
  import { homedir as homedir3 } from "os";
2442
2450
 
2451
+ // src/utils/file.ts
2452
+ import { rename, unlink, writeFile } from "fs/promises";
2453
+ async function atomicWriteFile(path4, data) {
2454
+ const tmpFile = `${path4}.${process.pid}.${Date.now()}.tmp`;
2455
+ try {
2456
+ await writeFile(tmpFile, data, "utf-8");
2457
+ await rename(tmpFile, path4);
2458
+ } catch (error) {
2459
+ await unlink(tmpFile).catch(() => void 0);
2460
+ throw error;
2461
+ }
2462
+ }
2463
+
2443
2464
  // src/utils/type-guards.ts
2444
2465
  function isRecord4(value) {
2445
2466
  return typeof value === "object" && value !== null;
@@ -2462,14 +2483,7 @@ async function updateEngineState(updater) {
2462
2483
  await mkdir(STATE_DIR, { recursive: true });
2463
2484
  const currentState = await loadEngineState();
2464
2485
  const nextState = updater(currentState);
2465
- const tmpFile = `${STATE_FILE}.${process.pid}.tmp`;
2466
- try {
2467
- await writeFile(tmpFile, JSON.stringify(nextState, null, 2), "utf-8");
2468
- await rename(tmpFile, STATE_FILE);
2469
- } catch (err) {
2470
- await unlink(tmpFile).catch(() => void 0);
2471
- throw err;
2472
- }
2486
+ await atomicWriteFile(STATE_FILE, JSON.stringify(nextState, null, 2));
2473
2487
  });
2474
2488
  }
2475
2489
  function isEngineRepoDiff(value) {
@@ -3059,14 +3073,14 @@ var EngineLogger = class {
3059
3073
  var engineLogger = new EngineLogger();
3060
3074
 
3061
3075
  // src/services/replicas-config-service.ts
3062
- import { readFile as readFile4, appendFile as appendFile2, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
3076
+ import { readFile as readFile4, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
3063
3077
  import { existsSync as existsSync4 } from "fs";
3064
3078
  import { join as join9 } from "path";
3065
3079
  import { homedir as homedir7 } from "os";
3066
3080
  import { spawn } from "child_process";
3067
3081
 
3068
3082
  // src/services/environment-details-service.ts
3069
- import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
3083
+ import { mkdir as mkdir3, readFile as readFile2 } from "fs/promises";
3070
3084
  import { existsSync as existsSync3 } from "fs";
3071
3085
  import { homedir as homedir5 } from "os";
3072
3086
  import { join as join7 } from "path";
@@ -3154,8 +3168,8 @@ async function readDetails() {
3154
3168
  }
3155
3169
  async function writeDetails(details) {
3156
3170
  await mkdir3(REPLICAS_DIR, { recursive: true });
3157
- await writeFile3(DETAILS_FILE, `${JSON.stringify(details, null, 2)}
3158
- `, "utf-8");
3171
+ await atomicWriteFile(DETAILS_FILE, `${JSON.stringify(details, null, 2)}
3172
+ `);
3159
3173
  }
3160
3174
  var EnvironmentDetailsService = class {
3161
3175
  async getDetails() {
@@ -3240,7 +3254,7 @@ var environmentDetailsService = new EnvironmentDetailsService();
3240
3254
 
3241
3255
  // src/services/start-hook-logs-service.ts
3242
3256
  import { createHash } from "crypto";
3243
- import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4, readdir as readdir2 } from "fs/promises";
3257
+ import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
3244
3258
  import { homedir as homedir6 } from "os";
3245
3259
  import { join as join8 } from "path";
3246
3260
  var LOGS_DIR = join8(homedir6(), ".replicas", "start-hook-logs");
@@ -3263,7 +3277,7 @@ var StartHookLogsService = class {
3263
3277
  async saveRepoLog(repoName, entry) {
3264
3278
  await this.ensureDir();
3265
3279
  const log = { repoName, ...entry };
3266
- await writeFile4(join8(LOGS_DIR, repoFilename(repoName)), `${JSON.stringify(log, null, 2)}
3280
+ await writeFile3(join8(LOGS_DIR, repoFilename(repoName)), `${JSON.stringify(log, null, 2)}
3267
3281
  `, "utf-8");
3268
3282
  }
3269
3283
  async getAllLogs() {
@@ -3527,7 +3541,7 @@ var ReplicasConfigService = class {
3527
3541
  this.hooksFailed = false;
3528
3542
  try {
3529
3543
  await mkdir5(join9(homedir7(), ".replicas"), { recursive: true });
3530
- await writeFile5(
3544
+ await writeFile4(
3531
3545
  START_HOOKS_LOG,
3532
3546
  `=== Start Hooks Execution Log ===
3533
3547
  Started: ${(/* @__PURE__ */ new Date()).toISOString()}
@@ -3753,7 +3767,7 @@ var EventService = class {
3753
3767
  var eventService = new EventService();
3754
3768
 
3755
3769
  // src/services/preview-service.ts
3756
- import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
3770
+ import { mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
3757
3771
  import { existsSync as existsSync5 } from "fs";
3758
3772
  import { randomUUID as randomUUID2 } from "crypto";
3759
3773
  import { homedir as homedir9 } from "os";
@@ -3773,8 +3787,8 @@ async function readPreviewsFile() {
3773
3787
  async function writePreviewsFile(data) {
3774
3788
  const dir = dirname(PREVIEW_PORTS_FILE);
3775
3789
  await mkdir7(dir, { recursive: true });
3776
- await writeFile6(PREVIEW_PORTS_FILE, `${JSON.stringify(data, null, 2)}
3777
- `, "utf-8");
3790
+ await atomicWriteFile(PREVIEW_PORTS_FILE, `${JSON.stringify(data, null, 2)}
3791
+ `);
3778
3792
  }
3779
3793
  var PreviewService = class {
3780
3794
  async initialize() {
@@ -3826,7 +3840,7 @@ var previewService = new PreviewService();
3826
3840
 
3827
3841
  // src/services/chat/chat-service.ts
3828
3842
  import { existsSync as existsSync7 } from "fs";
3829
- import { appendFile as appendFile5, mkdir as mkdir11, readFile as readFile8, rm, writeFile as writeFile9 } from "fs/promises";
3843
+ import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
3830
3844
  import { homedir as homedir13 } from "os";
3831
3845
  import { join as join15 } from "path";
3832
3846
  import { randomUUID as randomUUID5 } from "crypto";
@@ -4379,7 +4393,7 @@ function extractPlanFromCodexAspNotification(notification) {
4379
4393
 
4380
4394
  // src/utils/image-utils.ts
4381
4395
  import { randomUUID as randomUUID3 } from "crypto";
4382
- import { mkdir as mkdir8, unlink as unlink2, writeFile as writeFile7 } from "fs/promises";
4396
+ import { mkdir as mkdir8, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
4383
4397
  import { homedir as homedir10 } from "os";
4384
4398
  import { join as join12 } from "path";
4385
4399
  function isImageMediaType(value) {
@@ -4467,7 +4481,7 @@ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(hom
4467
4481
  const ext = image.source.media_type.split("/")[1] || "png";
4468
4482
  const filename = `img_${randomUUID3()}.${ext}`;
4469
4483
  const filepath = join12(tempImageDir, filename);
4470
- await writeFile7(filepath, Buffer.from(image.source.data, "base64"));
4484
+ await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
4471
4485
  tempPaths.push(filepath);
4472
4486
  }
4473
4487
  } catch (error) {
@@ -5965,6 +5979,7 @@ var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
5965
5979
  var TURN_START_METHOD = "turn/start";
5966
5980
  var TURN_INTERRUPT_METHOD = "turn/interrupt";
5967
5981
  var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
5982
+ var MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS;
5968
5983
  function toReasoningEffort(thinkingLevel) {
5969
5984
  return codexReasoningEffortForThinkingLevel(thinkingLevel);
5970
5985
  }
@@ -5986,7 +6001,7 @@ function timestampFromMilliseconds(value) {
5986
6001
  }
5987
6002
  function stringifyToolOutput(value) {
5988
6003
  if (value === null || value === void 0) return void 0;
5989
- if (typeof value === "string") return value;
6004
+ if (typeof value === "string") return truncateCodexAspTranscriptOutput(value);
5990
6005
  if (typeof value === "object" && "content" in value && Array.isArray(value.content)) {
5991
6006
  const text = value.content.map((item) => {
5992
6007
  if (typeof item === "string") return item;
@@ -5995,14 +6010,24 @@ function stringifyToolOutput(value) {
5995
6010
  }
5996
6011
  return "";
5997
6012
  }).filter(Boolean).join("\n");
5998
- if (text) return text;
6013
+ if (text) return truncateCodexAspTranscriptOutput(text);
5999
6014
  }
6000
6015
  try {
6001
- return JSON.stringify(value);
6016
+ return truncateCodexAspTranscriptOutput(JSON.stringify(value));
6002
6017
  } catch {
6003
- return String(value);
6018
+ return truncateCodexAspTranscriptOutput(String(value));
6004
6019
  }
6005
6020
  }
6021
+ function truncateCodexAspTranscriptOutput(output, maxChars = MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS) {
6022
+ return buildOutputPreview(output, {
6023
+ maxChars,
6024
+ truncateFrom: "tail",
6025
+ marker: "\n[output truncated: showing tail of output]\n"
6026
+ }).output;
6027
+ }
6028
+ function appendCodexAspTranscriptOutput(current, delta, maxChars = MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS) {
6029
+ return truncateCodexAspTranscriptOutput(`${current ?? ""}${delta}`, maxChars);
6030
+ }
6006
6031
  function transcriptPatchOperation(change) {
6007
6032
  return {
6008
6033
  action: change.kind.type,
@@ -6049,7 +6074,7 @@ function itemToTranscriptItem(item, timestamp, status) {
6049
6074
  type: "commandExecution",
6050
6075
  id: item.id,
6051
6076
  command: item.command,
6052
- ...item.aggregatedOutput ? { output: item.aggregatedOutput } : {},
6077
+ ...item.aggregatedOutput ? { output: truncateCodexAspTranscriptOutput(item.aggregatedOutput) } : {},
6053
6078
  exitCode,
6054
6079
  timestamp,
6055
6080
  status: normalizeCodexAspTranscriptStatus(item.status, typeof exitCode === "number" && exitCode !== 0)
@@ -6073,7 +6098,7 @@ function itemToTranscriptItem(item, timestamp, status) {
6073
6098
  server: item.server,
6074
6099
  tool: item.tool,
6075
6100
  input: item.arguments,
6076
- output: item.error?.message ?? stringifyToolOutput(item.result),
6101
+ output: item.error?.message ? truncateCodexAspTranscriptOutput(item.error.message) : stringifyToolOutput(item.result),
6077
6102
  timestamp,
6078
6103
  status: normalizeCodexAspTranscriptStatus(item.status, item.status === "failed")
6079
6104
  };
@@ -7087,7 +7112,7 @@ var CodexAspManager = class extends CodingAgentManager {
7087
7112
  if (item.type !== "commandExecution" && item.type !== "fileChange") return;
7088
7113
  turn.items[itemIndex] = {
7089
7114
  ...item,
7090
- output: `${item.output ?? ""}${delta}`,
7115
+ output: appendCodexAspTranscriptOutput(item.output, delta),
7091
7116
  status: "in_progress"
7092
7117
  };
7093
7118
  this.codexAspTranscript.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -7189,7 +7214,7 @@ var CodexAspManager = class extends CodingAgentManager {
7189
7214
 
7190
7215
  // src/managers/codex-manager.ts
7191
7216
  import { Codex } from "@openai/codex-sdk";
7192
- import { readdir as readdir3, stat as stat2, writeFile as writeFile8, mkdir as mkdir10, readFile as readFile7 } from "fs/promises";
7217
+ import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as readFile7 } from "fs/promises";
7193
7218
  import { existsSync as existsSync6 } from "fs";
7194
7219
  import { join as join14 } from "path";
7195
7220
  import { homedir as homedir12 } from "os";
@@ -7293,7 +7318,7 @@ var CodexManager = class extends CodingAgentManager {
7293
7318
  delete config.developer_instructions;
7294
7319
  }
7295
7320
  const tomlContent = stringifyToml(config);
7296
- await writeFile8(CODEX_CONFIG_PATH, tomlContent, "utf-8");
7321
+ await writeFile6(CODEX_CONFIG_PATH, tomlContent, "utf-8");
7297
7322
  console.log("[CodexManager] Updated config.toml with developer_instructions");
7298
7323
  } catch (error) {
7299
7324
  console.error("[CodexManager] Failed to update config.toml:", error);
@@ -8204,6 +8229,7 @@ var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR2, "claude-histories");
8204
8229
  var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
8205
8230
  var CHAT_SENDERS_DIR = join15(ENGINE_DIR2, "chat-senders");
8206
8231
  var CODEX_AUTH_PATH2 = join15(homedir13(), ".codex", "auth.json");
8232
+ var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
8207
8233
  function isChatMessageSender(value) {
8208
8234
  if (!isRecord4(value)) return false;
8209
8235
  return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
@@ -8258,6 +8284,16 @@ function normalizePersistedChat(chat) {
8258
8284
  ...chat.provider === "codex" ? { codexBackend: codexBackendForChat(chat) } : {}
8259
8285
  };
8260
8286
  }
8287
+ function parsePersistedChatsContent(content) {
8288
+ const parsed = JSON.parse(content);
8289
+ if (!Array.isArray(parsed)) {
8290
+ return [];
8291
+ }
8292
+ return parsed.filter((entry) => isPersistedChat(entry)).map((entry) => normalizePersistedChat(entry));
8293
+ }
8294
+ function corruptChatsFilePath() {
8295
+ return `${CHATS_FILE}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
8296
+ }
8261
8297
  function createUserMessageEvent(message, messageId) {
8262
8298
  return {
8263
8299
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8749,23 +8785,44 @@ var ChatService = class {
8749
8785
  async loadChats() {
8750
8786
  try {
8751
8787
  const content = await readFile8(CHATS_FILE, "utf-8");
8752
- const parsed = JSON.parse(content);
8753
- if (!Array.isArray(parsed)) {
8754
- return [];
8755
- }
8756
- return parsed.filter((entry) => isPersistedChat(entry)).map((entry) => normalizePersistedChat(entry));
8788
+ return parsePersistedChatsContent(content);
8757
8789
  } catch (error) {
8758
8790
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
8759
8791
  return [];
8760
8792
  }
8761
- throw error;
8793
+ const quarantinePath = corruptChatsFilePath();
8794
+ console.error(`[ChatService] Failed to load ${CHATS_FILE}; quarantining and trying backup:`, error);
8795
+ try {
8796
+ await rename2(CHATS_FILE, quarantinePath);
8797
+ console.error(`[ChatService] Quarantined corrupt chats file at ${quarantinePath}`);
8798
+ } catch (renameError) {
8799
+ console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
8800
+ }
8801
+ try {
8802
+ const backupContent = await readFile8(CHATS_BACKUP_FILE, "utf-8");
8803
+ return parsePersistedChatsContent(backupContent);
8804
+ } catch (backupError) {
8805
+ if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
8806
+ return [];
8807
+ }
8808
+ console.error(`[ChatService] Failed to load backup ${CHATS_BACKUP_FILE}; starting with defaults:`, backupError);
8809
+ return [];
8810
+ }
8762
8811
  }
8763
8812
  }
8764
8813
  async persistAllChats() {
8765
8814
  this.writeChain = this.writeChain.catch(() => {
8766
8815
  }).then(async () => {
8767
8816
  const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
8768
- await writeFile9(CHATS_FILE, JSON.stringify(payload, null, 2), "utf-8");
8817
+ try {
8818
+ await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
8819
+ } catch (error) {
8820
+ if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
8821
+ console.error("[ChatService] Failed to update chats backup:", error);
8822
+ }
8823
+ }
8824
+ await atomicWriteFile(CHATS_FILE, `${JSON.stringify(payload, null, 2)}
8825
+ `);
8769
8826
  });
8770
8827
  await this.writeChain;
8771
8828
  }
@@ -9152,7 +9209,7 @@ import { join as join19 } from "path";
9152
9209
 
9153
9210
  // src/services/warm-hook-logs-service.ts
9154
9211
  import { createHash as createHash2 } from "crypto";
9155
- import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile10, readdir as readdir5, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
9212
+ import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile7, readdir as readdir5, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
9156
9213
  import { homedir as homedir15 } from "os";
9157
9214
  import { join as join18 } from "path";
9158
9215
  var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
@@ -9186,7 +9243,7 @@ var WarmHookLogsService = class {
9186
9243
  hookName: "organization",
9187
9244
  ...entry
9188
9245
  };
9189
- await writeFile10(join18(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
9246
+ await writeFile7(join18(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
9190
9247
  `, "utf-8");
9191
9248
  }
9192
9249
  async saveEnvironmentHookLog(entry) {
@@ -9196,7 +9253,7 @@ var WarmHookLogsService = class {
9196
9253
  hookName: "environment",
9197
9254
  ...entry
9198
9255
  };
9199
- await writeFile10(join18(LOGS_DIR2, environmentFilename()), `${JSON.stringify(log, null, 2)}
9256
+ await writeFile7(join18(LOGS_DIR2, environmentFilename()), `${JSON.stringify(log, null, 2)}
9200
9257
  `, "utf-8");
9201
9258
  }
9202
9259
  async saveRepoHookLog(repoName, entry) {
@@ -9206,7 +9263,7 @@ var WarmHookLogsService = class {
9206
9263
  hookName: repoName,
9207
9264
  ...entry
9208
9265
  };
9209
- await writeFile10(join18(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
9266
+ await writeFile7(join18(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
9210
9267
  `, "utf-8");
9211
9268
  }
9212
9269
  async getAllLogs() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.231",
3
+ "version": "0.1.232",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",