replicas-engine 0.1.286 → 0.1.288

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
@@ -116,9 +116,9 @@ var EXT_TO_LANGUAGE = {
116
116
  function detectLanguageByPath(filePath) {
117
117
  const dot = filePath.lastIndexOf(".");
118
118
  if (dot === -1) {
119
- const basename2 = filePath.split("/").pop() ?? "";
120
- if (basename2 === "Dockerfile") return "dockerfile";
121
- if (basename2 === "Makefile") return "makefile";
119
+ const basename3 = filePath.split("/").pop() ?? "";
120
+ if (basename3 === "Dockerfile") return "dockerfile";
121
+ if (basename3 === "Makefile") return "makefile";
122
122
  return null;
123
123
  }
124
124
  const ext = filePath.slice(dot).toLowerCase();
@@ -286,7 +286,7 @@ var WORKSPACE_SIZES = ["small", "large"];
286
286
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
287
287
 
288
288
  // ../shared/src/e2b.ts
289
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-08-v8";
289
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-09-v2";
290
290
 
291
291
  // ../shared/src/runtime-env.ts
292
292
  function parsePosixEnvFile(content) {
@@ -2502,10 +2502,35 @@ function areSameUserMessageEvents(a, b) {
2502
2502
  if (aItemId || bItemId) return aItemId === bItemId;
2503
2503
  return Math.abs(getEventTimestampMs(a) - getEventTimestampMs(b)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
2504
2504
  }
2505
+ function parseAgentEventJsonl(content, options = {}) {
2506
+ const events = [];
2507
+ for (const line of content.split("\n")) {
2508
+ const trimmed = line.trim();
2509
+ if (!trimmed) continue;
2510
+ try {
2511
+ const parsed = JSON.parse(trimmed);
2512
+ if (isAgentBackendEvent(parsed)) {
2513
+ events.push(parsed);
2514
+ } else {
2515
+ options.onInvalidLine?.({ line: trimmed });
2516
+ }
2517
+ } catch (error) {
2518
+ options.onInvalidLine?.({ line: trimmed, error });
2519
+ }
2520
+ }
2521
+ return events;
2522
+ }
2505
2523
 
2506
2524
  // ../shared/src/display-message/parsers/codex-asp-parser.ts
2507
2525
  var DUPLICATE_WINDOW_MS = 5 * 60 * 1e3;
2508
2526
 
2527
+ // ../shared/src/display-message/parsers/index.ts
2528
+ function isAgentBackendEvent(value) {
2529
+ if (!value || typeof value !== "object") return false;
2530
+ const candidate = value;
2531
+ return typeof candidate.timestamp === "string" && typeof candidate.type === "string" && typeof candidate.payload === "object" && candidate.payload !== null;
2532
+ }
2533
+
2509
2534
  // ../shared/src/object-store/types.ts
2510
2535
  var MEDIA_KIND = {
2511
2536
  IMAGE: "image",
@@ -2739,14 +2764,16 @@ async function monolithRequest(path4, init = {}) {
2739
2764
  if (!ENGINE_ENV.WORKSPACE_ID) {
2740
2765
  throw new Error("WORKSPACE_ID is not set; cannot call monolith");
2741
2766
  }
2767
+ const isFormData = init.body instanceof FormData;
2768
+ const headers = {
2769
+ Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
2770
+ "X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID
2771
+ };
2772
+ if (!isFormData) headers["Content-Type"] = "application/json";
2742
2773
  return fetch(`${ENGINE_ENV.MONOLITH_URL}${path4}`, {
2743
2774
  method: init.method ?? "POST",
2744
- headers: {
2745
- Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
2746
- "X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
2747
- "Content-Type": "application/json"
2748
- },
2749
- body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
2775
+ headers,
2776
+ body: init.body === void 0 ? void 0 : isFormData ? init.body : JSON.stringify(init.body),
2750
2777
  signal: init.signal
2751
2778
  });
2752
2779
  }
@@ -3738,28 +3765,91 @@ var GitService = class {
3738
3765
  var gitService = new GitService();
3739
3766
 
3740
3767
  // src/utils/logger.ts
3741
- import { appendFile, mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
3768
+ import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
3742
3769
  import { homedir as homedir4 } from "os";
3743
3770
  import { join as join6 } from "path";
3744
3771
  import { format } from "util";
3745
3772
  import { randomBytes } from "crypto";
3773
+
3774
+ // src/utils/stream-writer.ts
3775
+ import { createWriteStream } from "fs";
3776
+ var DEFAULT_HIGH_WATER = 8 * 1024 * 1024;
3777
+ var StreamWriter = class {
3778
+ stream = null;
3779
+ backpressured = false;
3780
+ droppedCount = 0;
3781
+ flushTimer = null;
3782
+ open(path4, highWaterMark = DEFAULT_HIGH_WATER) {
3783
+ this.stream = createWriteStream(path4, { flags: "a", highWaterMark });
3784
+ this.stream.on("error", () => {
3785
+ this.stream = null;
3786
+ });
3787
+ this.stream.on("drain", () => {
3788
+ this.backpressured = false;
3789
+ });
3790
+ }
3791
+ write(line) {
3792
+ if (!this.stream) return false;
3793
+ if (this.backpressured) {
3794
+ this.droppedCount++;
3795
+ this.scheduleDropWarning();
3796
+ return false;
3797
+ }
3798
+ const written = this.stream.write(line);
3799
+ if (!written) {
3800
+ this.backpressured = true;
3801
+ }
3802
+ return true;
3803
+ }
3804
+ flush() {
3805
+ return new Promise((resolve3) => {
3806
+ if (!this.stream) {
3807
+ resolve3();
3808
+ return;
3809
+ }
3810
+ if (this.flushTimer) {
3811
+ clearTimeout(this.flushTimer);
3812
+ this.flushTimer = null;
3813
+ }
3814
+ const s = this.stream;
3815
+ this.stream = null;
3816
+ s.end(() => resolve3());
3817
+ });
3818
+ }
3819
+ scheduleDropWarning() {
3820
+ if (this.flushTimer) return;
3821
+ this.flushTimer = setTimeout(() => {
3822
+ this.flushTimer = null;
3823
+ if (this.droppedCount > 0 && this.stream) {
3824
+ this.stream.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [WARN] Dropped ${this.droppedCount} log lines due to IO backpressure
3825
+ `);
3826
+ this.droppedCount = 0;
3827
+ }
3828
+ }, 2e3);
3829
+ if (this.flushTimer && typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
3830
+ this.flushTimer.unref();
3831
+ }
3832
+ }
3833
+ };
3834
+
3835
+ // src/utils/logger.ts
3746
3836
  var LOG_DIR = join6(homedir4(), ".replicas", "logs");
3747
3837
  var EngineLogger = class {
3748
3838
  _sessionId = null;
3749
- filePath = null;
3750
- writeChain = Promise.resolve();
3751
3839
  patched = false;
3840
+ writer = new StreamWriter();
3752
3841
  get sessionId() {
3753
3842
  return this._sessionId;
3754
3843
  }
3755
3844
  async initialize() {
3756
3845
  await mkdir3(LOG_DIR, { recursive: true });
3757
3846
  this._sessionId = this.createSessionId();
3758
- this.filePath = join6(LOG_DIR, `${this._sessionId}.log`);
3759
- await writeFile2(this.filePath, `=== Replicas Engine Session ${this._sessionId} ===
3847
+ const logPath = join6(LOG_DIR, `${this._sessionId}.log`);
3848
+ await writeFile2(logPath, `=== Replicas Engine Session ${this._sessionId} ===
3760
3849
  `, "utf-8");
3850
+ this.writer.open(logPath);
3761
3851
  this.patchConsole();
3762
- this.log("INFO", `Engine logging initialized at ${this.filePath}`);
3852
+ this.log("INFO", `Engine logging initialized at ${logPath}`);
3763
3853
  }
3764
3854
  patchConsole() {
3765
3855
  if (this.patched) return;
@@ -3781,21 +3871,11 @@ var EngineLogger = class {
3781
3871
  };
3782
3872
  }
3783
3873
  log(level, message) {
3784
- if (!this.filePath) return;
3785
- const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
3786
- `;
3787
- this.writeChain = this.writeChain.then(() => appendFile(this.filePath, line, "utf-8")).catch((error) => {
3788
- try {
3789
- const err = error instanceof Error ? error.message : "Unknown error";
3790
- process.stderr.write(`[EngineLogger] Failed to append log line: ${err}
3874
+ this.writer.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
3791
3875
  `);
3792
- } catch {
3793
- }
3794
- });
3795
3876
  }
3796
- /** Wait for all queued log writes to complete. */
3797
3877
  flush() {
3798
- return this.writeChain;
3878
+ return this.writer.flush();
3799
3879
  }
3800
3880
  createSessionId() {
3801
3881
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
@@ -3806,7 +3886,7 @@ var EngineLogger = class {
3806
3886
  var engineLogger = new EngineLogger();
3807
3887
 
3808
3888
  // src/services/replicas-config-service.ts
3809
- import { readFile as readFile5, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
3889
+ import { readFile as readFile5, appendFile, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
3810
3890
  import { existsSync as existsSync4 } from "fs";
3811
3891
  import { join as join9 } from "path";
3812
3892
  import { homedir as homedir7 } from "os";
@@ -4180,7 +4260,7 @@ var ReplicasConfigService = class {
4180
4260
  `;
4181
4261
  try {
4182
4262
  await mkdir6(join9(homedir7(), ".replicas"), { recursive: true });
4183
- await appendFile2(START_HOOKS_LOG, logLine, "utf-8");
4263
+ await appendFile(START_HOOKS_LOG, logLine, "utf-8");
4184
4264
  } catch (error) {
4185
4265
  console.error("Failed to write to start hooks log:", error);
4186
4266
  }
@@ -4508,7 +4588,7 @@ ${startHookConfig.commands.join("\n")}`;
4508
4588
  var replicasConfigService = new ReplicasConfigService();
4509
4589
 
4510
4590
  // src/services/event-service.ts
4511
- import { appendFile as appendFile3, mkdir as mkdir7 } from "fs/promises";
4591
+ import { mkdir as mkdir7 } from "fs/promises";
4512
4592
  import { homedir as homedir8 } from "os";
4513
4593
  import { join as join10 } from "path";
4514
4594
  import { randomUUID } from "crypto";
@@ -4516,9 +4596,10 @@ var ENGINE_DIR = join10(homedir8(), ".replicas", "engine");
4516
4596
  var EVENTS_FILE = join10(ENGINE_DIR, "events.jsonl");
4517
4597
  var EventService = class {
4518
4598
  subscribers = /* @__PURE__ */ new Map();
4519
- writeChain = Promise.resolve();
4599
+ writer = new StreamWriter();
4520
4600
  async initialize() {
4521
4601
  await mkdir7(ENGINE_DIR, { recursive: true });
4602
+ this.writer.open(EVENTS_FILE);
4522
4603
  }
4523
4604
  subscribe(subscriber) {
4524
4605
  const id = randomUUID();
@@ -4531,18 +4612,13 @@ var EventService = class {
4531
4612
  return this.subscribers.size;
4532
4613
  }
4533
4614
  async publish(event) {
4534
- this.writeChain = this.writeChain.catch((error) => {
4535
- const message = error instanceof Error ? error.message : "Unknown error";
4536
- process.stderr.write(`[EventService] Previous write failed, continuing: ${message}
4537
- `);
4538
- }).then(() => appendFile3(EVENTS_FILE, JSON.stringify(event) + "\n", "utf-8"));
4615
+ this.writer.write(JSON.stringify(event) + "\n");
4539
4616
  for (const subscriber of this.subscribers.values()) {
4540
4617
  try {
4541
4618
  subscriber(event);
4542
4619
  } catch {
4543
4620
  }
4544
4621
  }
4545
- await this.writeChain;
4546
4622
  }
4547
4623
  };
4548
4624
  var eventService = new EventService();
@@ -4664,7 +4740,7 @@ async function registerDesktopPreview() {
4664
4740
 
4665
4741
  // src/services/chat/chat-service.ts
4666
4742
  import { existsSync as existsSync7 } from "fs";
4667
- import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
4743
+ import { appendFile as appendFile3, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
4668
4744
  import { homedir as homedir12 } from "os";
4669
4745
  import { join as join14 } from "path";
4670
4746
  import { randomUUID as randomUUID5 } from "crypto";
@@ -4675,35 +4751,15 @@ import {
4675
4751
  } from "@anthropic-ai/claude-agent-sdk";
4676
4752
  import { randomUUID as randomUUID4 } from "crypto";
4677
4753
  import { join as join13 } from "path";
4678
- import { mkdir as mkdir10, appendFile as appendFile4 } from "fs/promises";
4754
+ import { mkdir as mkdir10, appendFile as appendFile2 } from "fs/promises";
4679
4755
  import { homedir as homedir11 } from "os";
4680
4756
 
4681
4757
  // src/utils/jsonl-reader.ts
4682
4758
  import { readFile as readFile7 } from "fs/promises";
4683
- function isJsonlEvent(value) {
4684
- if (!isRecord4(value)) {
4685
- return false;
4686
- }
4687
- return typeof value.timestamp === "string" && typeof value.type === "string" && isRecord4(value.payload);
4688
- }
4689
- function parseJsonlEvents(lines) {
4690
- const events = [];
4691
- for (const line of lines) {
4692
- try {
4693
- const parsed = JSON.parse(line);
4694
- if (isJsonlEvent(parsed)) {
4695
- events.push(parsed);
4696
- }
4697
- } catch {
4698
- }
4699
- }
4700
- return events;
4701
- }
4702
4759
  async function readJSONL(filePath) {
4703
4760
  try {
4704
4761
  const content = await readFile7(filePath, "utf-8");
4705
- const lines = content.split("\n").filter((line) => line.trim());
4706
- return parseJsonlEvents(lines);
4762
+ return parseAgentEventJsonl(content);
4707
4763
  } catch (error) {
4708
4764
  return [];
4709
4765
  }
@@ -5788,7 +5844,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5788
5844
  type,
5789
5845
  payload: { ...payload, parent_tool_use_id: null }
5790
5846
  };
5791
- await appendFile4(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
5847
+ await appendFile2(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
5792
5848
  this.onEvent(event);
5793
5849
  }
5794
5850
  buildCanUseTool() {
@@ -5924,7 +5980,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
5924
5980
  }
5925
5981
  };
5926
5982
  try {
5927
- await appendFile4(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
5983
+ await appendFile2(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
5928
5984
  } catch (writeError) {
5929
5985
  console.error("[ClaudeManager] Failed to record auth-retry-exhausted event:", writeError);
5930
5986
  }
@@ -6254,7 +6310,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6254
6310
  try {
6255
6311
  const usage = await response.getContextUsage();
6256
6312
  const event = this.emitContextUsage(this.buildContextUsagePayload(usage));
6257
- await appendFile4(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
6313
+ await appendFile2(this.historyFile, JSON.stringify(event) + "\n", "utf-8");
6258
6314
  } catch (error) {
6259
6315
  console.warn("[ClaudeManager] Failed to record context usage:", error instanceof Error ? error.message : error);
6260
6316
  }
@@ -6330,7 +6386,7 @@ var ClaudeManager = class _ClaudeManager extends CodingAgentManager {
6330
6386
  type: `claude-${event.type}`,
6331
6387
  payload: event
6332
6388
  };
6333
- await appendFile4(this.historyFile, JSON.stringify(jsonEvent) + "\n", "utf-8");
6389
+ await appendFile2(this.historyFile, JSON.stringify(jsonEvent) + "\n", "utf-8");
6334
6390
  this.onEvent(jsonEvent);
6335
6391
  }
6336
6392
  };
@@ -6531,7 +6587,7 @@ var AspClient = class {
6531
6587
  // src/managers/codex-asp/app-server-process.ts
6532
6588
  var DEFAULT_CODEX_BINARY = "codex";
6533
6589
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
6534
- var ENGINE_PACKAGE_VERSION = "0.1.286";
6590
+ var ENGINE_PACKAGE_VERSION = "0.1.288";
6535
6591
  var INITIALIZE_METHOD = "initialize";
6536
6592
  var INITIALIZED_NOTIFICATION = "initialized";
6537
6593
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -8911,7 +8967,8 @@ var ChatService = class {
8911
8967
  }
8912
8968
  workingDirectory;
8913
8969
  chats = /* @__PURE__ */ new Map();
8914
- writeChain = Promise.resolve();
8970
+ persistInFlight = false;
8971
+ persistQueued = false;
8915
8972
  async initialize() {
8916
8973
  await mkdir11(ENGINE_DIR2, { recursive: true });
8917
8974
  await mkdir11(CLAUDE_HISTORY_DIR, { recursive: true });
@@ -9018,7 +9075,7 @@ var ChatService = class {
9018
9075
  }
9019
9076
  async appendSender(chatId, sender) {
9020
9077
  try {
9021
- await appendFile5(this.senderFilePath(chatId), JSON.stringify(sender) + "\n", "utf-8");
9078
+ await appendFile3(this.senderFilePath(chatId), JSON.stringify(sender) + "\n", "utf-8");
9022
9079
  } catch (error) {
9023
9080
  console.error("[ChatService] Failed to append sender record:", error);
9024
9081
  }
@@ -9408,8 +9465,12 @@ var ChatService = class {
9408
9465
  }
9409
9466
  }
9410
9467
  async persistAllChats() {
9411
- this.writeChain = this.writeChain.catch(() => {
9412
- }).then(async () => {
9468
+ if (this.persistInFlight) {
9469
+ this.persistQueued = true;
9470
+ return;
9471
+ }
9472
+ this.persistInFlight = true;
9473
+ try {
9413
9474
  const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
9414
9475
  try {
9415
9476
  await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
@@ -9420,8 +9481,14 @@ var ChatService = class {
9420
9481
  }
9421
9482
  await atomicWriteFile(CHATS_FILE, `${JSON.stringify(payload, null, 2)}
9422
9483
  `);
9423
- });
9424
- await this.writeChain;
9484
+ } catch {
9485
+ } finally {
9486
+ this.persistInFlight = false;
9487
+ if (this.persistQueued) {
9488
+ this.persistQueued = false;
9489
+ void this.persistAllChats();
9490
+ }
9491
+ }
9425
9492
  }
9426
9493
  async publish(input) {
9427
9494
  const event = {
@@ -9532,11 +9599,11 @@ function scoreMatch(query2, filePath) {
9532
9599
  const lowerQuery = query2.toLowerCase();
9533
9600
  const lowerPath = filePath.toLowerCase();
9534
9601
  const segments = lowerPath.split("/");
9535
- const basename2 = segments[segments.length - 1] ?? "";
9536
- if (basename2 === lowerQuery) return 100;
9537
- if (basename2.startsWith(lowerQuery)) return 90;
9602
+ const basename3 = segments[segments.length - 1] ?? "";
9603
+ if (basename3 === lowerQuery) return 100;
9604
+ if (basename3.startsWith(lowerQuery)) return 90;
9538
9605
  if (segments.some((seg) => seg === lowerQuery)) return 80;
9539
- if (basename2.includes(lowerQuery)) return 70;
9606
+ if (basename3.includes(lowerQuery)) return 70;
9540
9607
  if (segments.some((seg) => seg.includes(lowerQuery))) return 60;
9541
9608
  if (lowerPath.includes(lowerQuery)) return 50;
9542
9609
  return 0;
@@ -9739,23 +9806,76 @@ var RepoFileService = class {
9739
9806
  // src/v1-routes.ts
9740
9807
  import { Hono } from "hono";
9741
9808
  import { z as z2 } from "zod";
9742
- import { readdir as readdir5, stat as stat4, readFile as readFile13 } from "fs/promises";
9743
- import { join as join19, resolve as resolve2 } from "path";
9809
+ import { readdir as readdir6, stat as stat4, readFile as readFile14 } from "fs/promises";
9810
+ import { join as join20, resolve as resolve2 } from "path";
9744
9811
 
9745
- // src/services/canvas-service.ts
9746
- import { readdir as readdir3, readFile as readFile10, stat as stat3 } from "fs/promises";
9747
- import { homedir as homedir13 } from "os";
9812
+ // src/services/upload-chat-transcripts.ts
9813
+ import { readdir as readdir3, readFile as readFile10 } from "fs/promises";
9748
9814
  import { basename, join as join16 } from "path";
9815
+ import { homedir as homedir13 } from "os";
9816
+ var ENGINE_DIR3 = join16(homedir13(), ".replicas", "engine");
9817
+ var HISTORY_DIRS = [
9818
+ join16(ENGINE_DIR3, "claude-histories"),
9819
+ join16(ENGINE_DIR3, "relay-histories")
9820
+ ];
9821
+ async function flushAllChatTranscripts() {
9822
+ let flushed = 0;
9823
+ let failed = 0;
9824
+ const tasks = [];
9825
+ for (const dir of HISTORY_DIRS) {
9826
+ let entries;
9827
+ try {
9828
+ entries = await readdir3(dir);
9829
+ } catch {
9830
+ continue;
9831
+ }
9832
+ for (const entry of entries) {
9833
+ if (!entry.endsWith(".jsonl")) continue;
9834
+ const chatId = basename(entry, ".jsonl");
9835
+ tasks.push(
9836
+ uploadOne(chatId, join16(dir, entry)).then(() => {
9837
+ flushed++;
9838
+ }).catch((err) => {
9839
+ failed++;
9840
+ console.error("[ChatTranscriptUploader] upload failed:", { chatId, err });
9841
+ })
9842
+ );
9843
+ }
9844
+ }
9845
+ await Promise.all(tasks);
9846
+ return { flushed, failed };
9847
+ }
9848
+ async function uploadOne(chatId, filePath) {
9849
+ const bytes = await readFile10(filePath);
9850
+ if (bytes.byteLength === 0) return;
9851
+ const form = new FormData();
9852
+ form.append("chat_id", chatId);
9853
+ form.append(
9854
+ "file",
9855
+ new Blob([new Uint8Array(bytes)], { type: "application/x-ndjson" }),
9856
+ "transcript.jsonl"
9857
+ );
9858
+ const response = await monolithRequest("/v1/engine/chat-transcripts", { body: form });
9859
+ if (!response.ok) {
9860
+ const errorText = await response.text();
9861
+ throw new Error(`upload failed: ${response.status} ${errorText}`);
9862
+ }
9863
+ }
9864
+
9865
+ // src/services/canvas-service.ts
9866
+ import { readdir as readdir4, readFile as readFile11, stat as stat3 } from "fs/promises";
9867
+ import { homedir as homedir14 } from "os";
9868
+ import { basename as basename2, join as join17 } from "path";
9749
9869
  var CANVAS_DIRECTORIES = [
9750
- join16(homedir13(), ".claude", "plans"),
9751
- join16(homedir13(), ".replicas", "canvas")
9870
+ join17(homedir14(), ".claude", "plans"),
9871
+ join17(homedir14(), ".replicas", "canvas")
9752
9872
  ];
9753
9873
  var MAX_CANVAS_FILE_BYTES = 5 * 1024 * 1024;
9754
9874
  function isTextKind(kind) {
9755
9875
  return kind === "markdown" || kind === "html";
9756
9876
  }
9757
9877
  function sanitizeFilename(filename) {
9758
- return basename(filename);
9878
+ return basename2(filename);
9759
9879
  }
9760
9880
  var CanvasService = class {
9761
9881
  async listItems() {
@@ -9763,7 +9883,7 @@ var CanvasService = class {
9763
9883
  for (const directory of CANVAS_DIRECTORIES) {
9764
9884
  let entries;
9765
9885
  try {
9766
- entries = await readdir3(directory, { withFileTypes: true });
9886
+ entries = await readdir4(directory, { withFileTypes: true });
9767
9887
  } catch {
9768
9888
  continue;
9769
9889
  }
@@ -9774,7 +9894,7 @@ var CanvasService = class {
9774
9894
  const { kind } = classifyCanvasFilename(entry.name);
9775
9895
  let sizeBytes = 0;
9776
9896
  try {
9777
- const s = await stat3(join16(directory, entry.name));
9897
+ const s = await stat3(join17(directory, entry.name));
9778
9898
  sizeBytes = s.size;
9779
9899
  } catch {
9780
9900
  continue;
@@ -9789,7 +9909,7 @@ var CanvasService = class {
9789
9909
  if (!safe || safe !== filename || safe.startsWith(".")) return null;
9790
9910
  const { kind, mimeType } = classifyCanvasFilename(safe);
9791
9911
  for (const directory of CANVAS_DIRECTORIES) {
9792
- const filePath = join16(directory, safe);
9912
+ const filePath = join17(directory, safe);
9793
9913
  let sizeBytes = 0;
9794
9914
  try {
9795
9915
  const s = await stat3(filePath);
@@ -9808,10 +9928,10 @@ var CanvasService = class {
9808
9928
  }
9809
9929
  try {
9810
9930
  if (isTextKind(kind)) {
9811
- const content = await readFile10(filePath, "utf-8");
9931
+ const content = await readFile11(filePath, "utf-8");
9812
9932
  return { filename: safe, kind, sizeBytes, mimeType, content };
9813
9933
  }
9814
- const buf = await readFile10(filePath);
9934
+ const buf = await readFile11(filePath);
9815
9935
  return { filename: safe, kind, sizeBytes, mimeType, base64: buf.toString("base64") };
9816
9936
  } catch {
9817
9937
  continue;
@@ -9824,16 +9944,16 @@ var canvasService = new CanvasService();
9824
9944
 
9825
9945
  // src/services/warm-hooks-service.ts
9826
9946
  import { spawn as spawn4 } from "child_process";
9827
- import { readFile as readFile12 } from "fs/promises";
9947
+ import { readFile as readFile13 } from "fs/promises";
9828
9948
  import { existsSync as existsSync8 } from "fs";
9829
- import { join as join18 } from "path";
9949
+ import { join as join19 } from "path";
9830
9950
 
9831
9951
  // src/services/warm-hook-logs-service.ts
9832
- import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile6, readdir as readdir4, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
9833
- import { homedir as homedir14 } from "os";
9834
- import { join as join17 } from "path";
9835
- var LOGS_DIR2 = join17(homedir14(), ".replicas", "warm-hook-logs");
9836
- var CURRENT_RUN_LOG = join17(LOGS_DIR2, "current-run.log");
9952
+ import { mkdir as mkdir12, readFile as readFile12, writeFile as writeFile6, readdir as readdir5, appendFile as appendFile4, unlink as unlink3 } from "fs/promises";
9953
+ import { homedir as homedir15 } from "os";
9954
+ import { join as join18 } from "path";
9955
+ var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
9956
+ var CURRENT_RUN_LOG = join18(LOGS_DIR2, "current-run.log");
9837
9957
  var GLOBAL_FILENAME = "global.json";
9838
9958
  function withPreview2(stored) {
9839
9959
  const preview = buildHookOutputPreview(stored.output);
@@ -9850,7 +9970,7 @@ var WarmHookLogsService = class {
9850
9970
  hookName: "organization",
9851
9971
  ...entry
9852
9972
  };
9853
- await writeFile6(join17(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
9973
+ await writeFile6(join18(LOGS_DIR2, GLOBAL_FILENAME), `${JSON.stringify(log, null, 2)}
9854
9974
  `, "utf-8");
9855
9975
  }
9856
9976
  async saveEnvironmentHookLog(entry) {
@@ -9860,7 +9980,7 @@ var WarmHookLogsService = class {
9860
9980
  hookName: "environment",
9861
9981
  ...entry
9862
9982
  };
9863
- await writeFile6(join17(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
9983
+ await writeFile6(join18(LOGS_DIR2, ENVIRONMENT_HOOK_LOG_FILENAME), `${JSON.stringify(log, null, 2)}
9864
9984
  `, "utf-8");
9865
9985
  }
9866
9986
  async saveRepoHookLog(repoName, entry) {
@@ -9870,13 +9990,13 @@ var WarmHookLogsService = class {
9870
9990
  hookName: repoName,
9871
9991
  ...entry
9872
9992
  };
9873
- await writeFile6(join17(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
9993
+ await writeFile6(join18(LOGS_DIR2, repoHookLogFilename(repoName)), `${JSON.stringify(log, null, 2)}
9874
9994
  `, "utf-8");
9875
9995
  }
9876
9996
  async getAllLogs() {
9877
9997
  let files;
9878
9998
  try {
9879
- files = await readdir4(LOGS_DIR2);
9999
+ files = await readdir5(LOGS_DIR2);
9880
10000
  } catch (err) {
9881
10001
  if (err.code === "ENOENT") {
9882
10002
  return [];
@@ -9889,7 +10009,7 @@ var WarmHookLogsService = class {
9889
10009
  continue;
9890
10010
  }
9891
10011
  try {
9892
- const raw = await readFile11(join17(LOGS_DIR2, file), "utf-8");
10012
+ const raw = await readFile12(join18(LOGS_DIR2, file), "utf-8");
9893
10013
  const stored = JSON.parse(raw);
9894
10014
  logs.push(withPreview2(stored));
9895
10015
  } catch {
@@ -9914,11 +10034,11 @@ var WarmHookLogsService = class {
9914
10034
  }
9915
10035
  async appendCurrentRunLog(chunk) {
9916
10036
  if (!chunk) return;
9917
- await appendFile6(CURRENT_RUN_LOG, chunk, "utf-8");
10037
+ await appendFile4(CURRENT_RUN_LOG, chunk, "utf-8");
9918
10038
  }
9919
10039
  async getCurrentRunLog() {
9920
10040
  try {
9921
- return await readFile11(CURRENT_RUN_LOG, "utf-8");
10041
+ return await readFile12(CURRENT_RUN_LOG, "utf-8");
9922
10042
  } catch (err) {
9923
10043
  if (err.code === "ENOENT") return null;
9924
10044
  throw err;
@@ -9927,7 +10047,7 @@ var WarmHookLogsService = class {
9927
10047
  async getFullOutput(hookType, hookName) {
9928
10048
  const filename = hookType === "global" ? GLOBAL_FILENAME : hookType === "environment" ? ENVIRONMENT_HOOK_LOG_FILENAME : repoHookLogFilename(hookName);
9929
10049
  try {
9930
- const raw = await readFile11(join17(LOGS_DIR2, filename), "utf-8");
10050
+ const raw = await readFile12(join18(LOGS_DIR2, filename), "utf-8");
9931
10051
  const stored = JSON.parse(raw);
9932
10052
  if (stored.hookType !== hookType || stored.hookName !== hookName) {
9933
10053
  return null;
@@ -9946,12 +10066,12 @@ var warmHookLogsService = new WarmHookLogsService();
9946
10066
  // src/services/warm-hooks-service.ts
9947
10067
  async function readRepoWarmHook(repoPath) {
9948
10068
  for (const filename of REPLICAS_CONFIG_FILENAMES) {
9949
- const configPath = join18(repoPath, filename);
10069
+ const configPath = join19(repoPath, filename);
9950
10070
  if (!existsSync8(configPath)) {
9951
10071
  continue;
9952
10072
  }
9953
10073
  try {
9954
- const raw = await readFile12(configPath, "utf-8");
10074
+ const raw = await readFile13(configPath, "utf-8");
9955
10075
  const config = parseReplicasConfigString(raw, filename);
9956
10076
  if (!config.warmHook) {
9957
10077
  return null;
@@ -10427,6 +10547,17 @@ function createV1Routes(deps) {
10427
10547
  return c.json(jsonError("Failed to clear goal", error instanceof Error ? error.message : "Unknown error"), 404);
10428
10548
  }
10429
10549
  });
10550
+ app2.post("/chat-transcripts/flush-all", async (c) => {
10551
+ try {
10552
+ const result = await flushAllChatTranscripts();
10553
+ return c.json(result);
10554
+ } catch (error) {
10555
+ return c.json(
10556
+ jsonError("Failed to flush transcripts", error instanceof Error ? error.message : "Unknown error"),
10557
+ 500
10558
+ );
10559
+ }
10560
+ });
10430
10561
  app2.get("/chats/:chatId/queue", (c) => {
10431
10562
  try {
10432
10563
  const result = deps.chatService.getChatQueue(c.req.param("chatId"));
@@ -10862,11 +10993,11 @@ function createV1Routes(deps) {
10862
10993
  });
10863
10994
  app2.get("/logs", async (c) => {
10864
10995
  try {
10865
- const files = await readdir5(LOG_DIR).catch(() => []);
10996
+ const files = await readdir6(LOG_DIR).catch(() => []);
10866
10997
  const logFiles = files.filter((f) => f.endsWith(".log"));
10867
10998
  const sessions = await Promise.all(
10868
10999
  logFiles.map(async (filename) => {
10869
- const filePath = join19(LOG_DIR, filename);
11000
+ const filePath = join20(LOG_DIR, filename);
10870
11001
  const fileStat = await stat4(filePath);
10871
11002
  const sessionId = filename.replace(/\.log$/, "");
10872
11003
  return {
@@ -10903,7 +11034,7 @@ function createV1Routes(deps) {
10903
11034
  const limit = Math.min(parseInt(c.req.query("limit") || "500", 10), 5e3);
10904
11035
  let content;
10905
11036
  try {
10906
- content = await readFile13(filePath, "utf-8");
11037
+ content = await readFile14(filePath, "utf-8");
10907
11038
  } catch {
10908
11039
  return c.json(jsonError("Log session not found"), 404);
10909
11040
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.286",
3
+ "version": "0.1.288",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -86,9 +86,9 @@ while true; do
86
86
 
87
87
  START_TIME=$(date +%s)
88
88
  if [ -n "$ENGINE_LD_PRELOAD" ]; then
89
- LD_PRELOAD="$ENGINE_LD_PRELOAD" "${ENGINE_CMD[@]}" >> "$BOOTSTRAP_LOG" 2>&1 &
89
+ LD_PRELOAD="$ENGINE_LD_PRELOAD" "${ENGINE_CMD[@]}" > >(cat >> "$BOOTSTRAP_LOG") 2>&1 &
90
90
  else
91
- "${ENGINE_CMD[@]}" >> "$BOOTSTRAP_LOG" 2>&1 &
91
+ "${ENGINE_CMD[@]}" > >(cat >> "$BOOTSTRAP_LOG") 2>&1 &
92
92
  fi
93
93
  ENGINE_PID=$!
94
94
  echo "$ENGINE_PID" > "$PIDFILE"