replicas-engine 0.1.335 → 0.1.337

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 +231 -53
  2. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -15,6 +15,10 @@ function isRecord(value) {
15
15
  }
16
16
 
17
17
  // ../shared/src/agent.ts
18
+ var VALID_AGENT_PROVIDERS = ["claude", "codex", "cursor", "relay"];
19
+ var VALID_CODING_AGENT_PROVIDERS = VALID_AGENT_PROVIDERS.filter(
20
+ (provider) => provider !== "relay"
21
+ );
18
22
  var CODEX_REASONING_EFFORT_BY_THINKING_LEVEL = {
19
23
  low: "low",
20
24
  medium: "medium",
@@ -291,7 +295,7 @@ var WORKSPACE_SIZES = ["small", "large"];
291
295
  var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
292
296
 
293
297
  // ../shared/src/e2b.ts
294
- var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-22-v1";
298
+ var E2B_TEMPLATE_NAME = "replicas-sandbox-2026-06-22-v3";
295
299
 
296
300
  // ../shared/src/runtime-env.ts
297
301
  function parsePosixEnvFile(content) {
@@ -377,6 +381,51 @@ var InFlightMap = class {
377
381
  }
378
382
  };
379
383
 
384
+ // ../shared/src/github/url.ts
385
+ var GITHUB_HOST_RE = /(^|@|\/\/)github\.com[:/]/;
386
+ function isGitHubUrl(url) {
387
+ if (!url) return false;
388
+ return GITHUB_HOST_RE.test(url);
389
+ }
390
+ function stripCredentialsFromUrl(url) {
391
+ try {
392
+ const parsed = new URL(url);
393
+ if (parsed.username || parsed.password) {
394
+ parsed.username = "";
395
+ parsed.password = "";
396
+ return parsed.toString();
397
+ }
398
+ } catch {
399
+ }
400
+ return url;
401
+ }
402
+
403
+ // ../shared/src/urls.ts
404
+ function decodePathSegments(segments) {
405
+ return segments.map((segment) => {
406
+ try {
407
+ return decodeURIComponent(segment);
408
+ } catch {
409
+ return segment;
410
+ }
411
+ });
412
+ }
413
+ function normalizeRepositoryUrl(url, options = {}) {
414
+ if (!url) return null;
415
+ const stripped = stripCredentialsFromUrl(url).trim().replace(/\/+$/, "").replace(/\.git$/, "");
416
+ if (!stripped) return null;
417
+ try {
418
+ const parsed = new URL(stripped);
419
+ parsed.hash = "";
420
+ parsed.search = "";
421
+ parsed.pathname = parsed.pathname.replace(/\/+$/, "").replace(/\.git$/, "");
422
+ const normalized = `${parsed.origin}${parsed.pathname}`;
423
+ return options.lowercase ? normalized.toLowerCase() : normalized;
424
+ } catch {
425
+ return options.lowercase ? stripped.toLowerCase() : stripped;
426
+ }
427
+ }
428
+
380
429
  // ../shared/src/slash-commands.ts
381
430
  var MAX_CODEX_GOAL_OBJECTIVE_CHARS = 4e3;
382
431
  function parseGoalCommand(message) {
@@ -2734,13 +2783,6 @@ var MEDIA_KIND = {
2734
2783
  };
2735
2784
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
2736
2785
 
2737
- // ../shared/src/github/url.ts
2738
- var GITHUB_HOST_RE = /(^|@|\/\/)github\.com[:/]/;
2739
- function isGitHubUrl(url) {
2740
- if (!url) return false;
2741
- return GITHUB_HOST_RE.test(url);
2742
- }
2743
-
2744
2786
  // ../shared/src/skill-registry.ts
2745
2787
  var SKILL_REGISTRY_MANIFEST_VERSION = 1;
2746
2788
 
@@ -3562,8 +3604,8 @@ var GitService = class {
3562
3604
  defaultBranchCache = /* @__PURE__ */ new Map();
3563
3605
  cachedPrByRepo = /* @__PURE__ */ new Map();
3564
3606
  // No invalidation on purpose — `git remote set-url` mid-session is rare and
3565
- // the worst case is `gh pr view` stays skipped until the next engine restart.
3566
- originIsGitHubCache = /* @__PURE__ */ new Map();
3607
+ // the worst case is PR/MR lookup stays skipped until the next engine restart.
3608
+ originInfoCache = /* @__PURE__ */ new Map();
3567
3609
  // Broadcaster + UI /repos requests fan in here every ~2s; one shared run.
3568
3610
  listReposInFlight = new InFlightMap();
3569
3611
  diffStatsInFlight = new InFlightMap();
@@ -3926,7 +3968,8 @@ var GitService = class {
3926
3968
  }
3927
3969
  }
3928
3970
  async lookupPrOnRemote(repoName, repoPath, branch) {
3929
- if (!await this.originIsGitHub(repoPath)) {
3971
+ const origin = await this.getOriginInfo(repoPath);
3972
+ if (origin.provider === "unknown") {
3930
3973
  return { status: "not_found" };
3931
3974
  }
3932
3975
  try {
@@ -3941,6 +3984,9 @@ var GitService = class {
3941
3984
  } catch {
3942
3985
  return { status: "error" };
3943
3986
  }
3987
+ if (origin.provider === "gitlab") {
3988
+ return this.lookupGitLabMrOnRemote(repoName, origin.gitLab, branch);
3989
+ }
3944
3990
  try {
3945
3991
  const { stdout } = await execFileAsync("gh", ["pr", "view", branch, "--json", "url", "--jq", ".url"], {
3946
3992
  cwd: repoPath,
@@ -3955,24 +4001,120 @@ var GitService = class {
3955
4001
  return { status: "error" };
3956
4002
  }
3957
4003
  }
3958
- async originIsGitHub(repoPath) {
3959
- const cached = this.originIsGitHubCache.get(repoPath);
4004
+ async lookupGitLabMrOnRemote(repoName, remote, branch) {
4005
+ const token = await this.getGitLabAccessToken(remote.host);
4006
+ if (!token) {
4007
+ return { status: "not_found" };
4008
+ }
4009
+ const url = `${remote.instanceUrl}/api/v4/projects/${encodeURIComponent(remote.projectPath)}/merge_requests?state=opened&source_branch=${encodeURIComponent(branch)}`;
4010
+ try {
4011
+ const response = await fetch(url, {
4012
+ headers: { Authorization: `Bearer ${token}` },
4013
+ signal: AbortSignal.timeout(1e4)
4014
+ });
4015
+ if (!response.ok) {
4016
+ console.warn(`[GitService] GitLab MR lookup failed for ${repoName}: ${response.status}`);
4017
+ return response.status === 404 ? { status: "not_found" } : { status: "error" };
4018
+ }
4019
+ const data = await response.json();
4020
+ if (!Array.isArray(data)) return { status: "not_found" };
4021
+ const mergeRequest = data.find((item) => isRecord4(item) && item.source_branch === branch) ?? data[0];
4022
+ if (!isRecord4(mergeRequest)) return { status: "not_found" };
4023
+ const webUrl = mergeRequest.web_url;
4024
+ if (typeof webUrl === "string" && webUrl.length > 0) return { status: "found", url: webUrl };
4025
+ const iid = mergeRequest.iid;
4026
+ return typeof iid === "number" ? { status: "found", url: `${remote.instanceUrl}/${remote.projectPath}/-/merge_requests/${iid}` } : { status: "not_found" };
4027
+ } catch (error) {
4028
+ const message = error instanceof Error ? error.message : String(error);
4029
+ console.warn(`[GitService] GitLab MR lookup failed for ${repoName}: ${message}`);
4030
+ return { status: "error" };
4031
+ }
4032
+ }
4033
+ async getOriginInfo(repoPath) {
4034
+ const cached = this.originInfoCache.get(repoPath);
3960
4035
  if (cached !== void 0) {
3961
4036
  return cached;
3962
4037
  }
3963
- let isGitHub = false;
4038
+ let info = { provider: "unknown" };
3964
4039
  try {
3965
4040
  const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
3966
4041
  cwd: repoPath,
3967
4042
  encoding: "utf-8",
3968
4043
  maxBuffer: SUBPROCESS_MAX_BUFFER
3969
4044
  });
3970
- isGitHub = isGitHubUrl(stdout.trim());
4045
+ const originUrl = stdout.trim();
4046
+ if (isGitHubUrl(originUrl)) {
4047
+ info = { provider: "github" };
4048
+ } else {
4049
+ const gitLabRemote = this.parseGitLabRemote(originUrl);
4050
+ if (gitLabRemote) info = { provider: "gitlab", gitLab: gitLabRemote };
4051
+ }
3971
4052
  } catch {
3972
- isGitHub = false;
4053
+ info = { provider: "unknown" };
3973
4054
  }
3974
- this.originIsGitHubCache.set(repoPath, isGitHub);
3975
- return isGitHub;
4055
+ this.originInfoCache.set(repoPath, info);
4056
+ return info;
4057
+ }
4058
+ parseGitLabRemote(remoteUrl) {
4059
+ const sshUrl = remoteUrl.trim().replace(/\/+$/, "").replace(/\.git$/, "");
4060
+ try {
4061
+ const parsed = new URL(sshUrl);
4062
+ if (parsed.protocol === "ssh:") {
4063
+ const projectPath = decodePathSegments(parsed.pathname.split("/").filter(Boolean)).join("/");
4064
+ if (!parsed.hostname || !projectPath) return null;
4065
+ const host = parsed.hostname.toLowerCase();
4066
+ return {
4067
+ host,
4068
+ instanceUrl: `https://${host}`,
4069
+ projectPath
4070
+ };
4071
+ }
4072
+ } catch {
4073
+ }
4074
+ const scpLike = sshUrl.match(/^git@([^:/]+):(?:(?:\d+)\/)?(.+)$/);
4075
+ if (scpLike) {
4076
+ const [, host, projectPath] = scpLike;
4077
+ if (!host || !projectPath) return null;
4078
+ const decodedProjectPath = decodePathSegments(projectPath.split("/").filter(Boolean)).join("/");
4079
+ return {
4080
+ host: host.toLowerCase(),
4081
+ instanceUrl: `https://${host}`,
4082
+ projectPath: decodedProjectPath
4083
+ };
4084
+ }
4085
+ const normalized = normalizeRepositoryUrl(remoteUrl);
4086
+ if (!normalized) return null;
4087
+ try {
4088
+ const parsed = new URL(normalized);
4089
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
4090
+ const projectPath = decodePathSegments(parsed.pathname.split("/").filter(Boolean)).join("/");
4091
+ if (!projectPath) return null;
4092
+ return {
4093
+ host: parsed.host.toLowerCase(),
4094
+ instanceUrl: parsed.origin,
4095
+ projectPath
4096
+ };
4097
+ } catch {
4098
+ }
4099
+ return null;
4100
+ }
4101
+ async getGitLabAccessToken(host) {
4102
+ try {
4103
+ const credentials = await readFile3(join5(ENGINE_ENV.HOME_DIR, ".git-credentials"), "utf-8");
4104
+ for (const line of credentials.split("\n")) {
4105
+ const trimmed = line.trim();
4106
+ if (!trimmed) continue;
4107
+ try {
4108
+ const parsed = new URL(trimmed);
4109
+ if (parsed.protocol === "https:" && parsed.host.toLowerCase() === host && parsed.password) {
4110
+ return decodeURIComponent(parsed.password);
4111
+ }
4112
+ } catch {
4113
+ }
4114
+ }
4115
+ } catch {
4116
+ }
4117
+ return null;
3976
4118
  }
3977
4119
  async resolveDefaultBranch(repoPath) {
3978
4120
  const cached = this.defaultBranchCache.get(repoPath);
@@ -7359,7 +7501,7 @@ var AspClient = class {
7359
7501
  // src/managers/codex-asp/app-server-process.ts
7360
7502
  var DEFAULT_CODEX_BINARY = "codex";
7361
7503
  var DEFAULT_CODEX_ARGS = ["app-server", "--listen", "stdio://"];
7362
- var ENGINE_PACKAGE_VERSION = "0.1.335";
7504
+ var ENGINE_PACKAGE_VERSION = "0.1.337";
7363
7505
  var INITIALIZE_METHOD = "initialize";
7364
7506
  var INITIALIZED_NOTIFICATION = "initialized";
7365
7507
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -9247,8 +9389,29 @@ var CursorManager = class extends CodingAgentManager {
9247
9389
  // src/managers/relay-tools.ts
9248
9390
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
9249
9391
  import { z } from "zod";
9392
+
9393
+ // src/managers/relay-providers.ts
9394
+ function getAvailableRelayProviders(availability) {
9395
+ const codexAvailable = availability.codexAvailable ?? false;
9396
+ const cursorAvailable = availability.cursorAvailable ?? false;
9397
+ const providers = ["claude"];
9398
+ if (codexAvailable) providers.push("codex");
9399
+ if (cursorAvailable) providers.push("cursor");
9400
+ providers.push("relay");
9401
+ return providers;
9402
+ }
9403
+ function getAvailableCodeProviders(availability) {
9404
+ return getAvailableRelayProviders(availability).filter((provider) => provider === "codex" || provider === "cursor");
9405
+ }
9406
+
9407
+ // src/managers/relay-tools.ts
9250
9408
  var POLL_INTERVAL_MS = 2e3;
9251
9409
  var DEFAULT_SUBAGENT_TIMEOUT_MS = 6e5;
9410
+ function extractTextBlocks(content, textBlockType, separator = "\n") {
9411
+ if (!Array.isArray(content)) return null;
9412
+ const texts = content.map((block) => block && typeof block === "object" && "type" in block && block.type === textBlockType && "text" in block && typeof block.text === "string" ? block.text : "").filter(Boolean);
9413
+ return texts.length > 0 ? texts.join(separator) : null;
9414
+ }
9252
9415
  async function engineFetch(path5, options) {
9253
9416
  const baseUrl = `http://localhost:${ENGINE_ENV.REPLICAS_ENGINE_PORT}`;
9254
9417
  return fetch(`${baseUrl}${path5}`, {
@@ -9301,21 +9464,12 @@ async function getChatFinalResponse(chatId) {
9301
9464
  }
9302
9465
  if (event.type === "claude-assistant") {
9303
9466
  const message = payload.message;
9304
- if (message?.content) {
9305
- const textBlocks = message.content.filter((block) => block.type === "text" && block.text).map((block) => block.text);
9306
- if (textBlocks.length > 0) {
9307
- return textBlocks.join("\n");
9308
- }
9309
- }
9467
+ const text = extractTextBlocks(message?.content, "text");
9468
+ if (text) return text;
9310
9469
  }
9311
9470
  if (event.type === "response_item" && payload.type === "message" && payload.role === "assistant") {
9312
- const content = payload.content;
9313
- if (Array.isArray(content)) {
9314
- const texts = content.filter((block) => block.type === "output_text" && block.text).map((block) => block.text);
9315
- if (texts.length > 0) {
9316
- return texts.join("\n");
9317
- }
9318
- }
9471
+ const text = extractTextBlocks(payload.content, "output_text");
9472
+ if (text) return text;
9319
9473
  }
9320
9474
  if (event.type === "event_msg" && payload.type === "agent_reasoning") {
9321
9475
  const text = payload.text;
@@ -9323,13 +9477,22 @@ async function getChatFinalResponse(chatId) {
9323
9477
  return text;
9324
9478
  }
9325
9479
  }
9480
+ if (event.type === "cursor-assistant") {
9481
+ const message = payload.message;
9482
+ const text = extractTextBlocks(message?.content, "text", "");
9483
+ if (text) return text;
9484
+ }
9326
9485
  }
9327
9486
  return "[No response from subagent]";
9328
9487
  }
9329
- function buildSpawnAgentTool(parentChatId, codexAvailable = false) {
9330
- const providerEnum = codexAvailable ? z.enum(["claude", "codex", "relay"]) : z.enum(["claude", "relay"]);
9331
- const providerDesc = codexAvailable ? "Which agent to use. Prefer codex for code writing, claude for exploration/analysis, relay for complex multi-step orchestration." : "Which agent to use. Use claude for code writing, exploration, and analysis. Use relay for complex multi-step orchestration.";
9332
- const useCases = codexAvailable ? `- Complex code writing tasks (use provider 'codex' with a capable model)
9488
+ function buildSpawnAgentTool(parentChatId, availability = {}) {
9489
+ const codexAvailable = availability.codexAvailable ?? false;
9490
+ const cursorAvailable = availability.cursorAvailable ?? false;
9491
+ const availableProviders = getAvailableRelayProviders(availability);
9492
+ const providerEnum = z.enum(availableProviders);
9493
+ const codeProviders = getAvailableCodeProviders(availability);
9494
+ const providerDesc = codeProviders.length > 0 ? `Which agent to use. Prefer ${codeProviders.join(" or ")} for code writing, claude for exploration/analysis, relay for complex multi-step orchestration.` : "Which agent to use. Use claude for code writing, exploration, and analysis. Use relay for complex multi-step orchestration.";
9495
+ const useCases = codeProviders.length > 0 ? `- Complex code writing tasks (use provider '${codeProviders.join("' or '")}' with a capable model)
9333
9496
  - Codebase exploration that would consume many tokens (use provider 'claude')` : `- Complex code writing tasks (use provider 'claude')
9334
9497
  - Codebase exploration that would consume many tokens (use provider 'claude')`;
9335
9498
  return tool(
@@ -9346,9 +9509,13 @@ You will also receive the chatId so you can send follow-up messages or clean up
9346
9509
  {
9347
9510
  provider: providerEnum.describe(providerDesc),
9348
9511
  prompt: z.string().describe("The full prompt/instructions for the subagent. Be detailed - it has no context from your conversation."),
9349
- model: z.string().optional().describe(codexAvailable ? `Model override. Claude: ${AGENT_MODELS.claude.join(", ")} (opus[1m] is the default, 1M context, use for very large codebases or huge context tasks). Codex: gpt-5.5, gpt-5.4, gpt-5.3-codex, etc.` : `Model override. Claude: ${AGENT_MODELS.claude.join(", ")} (opus[1m] is the default, 1M context, use for very large codebases or huge context tasks).`),
9512
+ model: z.string().optional().describe([
9513
+ `Model override. Claude: ${AGENT_MODELS.claude.join(", ")} (opus[1m] is the default, 1M context, use for very large codebases or huge context tasks).`,
9514
+ codexAvailable ? "Codex: gpt-5.5, gpt-5.4, gpt-5.3-codex, etc." : null,
9515
+ cursorAvailable ? `Cursor: ${AGENT_MODELS.cursor.join(", ")}.` : null
9516
+ ].filter(Boolean).join(" ")),
9350
9517
  thinking_level: z.enum(["low", "medium", "high", "max"]).optional().describe(
9351
- "Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, max = maximum effort. Defaults: Claude = high, Codex = medium."
9518
+ "Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, max = maximum effort. Defaults: Claude = high, Codex = medium, Cursor = medium."
9352
9519
  ),
9353
9520
  title: z.string().optional().describe("Optional title for the subagent chat (for identification)."),
9354
9521
  timeout_minutes: z.number().positive().optional().describe("Timeout in minutes for the subagent to complete (default: 10). Set higher for large tasks to avoid losing work.")
@@ -9416,7 +9583,7 @@ The tool blocks until the subagent completes and returns its response.`,
9416
9583
  message: z.string().describe("The follow-up message to send."),
9417
9584
  model: z.string().optional().describe("Optional model override for this message."),
9418
9585
  thinking_level: z.enum(["low", "medium", "high", "max"]).optional().describe(
9419
- "Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, max = maximum effort. Defaults: Claude = high, Codex = medium."
9586
+ "Controls how much thinking/reasoning the subagent applies. low = light thinking, medium = moderate, high = deep reasoning, max = maximum effort. Defaults: Claude = high, Codex = medium, Cursor = medium."
9420
9587
  ),
9421
9588
  timeout_minutes: z.number().positive().optional().describe("Timeout in minutes for the subagent to complete (default: 10). Set higher for large tasks to avoid losing work.")
9422
9589
  },
@@ -9474,11 +9641,11 @@ var deleteAgentTool = tool(
9474
9641
  }
9475
9642
  }
9476
9643
  );
9477
- function createRelayMcpServer(parentChatId, codexAvailable = false) {
9644
+ function createRelayMcpServer(parentChatId, availability = {}) {
9478
9645
  return createSdkMcpServer({
9479
9646
  name: "relay-subagent-tools",
9480
9647
  version: "1.0.0",
9481
- tools: [buildSpawnAgentTool(parentChatId, codexAvailable), messageAgentTool, deleteAgentTool]
9648
+ tools: [buildSpawnAgentTool(parentChatId, availability), messageAgentTool, deleteAgentTool]
9482
9649
  });
9483
9650
  }
9484
9651
 
@@ -9580,11 +9747,15 @@ function getUsingToolsSection() {
9580
9747
  ];
9581
9748
  return [`# Using your tools`, ...prependBullets(items)].join("\n");
9582
9749
  }
9583
- function getDelegationSection(codexAvailable) {
9584
- const providerList = codexAvailable ? "claude, codex, or relay" : "claude or relay";
9750
+ function getDelegationSection(codexAvailable, cursorAvailable) {
9751
+ const providerList = getAvailableRelayProviders({ codexAvailable, cursorAvailable }).join(", ");
9585
9752
  const spawnDesc = `Create a new subagent with a specific provider (${providerList}), send it a prompt, and wait for its response. Returns the chatId and the agent's final response. You can set a custom timeout via the timeout_minutes parameter (default: 10 minutes).`;
9586
9753
  const claudeModelList = AGENT_MODELS.claude.join(", ");
9587
- const agentSelectionLines = codexAvailable ? `Use provider 'codex' for heavy code writing, implementation, and large refactors. Suggested models: gpt-5.5 (default), gpt-5.4, gpt-5.3-codex.
9754
+ const extraAgentLines = [
9755
+ codexAvailable ? `Use provider 'codex' for heavy code writing, implementation, and large refactors. Suggested models: gpt-5.5 (default), gpt-5.4, gpt-5.3-codex.` : null,
9756
+ cursorAvailable ? `Use provider 'cursor' for fast iteration on code changes. Suggested models: ${AGENT_MODELS.cursor.join(", ")}.` : null
9757
+ ].filter(Boolean);
9758
+ const agentSelectionLines = extraAgentLines.length > 0 ? `${extraAgentLines.join("\n\n")}
9588
9759
 
9589
9760
  Use provider 'claude' for codebase exploration, code review, planning, complex debugging, and tasks requiring nuanced architectural understanding. Suggested models: ${claudeModelList} (opus[1m] is the default, 1M context window, use for very large codebases; sonnet is faster).` : `Use provider 'claude' for all tasks including code writing, codebase exploration, code review, planning, complex debugging, and tasks requiring nuanced architectural understanding. Suggested models: ${claudeModelList} (opus[1m] is the default, 1M context window, use for very large codebases; sonnet is faster).`;
9590
9761
  return `# Delegation
@@ -9704,14 +9875,14 @@ function getEnvironmentSection() {
9704
9875
  ].join("\n");
9705
9876
  }
9706
9877
  function buildRelaySystemPrompt(options) {
9707
- const { customInstructions, codexAvailable } = options ?? {};
9878
+ const { customInstructions, codexAvailable, cursorAvailable } = options ?? {};
9708
9879
  const sections = [
9709
9880
  getIntroSection(),
9710
9881
  getSystemSection(),
9711
9882
  getDoingTasksSection(),
9712
9883
  getActionsSection(),
9713
9884
  getUsingToolsSection(),
9714
- getDelegationSection(codexAvailable ?? false),
9885
+ getDelegationSection(codexAvailable ?? false, cursorAvailable ?? false),
9715
9886
  getToneAndStyleSection(),
9716
9887
  getOutputEfficiencySection(),
9717
9888
  getEnvironmentSection(),
@@ -9742,12 +9913,13 @@ var RelayManager = class {
9742
9913
  inner;
9743
9914
  constructor(options) {
9744
9915
  const codexAvailable = options.codexAvailable ?? false;
9916
+ const cursorAvailable = options.cursorAvailable ?? false;
9745
9917
  this.inner = new ClaudeManager({
9746
9918
  ...options,
9747
- systemPromptOverride: (customInstructions) => buildRelaySystemPrompt({ customInstructions, codexAvailable }),
9919
+ systemPromptOverride: (customInstructions) => buildRelaySystemPrompt({ customInstructions, codexAvailable, cursorAvailable }),
9748
9920
  tools: RELAY_TOOLS,
9749
9921
  mcpServers: {
9750
- "relay-subagent-tools": createRelayMcpServer(options.chatId, codexAvailable)
9922
+ "relay-subagent-tools": createRelayMcpServer(options.chatId, { codexAvailable, cursorAvailable })
9751
9923
  },
9752
9924
  envOverrides: {
9753
9925
  CLAUDE_CODE_STREAM_CLOSE_TIMEOUT: "900000"
@@ -10114,6 +10286,9 @@ function isChatMessageSender(value) {
10114
10286
  function isCodexAvailable() {
10115
10287
  return existsSync7(CODEX_AUTH_PATH2) || Boolean(ENGINE_ENV.OPENAI_API_KEY);
10116
10288
  }
10289
+ function isCursorAvailable() {
10290
+ return Boolean(ENGINE_ENV.CURSOR_API_KEY);
10291
+ }
10117
10292
  function isSameAcceptedUserEvent(event, acceptedEvent) {
10118
10293
  if (areSameUserMessageEvents(event, acceptedEvent)) return true;
10119
10294
  const eventMessage = getUserMessage(event);
@@ -10555,7 +10730,8 @@ var ChatService = class {
10555
10730
  onTurnComplete: onProviderTurnComplete,
10556
10731
  onEvent: onProviderEvent,
10557
10732
  chatId: persisted.id,
10558
- codexAvailable: isCodexAvailable()
10733
+ codexAvailable: isCodexAvailable(),
10734
+ cursorAvailable: isCursorAvailable()
10559
10735
  });
10560
10736
  } else if (persisted.provider === "cursor") {
10561
10737
  provider = new CursorManager({
@@ -10798,10 +10974,12 @@ var ChatService = class {
10798
10974
  }
10799
10975
  }
10800
10976
  async publishAgentTurnCompleteWebhook(chat) {
10801
- try {
10802
- await githubTokenManager.refreshOnce();
10803
- } catch {
10804
- }
10977
+ await Promise.all([
10978
+ githubTokenManager.refreshOnce().catch(() => {
10979
+ }),
10980
+ gitlabTokenManager.refreshOnce().catch(() => {
10981
+ })
10982
+ ]);
10805
10983
  const linearSessionId = ENGINE_ENV.LINEAR_SESSION_ID;
10806
10984
  const observedBranches = chat.observedBranchesByRepo;
10807
10985
  chat.observedBranchesByRepo = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.335",
3
+ "version": "0.1.337",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",