replicas-engine 0.1.419 → 0.1.421

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 +107 -6
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -9,6 +9,24 @@ function isRecord(value) {
9
9
  return typeof value === "object" && value !== null && !Array.isArray(value);
10
10
  }
11
11
 
12
+ // ../shared/src/async.ts
13
+ var TIMEOUT = /* @__PURE__ */ Symbol("timeout");
14
+ async function raceWithTimeout(promise, ms) {
15
+ let timer;
16
+ try {
17
+ return await Promise.race([
18
+ promise,
19
+ new Promise((resolve4) => {
20
+ timer = setTimeout(() => resolve4(TIMEOUT), ms);
21
+ })
22
+ ]);
23
+ } finally {
24
+ clearTimeout(timer);
25
+ promise.catch(() => {
26
+ });
27
+ }
28
+ }
29
+
12
30
  // ../shared/src/agent.ts
13
31
  var VALID_AGENT_PROVIDERS = ["claude", "codex", "cursor", "opencode", "relay"];
14
32
  var VALID_CODING_AGENT_PROVIDERS = VALID_AGENT_PROVIDERS.filter(
@@ -490,7 +508,7 @@ var WORKSPACE_SIZES = ["small", "large"];
490
508
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
491
509
 
492
510
  // ../shared/src/e2b.ts
493
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-09-v8";
511
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-10-v2";
494
512
 
495
513
  // ../shared/src/runtime-env.ts
496
514
  function parsePosixEnvFile(content) {
@@ -2802,6 +2820,7 @@ var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
2802
2820
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
2803
2821
  var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
2804
2822
  var WORKSPACE_SORT_CHOICES = [
2823
+ ["manual", "Custom"],
2805
2824
  ["activity", "Last activity"],
2806
2825
  ["created", "Created date"],
2807
2826
  ["status", "Status"]
@@ -8272,7 +8291,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
8272
8291
  var MIN_CODEX_CLI_VERSION = "0.144.0";
8273
8292
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
8274
8293
  var codexCliVersionEnsured = null;
8275
- var ENGINE_PACKAGE_VERSION = "0.1.419";
8294
+ var ENGINE_PACKAGE_VERSION = "0.1.421";
8276
8295
  var INITIALIZE_METHOD = "initialize";
8277
8296
  var INITIALIZED_NOTIFICATION = "initialized";
8278
8297
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -10390,6 +10409,10 @@ import { basename, dirname as dirname5, extname, join as join17 } from "path";
10390
10409
  import { parse as parseYaml2 } from "yaml";
10391
10410
  import { Agent as CursorAgent } from "@cursor/sdk";
10392
10411
  var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
10412
+ var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
10413
+ var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
10414
+ var CURSOR_CANCEL_TIMEOUT_MS = 1e4;
10415
+ var TURN_ABORTED = /* @__PURE__ */ Symbol("cursor-turn-aborted");
10393
10416
  var CURSOR_COMPOSER_CONTEXT_WINDOW = 2e5;
10394
10417
  var CURSOR_CATEGORY_COLORS = {
10395
10418
  input: "#3eeba3",
@@ -10453,6 +10476,7 @@ var CursorManager = class extends CodingAgentManager {
10453
10476
  agent = null;
10454
10477
  activeRun = null;
10455
10478
  activeModel = null;
10479
+ abortActiveTurn = null;
10456
10480
  blockedToolCallIds = /* @__PURE__ */ new Set();
10457
10481
  pendingCursorBlockReason = null;
10458
10482
  historyFilePath;
@@ -10469,7 +10493,27 @@ var CursorManager = class extends CodingAgentManager {
10469
10493
  await mkdir11(dirname5(this.historyFilePath), { recursive: true });
10470
10494
  }
10471
10495
  async interruptActiveTurn() {
10472
- await this.activeRun?.cancel();
10496
+ await this.cancelRun(this.activeRun);
10497
+ this.abortActiveTurn?.();
10498
+ }
10499
+ async cancelRun(run) {
10500
+ if (!run || run.status !== "running") return;
10501
+ try {
10502
+ const result = await raceWithTimeout(run.cancel(), CURSOR_CANCEL_TIMEOUT_MS);
10503
+ if (result === TIMEOUT) {
10504
+ console.error("[CursorManager] Timed out cancelling Cursor run:", run.id);
10505
+ }
10506
+ } catch (error) {
10507
+ console.error("[CursorManager] Failed to cancel Cursor run:", error);
10508
+ }
10509
+ }
10510
+ dispose() {
10511
+ try {
10512
+ this.agent?.close();
10513
+ } catch (error) {
10514
+ console.error("[CursorManager] Failed to close Cursor agent:", error);
10515
+ }
10516
+ this.agent = null;
10473
10517
  }
10474
10518
  async getHistory() {
10475
10519
  await this.historyFile.flush();
@@ -10521,6 +10565,10 @@ var CursorManager = class extends CodingAgentManager {
10521
10565
  return this.agent;
10522
10566
  }
10523
10567
  async processMessageInternal(request) {
10568
+ let run = null;
10569
+ const aborted = new Promise((resolve4) => {
10570
+ this.abortActiveTurn = () => resolve4(TURN_ABORTED);
10571
+ });
10524
10572
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
10525
10573
  const linearForwarder = new LinearEventForwarder(linearSessionId);
10526
10574
  try {
@@ -10531,7 +10579,7 @@ var CursorManager = class extends CodingAgentManager {
10531
10579
  type: "user_message",
10532
10580
  message: request.message
10533
10581
  }, this.historyFile);
10534
- const run = await agent.send(message, {
10582
+ run = await agent.send(message, {
10535
10583
  model: { id: model },
10536
10584
  mode: request.planMode ? "plan" : "agent",
10537
10585
  onDelta: async (args) => {
@@ -10544,7 +10592,25 @@ var CursorManager = class extends CodingAgentManager {
10544
10592
  await run.cancel();
10545
10593
  throw new Error(this.pendingCursorBlockReason);
10546
10594
  }
10547
- for await (const event of run.stream()) {
10595
+ const events = run.stream()[Symbol.asyncIterator]();
10596
+ let interrupted = false;
10597
+ while (true) {
10598
+ const nextPromise = events.next();
10599
+ nextPromise.catch(() => {
10600
+ });
10601
+ const next = await raceWithTimeout(
10602
+ Promise.race([nextPromise, aborted]),
10603
+ CURSOR_STREAM_INACTIVITY_TIMEOUT_MS
10604
+ );
10605
+ if (next === TIMEOUT) {
10606
+ throw new Error(`Cursor run stalled: no events for ${CURSOR_STREAM_INACTIVITY_TIMEOUT_MS / 6e4} minutes; cancelling the run`);
10607
+ }
10608
+ if (next === TURN_ABORTED) {
10609
+ interrupted = true;
10610
+ break;
10611
+ }
10612
+ if (next.done) break;
10613
+ const event = next.value;
10548
10614
  this.recordCursorEvent(event);
10549
10615
  if (event.type === "tool_call" && event.status === "running") {
10550
10616
  await this.handleCursorToolCall(cursorToolInputFromMessage(event));
@@ -10553,7 +10619,14 @@ var CursorManager = class extends CodingAgentManager {
10553
10619
  linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
10554
10620
  }
10555
10621
  }
10556
- const result = await run.wait();
10622
+ if (interrupted) {
10623
+ await this.cancelRun(run);
10624
+ return;
10625
+ }
10626
+ const result = await raceWithTimeout(run.wait(), CURSOR_RUN_SETTLE_TIMEOUT_MS);
10627
+ if (result === TIMEOUT) {
10628
+ throw new Error("Cursor run did not report a result after its event stream ended");
10629
+ }
10557
10630
  if (result.status === "error") {
10558
10631
  this.recordHistoryEvent("cursor-error", {
10559
10632
  type: "error",
@@ -10564,11 +10637,13 @@ var CursorManager = class extends CodingAgentManager {
10564
10637
  linearForwarder.flushThoughtAsResponse();
10565
10638
  }
10566
10639
  } catch (error) {
10640
+ await this.cancelRun(run);
10567
10641
  this.recordHistoryEvent("cursor-error", {
10568
10642
  type: "error",
10569
10643
  message: error instanceof Error ? error.message : String(error)
10570
10644
  }, this.historyFile);
10571
10645
  } finally {
10646
+ this.abortActiveTurn = null;
10572
10647
  this.activeRun = null;
10573
10648
  this.activeModel = null;
10574
10649
  this.blockedToolCallIds.clear();
@@ -12305,6 +12380,7 @@ function acceptedEventInCodexTranscript(acceptedEvent, transcript) {
12305
12380
  return getCodexTranscriptUserMessages(transcript).includes(message);
12306
12381
  }
12307
12382
  var LAST_MESSAGE_PREVIEW_MAX = 200;
12383
+ var MAX_ACCEPTED_SEND_RESPONSES = 50;
12308
12384
  function isChatActive(chat) {
12309
12385
  return chat.provider.isProcessing() || (chat.provider.hasActiveBackgroundTasks?.() ?? false);
12310
12386
  }
@@ -12463,7 +12539,20 @@ var ChatService = class {
12463
12539
  }
12464
12540
  async sendMessage(chatId, request) {
12465
12541
  const chat = this.requireChat(chatId);
12542
+ if (request.idempotencyKey) {
12543
+ const accepted = chat.acceptedSendResponses.get(request.idempotencyKey);
12544
+ if (accepted) {
12545
+ return accepted;
12546
+ }
12547
+ }
12466
12548
  const result = await chat.provider.enqueueMessage(request);
12549
+ if (request.idempotencyKey) {
12550
+ chat.acceptedSendResponses.set(request.idempotencyKey, result);
12551
+ for (const key of chat.acceptedSendResponses.keys()) {
12552
+ if (chat.acceptedSendResponses.size <= MAX_ACCEPTED_SEND_RESPONSES) break;
12553
+ chat.acceptedSendResponses.delete(key);
12554
+ }
12555
+ }
12467
12556
  const acceptedEvent = createUserMessageEvent(request.message, result.messageId, request.images);
12468
12557
  chat.pendingMessageIds.push(result.messageId);
12469
12558
  chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
@@ -12644,6 +12733,7 @@ var ChatService = class {
12644
12733
  const target = this.chats.get(id);
12645
12734
  if (!target) continue;
12646
12735
  this.chats.delete(id);
12736
+ target.provider.dispose?.();
12647
12737
  await this.deleteHistoryFile(target.persisted);
12648
12738
  await this.publish({ type: "chat.deleted", payload: { chatId: id } });
12649
12739
  }
@@ -12788,6 +12878,7 @@ var ChatService = class {
12788
12878
  provider,
12789
12879
  pendingMessageIds: [],
12790
12880
  acceptedUserEvents: /* @__PURE__ */ new Map(),
12881
+ acceptedSendResponses: /* @__PURE__ */ new Map(),
12791
12882
  activeMessageId: null,
12792
12883
  hasActiveTurn: false,
12793
12884
  observedBranchesByRepo: /* @__PURE__ */ new Map()
@@ -13726,6 +13817,7 @@ var sendMessageSchema = z2.object({
13726
13817
  enableInteractiveTools: z2.boolean().optional(),
13727
13818
  type: z2.string().min(1).optional(),
13728
13819
  merge: z2.boolean().optional(),
13820
+ idempotencyKey: z2.string().min(1).max(128).optional(),
13729
13821
  senderUserId: z2.string().optional(),
13730
13822
  senderEmail: z2.string().optional(),
13731
13823
  senderDisplayName: z2.string().optional()
@@ -14566,7 +14658,16 @@ process.on("uncaughtException", (error) => {
14566
14658
  console.error("[FATAL] Uncaught exception:", error);
14567
14659
  engineLogger.flush().finally(() => process.exit(1));
14568
14660
  });
14661
+ function isCursorSdkRejection(reason) {
14662
+ const text = reason instanceof Error ? `${reason.stack ?? ""}
14663
+ ${reason.message}` : String(reason);
14664
+ return text.includes("@cursor/sdk");
14665
+ }
14569
14666
  process.on("unhandledRejection", (reason) => {
14667
+ if (isCursorSdkRejection(reason)) {
14668
+ console.error("[CursorSDK] Unhandled rejection (non-fatal):", reason);
14669
+ return;
14670
+ }
14570
14671
  console.error("[FATAL] Unhandled rejection:", reason);
14571
14672
  engineLogger.flush().finally(() => process.exit(1));
14572
14673
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.419",
3
+ "version": "0.1.421",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",