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.
- package/dist/src/index.js +231 -53
- 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-
|
|
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
|
|
3566
|
-
|
|
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
|
-
|
|
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
|
|
3959
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4053
|
+
info = { provider: "unknown" };
|
|
3973
4054
|
}
|
|
3974
|
-
this.
|
|
3975
|
-
return
|
|
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.
|
|
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
|
-
|
|
9305
|
-
|
|
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
|
|
9313
|
-
if (
|
|
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,
|
|
9330
|
-
const
|
|
9331
|
-
const
|
|
9332
|
-
const
|
|
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(
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
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();
|