replicas-engine 0.1.420 → 0.1.422

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 +172 -14
  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-10-v1";
511
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-07-10-v3";
494
512
 
495
513
  // ../shared/src/runtime-env.ts
496
514
  function parsePosixEnvFile(content) {
@@ -1151,18 +1169,19 @@ var GITLAB_ABILITY = {
1151
1169
  };
1152
1170
 
1153
1171
  // ../shared/src/default-skills/replicas-agent/abilities/google.ts
1154
- var SECTION5 = `### Google Workspace (Docs, Sheets, Forms, Drive)
1155
- Create and edit Google Docs, Sheets, and Forms via the Replicas gateway. Files are owned by Replicas \u2014 the integration cannot access pre-existing Google content created outside of it.
1172
+ var SECTION5 = `### Google Workspace and Search Console
1173
+ Create and edit Google Docs, Sheets, and Forms, and read Google Search Console data via the Replicas gateway. Drive access is limited to files created by Replicas.
1156
1174
 
1157
1175
  **Reference:** \`references/GOOGLE.md\`
1158
1176
 
1159
1177
  Use this when:
1160
1178
  - You need to create or edit a Google Doc, Sheet, or Form
1161
1179
  - You need to share, rename, move, or delete a Replicas-created Google file
1162
- - You need to read responses from a Replicas-created Google Form`;
1163
- var REFERENCE5 = `# Google Workspace (Docs, Sheets, Forms, Drive)
1180
+ - You need to read responses from a Replicas-created Google Form
1181
+ - You need to list Search Console properties, query search performance, read sitemaps, or inspect an indexed URL`;
1182
+ var REFERENCE5 = `# Google Workspace and Search Console
1164
1183
 
1165
- This guide covers how to create and edit Google Docs, Sheets, and Forms \u2014 plus do basic Drive file operations \u2014 from inside a Replicas workspace, using the monolith as a gateway to Google's APIs.
1184
+ This guide covers how to create and edit Google Docs, Sheets, and Forms, perform basic Drive file operations, and read Google Search Console data from inside a Replicas workspace using the monolith as a gateway to Google's APIs.
1166
1185
 
1167
1186
  ## Prerequisites
1168
1187
 
@@ -1411,6 +1430,62 @@ curl -s "$MONOLITH_URL/v1/gdrive/files?pageSize=50" "\${GDRIVE_AUTH[@]}"
1411
1430
 
1412
1431
  Pass \`?q=...\` to filter using [Drive API search syntax](https://developers.google.com/workspace/drive/api/guides/search-files). Only files the app created or was granted access to will appear.
1413
1432
 
1433
+ ## Google Search Console (read-only)
1434
+
1435
+ Search Console access uses the \`webmasters.readonly\` scope. The connected Google account must have access to the requested property. If the account was connected before this scope was added, reconnect it in **Settings \u2192 Integrations \u2192 Google** first.
1436
+
1437
+ Property identifiers must be URL-encoded as query parameters. URL-prefix properties include the trailing slash (for example, \`https://example.com/\`); Domain properties use \`sc-domain:example.com\`.
1438
+
1439
+ ### List accessible properties
1440
+
1441
+ \`\`\`bash
1442
+ curl -s "$MONOLITH_URL/v1/gdrive/search-console/sites" "\${GDRIVE_AUTH[@]}"
1443
+ \`\`\`
1444
+
1445
+ ### Get a property
1446
+
1447
+ \`\`\`bash
1448
+ SITE_URL=$(printf %s 'sc-domain:example.com' | jq -sRr @uri)
1449
+ curl -s "$MONOLITH_URL/v1/gdrive/search-console/site?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}"
1450
+ \`\`\`
1451
+
1452
+ ### Query search performance
1453
+
1454
+ \`\`\`bash
1455
+ curl -s -X POST "$MONOLITH_URL/v1/gdrive/search-console/search-analytics/query?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}" \\
1456
+ -H "Content-Type: application/json" \\
1457
+ -d '{
1458
+ "startDate": "2026-06-01",
1459
+ "endDate": "2026-06-30",
1460
+ "dimensions": ["query", "page"],
1461
+ "rowLimit": 1000
1462
+ }'
1463
+ \`\`\`
1464
+
1465
+ Results contain clicks, impressions, CTR, and average position. Use \`startRow\` with \`rowLimit\` to paginate; Google returns at most 25,000 rows per request.
1466
+
1467
+ ### List sitemaps
1468
+
1469
+ \`\`\`bash
1470
+ curl -s "$MONOLITH_URL/v1/gdrive/search-console/sitemaps?siteUrl=$SITE_URL" "\${GDRIVE_AUTH[@]}"
1471
+ \`\`\`
1472
+
1473
+ Pass a URL-encoded \`sitemapIndex\` query parameter to list sitemaps referenced by a sitemap index. To fetch one sitemap, call \`/v1/gdrive/search-console/sitemap\` with URL-encoded \`siteUrl\` and \`feedpath\` query parameters.
1474
+
1475
+ ### Inspect an indexed URL
1476
+
1477
+ \`\`\`bash
1478
+ curl -s -X POST "$MONOLITH_URL/v1/gdrive/search-console/url-inspection" "\${GDRIVE_AUTH[@]}" \\
1479
+ -H "Content-Type: application/json" \\
1480
+ -d '{
1481
+ "inspectionUrl": "https://example.com/page",
1482
+ "siteUrl": "sc-domain:example.com",
1483
+ "languageCode": "en-US"
1484
+ }'
1485
+ \`\`\`
1486
+
1487
+ URL Inspection reports the version currently in Google's index; it does not run a live URL test.
1488
+
1414
1489
  ## Tips
1415
1490
 
1416
1491
  - **Always return the \`webViewLink\`** to the user when you create or edit a file so they can open it. Get it from \`/v1/gdrive/files/$FILE_ID?fields=webViewLink\` or from \`documents.documentId\` \u2192 \`https://docs.google.com/document/d/$ID/edit\`.
@@ -1418,9 +1493,9 @@ Pass \`?q=...\` to filter using [Drive API search syntax](https://developers.goo
1418
1493
  - **Watch error responses.** A 412 from the gateway means the org has no Google credentials connected \u2014 tell the user to connect Google in the dashboard. Other 4xx responses come straight from Google and usually explain the issue clearly.
1419
1494
  `;
1420
1495
  var GOOGLE_ABILITY = {
1421
- label: "Google Workspace",
1422
- description: "Create / edit Docs, Sheets, Forms, and Drive files via the Replicas gateway.",
1423
- bullet: "- Interacting with Google Workspace (creating and editing Docs, Sheets, and Forms, sharing files, reading form responses, etc.)",
1496
+ label: "Google",
1497
+ description: "Use Google Workspace and read Search Console data via the Replicas gateway.",
1498
+ bullet: "- Interacting with Google Workspace or Search Console (Docs, Sheets, Forms, Drive files, search performance, sitemaps, and URL inspection)",
1424
1499
  section: SECTION5,
1425
1500
  referenceFile: { name: "GOOGLE.md", content: REFERENCE5 }
1426
1501
  };
@@ -2802,6 +2877,7 @@ var WORKSPACE_FILE_UPLOAD_MAX_SIZE_BYTES = 20 * 1024 * 1024;
2802
2877
  var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
2803
2878
  var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
2804
2879
  var WORKSPACE_SORT_CHOICES = [
2880
+ ["manual", "Custom"],
2805
2881
  ["activity", "Last activity"],
2806
2882
  ["created", "Created date"],
2807
2883
  ["status", "Status"]
@@ -8272,7 +8348,7 @@ var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
8272
8348
  var MIN_CODEX_CLI_VERSION = "0.144.0";
8273
8349
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
8274
8350
  var codexCliVersionEnsured = null;
8275
- var ENGINE_PACKAGE_VERSION = "0.1.420";
8351
+ var ENGINE_PACKAGE_VERSION = "0.1.422";
8276
8352
  var INITIALIZE_METHOD = "initialize";
8277
8353
  var INITIALIZED_NOTIFICATION = "initialized";
8278
8354
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -10390,6 +10466,10 @@ import { basename, dirname as dirname5, extname, join as join17 } from "path";
10390
10466
  import { parse as parseYaml2 } from "yaml";
10391
10467
  import { Agent as CursorAgent } from "@cursor/sdk";
10392
10468
  var CURSOR_SLASH_COMMANDS_CACHE_MS = 3e4;
10469
+ var CURSOR_STREAM_INACTIVITY_TIMEOUT_MS = 30 * 6e4;
10470
+ var CURSOR_RUN_SETTLE_TIMEOUT_MS = 3e4;
10471
+ var CURSOR_CANCEL_TIMEOUT_MS = 1e4;
10472
+ var TURN_ABORTED = /* @__PURE__ */ Symbol("cursor-turn-aborted");
10393
10473
  var CURSOR_COMPOSER_CONTEXT_WINDOW = 2e5;
10394
10474
  var CURSOR_CATEGORY_COLORS = {
10395
10475
  input: "#3eeba3",
@@ -10453,6 +10533,7 @@ var CursorManager = class extends CodingAgentManager {
10453
10533
  agent = null;
10454
10534
  activeRun = null;
10455
10535
  activeModel = null;
10536
+ abortActiveTurn = null;
10456
10537
  blockedToolCallIds = /* @__PURE__ */ new Set();
10457
10538
  pendingCursorBlockReason = null;
10458
10539
  historyFilePath;
@@ -10469,7 +10550,27 @@ var CursorManager = class extends CodingAgentManager {
10469
10550
  await mkdir11(dirname5(this.historyFilePath), { recursive: true });
10470
10551
  }
10471
10552
  async interruptActiveTurn() {
10472
- await this.activeRun?.cancel();
10553
+ await this.cancelRun(this.activeRun);
10554
+ this.abortActiveTurn?.();
10555
+ }
10556
+ async cancelRun(run) {
10557
+ if (!run || run.status !== "running") return;
10558
+ try {
10559
+ const result = await raceWithTimeout(run.cancel(), CURSOR_CANCEL_TIMEOUT_MS);
10560
+ if (result === TIMEOUT) {
10561
+ console.error("[CursorManager] Timed out cancelling Cursor run:", run.id);
10562
+ }
10563
+ } catch (error) {
10564
+ console.error("[CursorManager] Failed to cancel Cursor run:", error);
10565
+ }
10566
+ }
10567
+ dispose() {
10568
+ try {
10569
+ this.agent?.close();
10570
+ } catch (error) {
10571
+ console.error("[CursorManager] Failed to close Cursor agent:", error);
10572
+ }
10573
+ this.agent = null;
10473
10574
  }
10474
10575
  async getHistory() {
10475
10576
  await this.historyFile.flush();
@@ -10521,6 +10622,10 @@ var CursorManager = class extends CodingAgentManager {
10521
10622
  return this.agent;
10522
10623
  }
10523
10624
  async processMessageInternal(request) {
10625
+ let run = null;
10626
+ const aborted = new Promise((resolve4) => {
10627
+ this.abortActiveTurn = () => resolve4(TURN_ABORTED);
10628
+ });
10524
10629
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
10525
10630
  const linearForwarder = new LinearEventForwarder(linearSessionId);
10526
10631
  try {
@@ -10531,7 +10636,7 @@ var CursorManager = class extends CodingAgentManager {
10531
10636
  type: "user_message",
10532
10637
  message: request.message
10533
10638
  }, this.historyFile);
10534
- const run = await agent.send(message, {
10639
+ run = await agent.send(message, {
10535
10640
  model: { id: model },
10536
10641
  mode: request.planMode ? "plan" : "agent",
10537
10642
  onDelta: async (args) => {
@@ -10544,7 +10649,25 @@ var CursorManager = class extends CodingAgentManager {
10544
10649
  await run.cancel();
10545
10650
  throw new Error(this.pendingCursorBlockReason);
10546
10651
  }
10547
- for await (const event of run.stream()) {
10652
+ const events = run.stream()[Symbol.asyncIterator]();
10653
+ let interrupted = false;
10654
+ while (true) {
10655
+ const nextPromise = events.next();
10656
+ nextPromise.catch(() => {
10657
+ });
10658
+ const next = await raceWithTimeout(
10659
+ Promise.race([nextPromise, aborted]),
10660
+ CURSOR_STREAM_INACTIVITY_TIMEOUT_MS
10661
+ );
10662
+ if (next === TIMEOUT) {
10663
+ throw new Error(`Cursor run stalled: no events for ${CURSOR_STREAM_INACTIVITY_TIMEOUT_MS / 6e4} minutes; cancelling the run`);
10664
+ }
10665
+ if (next === TURN_ABORTED) {
10666
+ interrupted = true;
10667
+ break;
10668
+ }
10669
+ if (next.done) break;
10670
+ const event = next.value;
10548
10671
  this.recordCursorEvent(event);
10549
10672
  if (event.type === "tool_call" && event.status === "running") {
10550
10673
  await this.handleCursorToolCall(cursorToolInputFromMessage(event));
@@ -10553,7 +10676,14 @@ var CursorManager = class extends CodingAgentManager {
10553
10676
  linearForwarder.sendEvent(convertCursorEvent(event, linearSessionId));
10554
10677
  }
10555
10678
  }
10556
- const result = await run.wait();
10679
+ if (interrupted) {
10680
+ await this.cancelRun(run);
10681
+ return;
10682
+ }
10683
+ const result = await raceWithTimeout(run.wait(), CURSOR_RUN_SETTLE_TIMEOUT_MS);
10684
+ if (result === TIMEOUT) {
10685
+ throw new Error("Cursor run did not report a result after its event stream ended");
10686
+ }
10557
10687
  if (result.status === "error") {
10558
10688
  this.recordHistoryEvent("cursor-error", {
10559
10689
  type: "error",
@@ -10564,11 +10694,13 @@ var CursorManager = class extends CodingAgentManager {
10564
10694
  linearForwarder.flushThoughtAsResponse();
10565
10695
  }
10566
10696
  } catch (error) {
10697
+ await this.cancelRun(run);
10567
10698
  this.recordHistoryEvent("cursor-error", {
10568
10699
  type: "error",
10569
10700
  message: error instanceof Error ? error.message : String(error)
10570
10701
  }, this.historyFile);
10571
10702
  } finally {
10703
+ this.abortActiveTurn = null;
10572
10704
  this.activeRun = null;
10573
10705
  this.activeModel = null;
10574
10706
  this.blockedToolCallIds.clear();
@@ -12305,6 +12437,7 @@ function acceptedEventInCodexTranscript(acceptedEvent, transcript) {
12305
12437
  return getCodexTranscriptUserMessages(transcript).includes(message);
12306
12438
  }
12307
12439
  var LAST_MESSAGE_PREVIEW_MAX = 200;
12440
+ var MAX_ACCEPTED_SEND_RESPONSES = 50;
12308
12441
  function isChatActive(chat) {
12309
12442
  return chat.provider.isProcessing() || (chat.provider.hasActiveBackgroundTasks?.() ?? false);
12310
12443
  }
@@ -12463,7 +12596,20 @@ var ChatService = class {
12463
12596
  }
12464
12597
  async sendMessage(chatId, request) {
12465
12598
  const chat = this.requireChat(chatId);
12599
+ if (request.idempotencyKey) {
12600
+ const accepted = chat.acceptedSendResponses.get(request.idempotencyKey);
12601
+ if (accepted) {
12602
+ return accepted;
12603
+ }
12604
+ }
12466
12605
  const result = await chat.provider.enqueueMessage(request);
12606
+ if (request.idempotencyKey) {
12607
+ chat.acceptedSendResponses.set(request.idempotencyKey, result);
12608
+ for (const key of chat.acceptedSendResponses.keys()) {
12609
+ if (chat.acceptedSendResponses.size <= MAX_ACCEPTED_SEND_RESPONSES) break;
12610
+ chat.acceptedSendResponses.delete(key);
12611
+ }
12612
+ }
12467
12613
  const acceptedEvent = createUserMessageEvent(request.message, result.messageId, request.images);
12468
12614
  chat.pendingMessageIds.push(result.messageId);
12469
12615
  chat.acceptedUserEvents.set(result.messageId, acceptedEvent);
@@ -12644,6 +12790,7 @@ var ChatService = class {
12644
12790
  const target = this.chats.get(id);
12645
12791
  if (!target) continue;
12646
12792
  this.chats.delete(id);
12793
+ target.provider.dispose?.();
12647
12794
  await this.deleteHistoryFile(target.persisted);
12648
12795
  await this.publish({ type: "chat.deleted", payload: { chatId: id } });
12649
12796
  }
@@ -12788,6 +12935,7 @@ var ChatService = class {
12788
12935
  provider,
12789
12936
  pendingMessageIds: [],
12790
12937
  acceptedUserEvents: /* @__PURE__ */ new Map(),
12938
+ acceptedSendResponses: /* @__PURE__ */ new Map(),
12791
12939
  activeMessageId: null,
12792
12940
  hasActiveTurn: false,
12793
12941
  observedBranchesByRepo: /* @__PURE__ */ new Map()
@@ -13726,6 +13874,7 @@ var sendMessageSchema = z2.object({
13726
13874
  enableInteractiveTools: z2.boolean().optional(),
13727
13875
  type: z2.string().min(1).optional(),
13728
13876
  merge: z2.boolean().optional(),
13877
+ idempotencyKey: z2.string().min(1).max(128).optional(),
13729
13878
  senderUserId: z2.string().optional(),
13730
13879
  senderEmail: z2.string().optional(),
13731
13880
  senderDisplayName: z2.string().optional()
@@ -14566,7 +14715,16 @@ process.on("uncaughtException", (error) => {
14566
14715
  console.error("[FATAL] Uncaught exception:", error);
14567
14716
  engineLogger.flush().finally(() => process.exit(1));
14568
14717
  });
14718
+ function isCursorSdkRejection(reason) {
14719
+ const text = reason instanceof Error ? `${reason.stack ?? ""}
14720
+ ${reason.message}` : String(reason);
14721
+ return text.includes("@cursor/sdk");
14722
+ }
14569
14723
  process.on("unhandledRejection", (reason) => {
14724
+ if (isCursorSdkRejection(reason)) {
14725
+ console.error("[CursorSDK] Unhandled rejection (non-fatal):", reason);
14726
+ return;
14727
+ }
14570
14728
  console.error("[FATAL] Unhandled rejection:", reason);
14571
14729
  engineLogger.flush().finally(() => process.exit(1));
14572
14730
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.420",
3
+ "version": "0.1.422",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",