@sideboard-ai/core 0.1.44 → 0.1.46
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/agents/cursor-runner.cjs +11 -4
- package/dist/agents/cursor-runner.js +12 -5
- package/dist/{agents-PP3URTSF.js → agents-6L6VENDT.js} +25 -5
- package/dist/{app-settings-ZKVZHJPQ.js → app-settings-LZP632KI.js} +5 -1
- package/dist/{chunk-JM2TVGNW.js → chunk-6NAPN2N5.js} +4 -1
- package/dist/{chunk-V3S4NF5F.js → chunk-7DTJFP4D.js} +406 -30
- package/dist/{chunk-7PCTK4WO.js → chunk-FV6FN6V5.js} +1 -1
- package/dist/chunk-GI7E5733.js +30 -0
- package/dist/{chunk-PU27NUO4.js → chunk-J5JTEJ5O.js} +7 -2
- package/dist/{chunk-ENSD62HW.js → chunk-O6W3P7V3.js} +3 -1
- package/dist/{chunk-I3PKMLFW.js → chunk-SPCU3MFY.js} +8 -4
- package/dist/{chunk-TXFJEXFB.js → chunk-SZAKJ4TR.js} +2 -2
- package/dist/{chunk-FSIK442J.js → chunk-T5QQVXK3.js} +24 -0
- package/dist/{chunk-XX26NCB6.js → chunk-TQK2OYPU.js} +246 -6
- package/dist/{coordinator-prompt-I5YNS27B.js → coordinator-prompt-2XFYG3C5.js} +3 -3
- package/dist/{global-workspace-FU6UIPDY.js → global-workspace-XFOXEQCP.js} +5 -4
- package/dist/index.cjs +747 -18
- package/dist/index.d.cts +96 -2
- package/dist/index.d.ts +96 -2
- package/dist/index.js +33 -9
- package/dist/mcp/run-stdio.cjs +737 -28
- package/dist/mcp/run-stdio.js +10 -9
- package/dist/{thread-store-EHROA3VZ.js → thread-store-OV2X6PYO.js} +1 -1
- package/dist/{workspaces-RSEDTBGJ.js → workspaces-SRL2HZTB.js} +6 -5
- package/dist/{worktree-N4PRV4V3.js → worktree-TEDAJ57S.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -412,6 +412,8 @@ __export(app_settings_exports, {
|
|
|
412
412
|
isLinearConnected: () => isLinearConnected,
|
|
413
413
|
loadAppSettings: () => loadAppSettings,
|
|
414
414
|
maxConcurrentAgents: () => maxConcurrentAgents,
|
|
415
|
+
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
416
|
+
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
415
417
|
resolveClaudeExecutable: () => resolveClaudeExecutable,
|
|
416
418
|
resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
|
|
417
419
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
@@ -523,6 +525,12 @@ function normalizeAdvanced(raw) {
|
|
|
523
525
|
if (typeof source.autoCleanupOrphans === "boolean") {
|
|
524
526
|
out.autoCleanupOrphans = source.autoCleanupOrphans;
|
|
525
527
|
}
|
|
528
|
+
if (source.orchestrationQuotaOnLimit === "switch_agent" || source.orchestrationQuotaOnLimit === "wait_reset") {
|
|
529
|
+
out.orchestrationQuotaOnLimit = source.orchestrationQuotaOnLimit;
|
|
530
|
+
}
|
|
531
|
+
if (typeof source.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(source.orchestrationQuotaFallbackAgent) && source.orchestrationQuotaFallbackAgent !== "brightsy") {
|
|
532
|
+
out.orchestrationQuotaFallbackAgent = source.orchestrationQuotaFallbackAgent;
|
|
533
|
+
}
|
|
526
534
|
return out;
|
|
527
535
|
}
|
|
528
536
|
function normalizeSettings(raw) {
|
|
@@ -757,6 +765,12 @@ function updateAdvancedSettings(patch) {
|
|
|
757
765
|
if (typeof patch.autoCleanupOrphans === "boolean") {
|
|
758
766
|
advanced.autoCleanupOrphans = patch.autoCleanupOrphans;
|
|
759
767
|
}
|
|
768
|
+
if (patch.orchestrationQuotaOnLimit === "switch_agent" || patch.orchestrationQuotaOnLimit === "wait_reset") {
|
|
769
|
+
advanced.orchestrationQuotaOnLimit = patch.orchestrationQuotaOnLimit;
|
|
770
|
+
}
|
|
771
|
+
if (typeof patch.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(patch.orchestrationQuotaFallbackAgent) && patch.orchestrationQuotaFallbackAgent !== "brightsy") {
|
|
772
|
+
advanced.orchestrationQuotaFallbackAgent = patch.orchestrationQuotaFallbackAgent;
|
|
773
|
+
}
|
|
760
774
|
return saveAppSettings({ ...current, advanced });
|
|
761
775
|
}
|
|
762
776
|
function autoRenameBranchEnabled(settings = loadAppSettings()) {
|
|
@@ -777,6 +791,16 @@ function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
|
|
|
777
791
|
function autoCleanupOrphansEnabled(settings = loadAppSettings()) {
|
|
778
792
|
return Boolean(settings.advanced.autoCleanupOrphans);
|
|
779
793
|
}
|
|
794
|
+
function orchestrationQuotaOnLimit(settings = loadAppSettings()) {
|
|
795
|
+
return settings.advanced.orchestrationQuotaOnLimit ?? "switch_agent";
|
|
796
|
+
}
|
|
797
|
+
function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
|
|
798
|
+
const preferred = settings.advanced.orchestrationQuotaFallbackAgent;
|
|
799
|
+
if (preferred && DEFAULT_AGENTS.has(preferred) && preferred !== "brightsy") {
|
|
800
|
+
return preferred;
|
|
801
|
+
}
|
|
802
|
+
return "cursor";
|
|
803
|
+
}
|
|
780
804
|
function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
781
805
|
const n = settings.advanced.maxConcurrent;
|
|
782
806
|
if (typeof n === "number" && Number.isFinite(n)) {
|
|
@@ -893,7 +917,9 @@ function normalizeThread(raw) {
|
|
|
893
917
|
attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
|
|
894
918
|
prTitle: raw.prTitle ?? null,
|
|
895
919
|
userSetTitle: Boolean(raw.userSetTitle),
|
|
896
|
-
activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : []
|
|
920
|
+
activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : [],
|
|
921
|
+
quotaResumeAt: raw.quotaResumeAt ?? null,
|
|
922
|
+
quotaContinuedFromId: raw.quotaContinuedFromId ?? null
|
|
897
923
|
};
|
|
898
924
|
}
|
|
899
925
|
function createEmptyThread(partial) {
|
|
@@ -1052,6 +1078,36 @@ var init_cloud_connect_constants = __esm({
|
|
|
1052
1078
|
}
|
|
1053
1079
|
});
|
|
1054
1080
|
|
|
1081
|
+
// src/agents/orchestrator-capable.ts
|
|
1082
|
+
function isOrchestratorCapableAgent(agent) {
|
|
1083
|
+
return Boolean(
|
|
1084
|
+
agent && ORCHESTRATOR_AGENT_KINDS.includes(agent)
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function assertOrchestratorCapableAgent(agent, context = "orchestration") {
|
|
1088
|
+
if (!isOrchestratorCapableAgent(agent)) {
|
|
1089
|
+
throw new Error(
|
|
1090
|
+
`${agent} cannot run ${context} \u2014 it does not support Sideboard MCP. Use Claude, Cursor, Codex, or OpenCode.`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
return agent;
|
|
1094
|
+
}
|
|
1095
|
+
function coerceOrchestratorAgent(agent, fallback = "claude") {
|
|
1096
|
+
return isOrchestratorCapableAgent(agent) ? agent : fallback;
|
|
1097
|
+
}
|
|
1098
|
+
var ORCHESTRATOR_AGENT_KINDS;
|
|
1099
|
+
var init_orchestrator_capable = __esm({
|
|
1100
|
+
"src/agents/orchestrator-capable.ts"() {
|
|
1101
|
+
"use strict";
|
|
1102
|
+
ORCHESTRATOR_AGENT_KINDS = [
|
|
1103
|
+
"claude",
|
|
1104
|
+
"codex",
|
|
1105
|
+
"opencode",
|
|
1106
|
+
"cursor"
|
|
1107
|
+
];
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1055
1111
|
// src/git/team-meta.ts
|
|
1056
1112
|
var SOCCER_TEAM_META;
|
|
1057
1113
|
var init_team_meta = __esm({
|
|
@@ -3184,11 +3240,14 @@ var init_coordinator_prompt = __esm({
|
|
|
3184
3240
|
"Discover:",
|
|
3185
3241
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
3186
3242
|
"- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
|
|
3243
|
+
"- list_models \u2014 only when you need a specific model (rare); otherwise leave model unset = Auto",
|
|
3187
3244
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
3188
3245
|
"Workspaces:",
|
|
3189
3246
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
3190
3247
|
"Worktree threads (chats):",
|
|
3191
3248
|
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId",
|
|
3249
|
+
"- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
|
|
3250
|
+
"- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
|
|
3192
3251
|
"- send_to_thread \u2014 queue a prompt (start/continue a chat turn); pass force_stop: true to interrupt mid-turn / clear stale queued prompts before replacing with a new request",
|
|
3193
3252
|
"- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply",
|
|
3194
3253
|
"- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
|
|
@@ -3286,6 +3345,7 @@ function createGlobalChat(opts) {
|
|
|
3286
3345
|
const explicit = opts.title?.trim();
|
|
3287
3346
|
const title = explicit && explicit !== CLOUD_ORCHESTRATOR_GOAL ? explicit : allocateTeamName(takenTeamSlugsForOrchestration()).name;
|
|
3288
3347
|
const sourceRef = opts.sourceRef?.trim() || (isCloud ? CLOUD_ORCHESTRATOR_GOAL : title);
|
|
3348
|
+
const agent = assertOrchestratorCapableAgent(opts.agent);
|
|
3289
3349
|
const thread = createEmptyThread({
|
|
3290
3350
|
title,
|
|
3291
3351
|
// Stick nicknames the same way chat tabs do (avoid later sync overwrites).
|
|
@@ -3295,7 +3355,7 @@ function createGlobalChat(opts) {
|
|
|
3295
3355
|
branchName: "global",
|
|
3296
3356
|
worktreePath: globalAgentCwd(),
|
|
3297
3357
|
repoPath: GLOBAL_WORKSPACE_ID,
|
|
3298
|
-
agent
|
|
3358
|
+
agent,
|
|
3299
3359
|
autonomy: opts.autonomy ?? "default",
|
|
3300
3360
|
model: opts.model ?? null,
|
|
3301
3361
|
effort: opts.effort ?? "high",
|
|
@@ -3357,6 +3417,7 @@ var init_global_workspace = __esm({
|
|
|
3357
3417
|
"src/store/global-workspace.ts"() {
|
|
3358
3418
|
"use strict";
|
|
3359
3419
|
init_cloud_connect_constants();
|
|
3420
|
+
init_orchestrator_capable();
|
|
3360
3421
|
init_teams();
|
|
3361
3422
|
init_coordinator_prompt();
|
|
3362
3423
|
init_paths();
|
|
@@ -3941,11 +4002,15 @@ function humanizeAgentFailDetail(detail) {
|
|
|
3941
4002
|
}
|
|
3942
4003
|
function formatTurnExitError(exitCode, stderrSummary) {
|
|
3943
4004
|
const code = exitCode ?? 1;
|
|
3944
|
-
const
|
|
4005
|
+
const raw = stderrSummary.trim();
|
|
4006
|
+
if (/^exit\s*\d+$/i.test(raw)) {
|
|
4007
|
+
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
4008
|
+
}
|
|
4009
|
+
const detail = humanizeAgentFailDetail(raw);
|
|
3945
4010
|
if (!detail) {
|
|
3946
4011
|
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
3947
4012
|
}
|
|
3948
|
-
if (looksLikeAgentFailureMessage(
|
|
4013
|
+
if (looksLikeAgentFailureMessage(raw)) return detail;
|
|
3949
4014
|
return `exit ${code}: ${detail}`;
|
|
3950
4015
|
}
|
|
3951
4016
|
var NODE_VERSION_FOOTER;
|
|
@@ -4508,6 +4573,45 @@ async function buildInjectedMcpServers(opts) {
|
|
|
4508
4573
|
}
|
|
4509
4574
|
return servers;
|
|
4510
4575
|
}
|
|
4576
|
+
function toCursorMcpServers(servers) {
|
|
4577
|
+
const out = {};
|
|
4578
|
+
for (const s of servers) {
|
|
4579
|
+
out[s.name] = {
|
|
4580
|
+
command: s.command,
|
|
4581
|
+
...s.args ? { args: s.args } : {},
|
|
4582
|
+
...s.env ? { env: s.env } : {}
|
|
4583
|
+
};
|
|
4584
|
+
}
|
|
4585
|
+
return out;
|
|
4586
|
+
}
|
|
4587
|
+
function toCodexMcpConfigArgs(servers) {
|
|
4588
|
+
const args = [];
|
|
4589
|
+
for (const s of servers) {
|
|
4590
|
+
const prefix = `mcp_servers.${s.name}`;
|
|
4591
|
+
args.push("-c", `${prefix}.command=${JSON.stringify(s.command)}`);
|
|
4592
|
+
if (s.args?.length) {
|
|
4593
|
+
args.push("-c", `${prefix}.args=${JSON.stringify(s.args)}`);
|
|
4594
|
+
}
|
|
4595
|
+
if (s.env) {
|
|
4596
|
+
for (const [key, value] of Object.entries(s.env)) {
|
|
4597
|
+
args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
return args;
|
|
4602
|
+
}
|
|
4603
|
+
function toOpencodeMcpConfigContent(servers) {
|
|
4604
|
+
const mcp = {};
|
|
4605
|
+
for (const s of servers) {
|
|
4606
|
+
mcp[s.name] = {
|
|
4607
|
+
type: "local",
|
|
4608
|
+
command: [s.command, ...s.args ?? []],
|
|
4609
|
+
enabled: true,
|
|
4610
|
+
...s.env && Object.keys(s.env).length > 0 ? { environment: s.env } : {}
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4613
|
+
return JSON.stringify({ mcp });
|
|
4614
|
+
}
|
|
4511
4615
|
function writeMcpServersConfig(servers) {
|
|
4512
4616
|
if (servers.length === 0) return null;
|
|
4513
4617
|
const mcpServers = {};
|
|
@@ -5015,6 +5119,7 @@ var init_codex = __esm({
|
|
|
5015
5119
|
import_node_path12 = require("path");
|
|
5016
5120
|
init_run();
|
|
5017
5121
|
init_error_detail();
|
|
5122
|
+
init_injected_mcp();
|
|
5018
5123
|
init_turn_input();
|
|
5019
5124
|
init_types();
|
|
5020
5125
|
CODEX_PROMPT_ARG_MAX = 2e5;
|
|
@@ -5073,6 +5178,11 @@ var init_codex = __esm({
|
|
|
5073
5178
|
}
|
|
5074
5179
|
const mode = permissionMode(thread);
|
|
5075
5180
|
const model = thread.model?.trim();
|
|
5181
|
+
const injected = await buildInjectedMcpServers({
|
|
5182
|
+
includeSideboard: true,
|
|
5183
|
+
includeBrightsy: isBrightsyConnected()
|
|
5184
|
+
});
|
|
5185
|
+
const mcpOverrides = toCodexMcpConfigArgs(injected);
|
|
5076
5186
|
const args = [
|
|
5077
5187
|
"exec",
|
|
5078
5188
|
...sessionId ? ["resume", sessionId] : [],
|
|
@@ -5084,7 +5194,8 @@ var init_codex = __esm({
|
|
|
5084
5194
|
mode.codexSandbox,
|
|
5085
5195
|
"--ask-for-approval",
|
|
5086
5196
|
"never",
|
|
5087
|
-
...model ? ["--model", model] : []
|
|
5197
|
+
...model ? ["--model", model] : [],
|
|
5198
|
+
...mcpOverrides
|
|
5088
5199
|
];
|
|
5089
5200
|
return {
|
|
5090
5201
|
file: "codex",
|
|
@@ -5369,6 +5480,7 @@ var init_cursor = __esm({
|
|
|
5369
5480
|
init_run();
|
|
5370
5481
|
init_app_settings();
|
|
5371
5482
|
init_cursor_events();
|
|
5483
|
+
init_injected_mcp();
|
|
5372
5484
|
init_node_launch();
|
|
5373
5485
|
init_turn_input();
|
|
5374
5486
|
init_cursor_events();
|
|
@@ -5419,6 +5531,11 @@ var init_cursor = __esm({
|
|
|
5419
5531
|
const prompt = flattenTurnInput(input);
|
|
5420
5532
|
const agentId = await this.resolveSessionId(thread.worktreePath, thread.sessionId);
|
|
5421
5533
|
const apiKey = resolveCursorApiKey() || void 0;
|
|
5534
|
+
const injected = await buildInjectedMcpServers({
|
|
5535
|
+
includeSideboard: true,
|
|
5536
|
+
includeBrightsy: isBrightsyConnected()
|
|
5537
|
+
});
|
|
5538
|
+
const mcpServers = toCursorMcpServers(injected);
|
|
5422
5539
|
const req = {
|
|
5423
5540
|
prompt,
|
|
5424
5541
|
cwd: thread.worktreePath,
|
|
@@ -5427,7 +5544,8 @@ var init_cursor = __esm({
|
|
|
5427
5544
|
effort: thread.effort,
|
|
5428
5545
|
fast: thread.fast,
|
|
5429
5546
|
planMode: thread.planMode,
|
|
5430
|
-
apiKey
|
|
5547
|
+
apiKey,
|
|
5548
|
+
...Object.keys(mcpServers).length > 0 ? { mcpServers } : {}
|
|
5431
5549
|
};
|
|
5432
5550
|
const runner = cursorRunnerPath();
|
|
5433
5551
|
const isTs = runner.endsWith(".ts");
|
|
@@ -5521,6 +5639,7 @@ var init_opencode = __esm({
|
|
|
5521
5639
|
"use strict";
|
|
5522
5640
|
init_run();
|
|
5523
5641
|
init_error_detail();
|
|
5642
|
+
init_injected_mcp();
|
|
5524
5643
|
init_turn_input();
|
|
5525
5644
|
init_types();
|
|
5526
5645
|
FALLBACK_OPENCODE_MODELS = [
|
|
@@ -5581,6 +5700,11 @@ var init_opencode = __esm({
|
|
|
5581
5700
|
if (model) {
|
|
5582
5701
|
args.push("--model", model);
|
|
5583
5702
|
}
|
|
5703
|
+
const injected = await buildInjectedMcpServers({
|
|
5704
|
+
includeSideboard: true,
|
|
5705
|
+
includeBrightsy: isBrightsyConnected()
|
|
5706
|
+
});
|
|
5707
|
+
const mcpContent = injected.length > 0 ? toOpencodeMcpConfigContent(injected) : null;
|
|
5584
5708
|
return {
|
|
5585
5709
|
file: "opencode",
|
|
5586
5710
|
args,
|
|
@@ -5589,7 +5713,8 @@ var init_opencode = __esm({
|
|
|
5589
5713
|
// message is given (see resolveRunInput in opencode's run.ts).
|
|
5590
5714
|
stdin: prompt,
|
|
5591
5715
|
env: {
|
|
5592
|
-
OPENCODE_PERMISSION: mode.opencodePermission
|
|
5716
|
+
OPENCODE_PERMISSION: mode.opencodePermission,
|
|
5717
|
+
...mcpContent ? { OPENCODE_CONFIG_CONTENT: mcpContent } : {}
|
|
5593
5718
|
}
|
|
5594
5719
|
};
|
|
5595
5720
|
},
|
|
@@ -5709,6 +5834,201 @@ var init_opencode = __esm({
|
|
|
5709
5834
|
}
|
|
5710
5835
|
});
|
|
5711
5836
|
|
|
5837
|
+
// src/agents/list-models.ts
|
|
5838
|
+
async function listBrightsyModels() {
|
|
5839
|
+
try {
|
|
5840
|
+
const targets = await listBrightsyChatTargets();
|
|
5841
|
+
const accountId = targets.activeAccountId;
|
|
5842
|
+
const models = (targets.models ?? []).map((m) => ({
|
|
5843
|
+
id: encodeBrightsyTarget("model", m.id, accountId),
|
|
5844
|
+
displayName: m.name || m.id,
|
|
5845
|
+
description: m.description ?? void 0
|
|
5846
|
+
}));
|
|
5847
|
+
const agents = (targets.agents ?? []).map((a) => ({
|
|
5848
|
+
id: encodeBrightsyTarget("agent", a.id, accountId),
|
|
5849
|
+
displayName: a.name || a.id,
|
|
5850
|
+
description: a.description ?? "Brightsy agent target"
|
|
5851
|
+
}));
|
|
5852
|
+
return [...models, ...agents].slice(0, 80);
|
|
5853
|
+
} catch {
|
|
5854
|
+
return [];
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
async function listModelsForAgent(agent) {
|
|
5858
|
+
const kinds = agent ? [agent] : ["claude", "codex", "opencode", "cursor", "brightsy"];
|
|
5859
|
+
const out = [];
|
|
5860
|
+
for (const kind of kinds) {
|
|
5861
|
+
if (kind === "claude") {
|
|
5862
|
+
out.push({
|
|
5863
|
+
agent: kind,
|
|
5864
|
+
auto: true,
|
|
5865
|
+
models: CLAUDE_MODEL_CATALOG,
|
|
5866
|
+
note: "Default Auto \u2014 only pass a model id when you have a reason."
|
|
5867
|
+
});
|
|
5868
|
+
continue;
|
|
5869
|
+
}
|
|
5870
|
+
if (kind === "codex") {
|
|
5871
|
+
out.push({
|
|
5872
|
+
agent: kind,
|
|
5873
|
+
auto: true,
|
|
5874
|
+
models: await listCodexModels(),
|
|
5875
|
+
note: "Default Auto \u2014 only pass a model slug when you have a reason."
|
|
5876
|
+
});
|
|
5877
|
+
continue;
|
|
5878
|
+
}
|
|
5879
|
+
if (kind === "opencode") {
|
|
5880
|
+
out.push({
|
|
5881
|
+
agent: kind,
|
|
5882
|
+
auto: true,
|
|
5883
|
+
models: await listOpencodeModels(),
|
|
5884
|
+
note: "Default Auto \u2014 only pass a provider/model id when you have a reason."
|
|
5885
|
+
});
|
|
5886
|
+
continue;
|
|
5887
|
+
}
|
|
5888
|
+
if (kind === "cursor") {
|
|
5889
|
+
out.push({
|
|
5890
|
+
agent: kind,
|
|
5891
|
+
auto: true,
|
|
5892
|
+
models: await listCursorModels(),
|
|
5893
|
+
note: 'Default Auto \u2014 only pass a model id when you have a reason (or use "default").'
|
|
5894
|
+
});
|
|
5895
|
+
continue;
|
|
5896
|
+
}
|
|
5897
|
+
if (kind === "brightsy") {
|
|
5898
|
+
const models = await listBrightsyModels();
|
|
5899
|
+
out.push({
|
|
5900
|
+
agent: kind,
|
|
5901
|
+
auto: true,
|
|
5902
|
+
models,
|
|
5903
|
+
note: models.length ? "Default Auto / Default agent \u2014 only pass a model/agent id when you have a reason." : "Brightsy not logged in or no targets \u2014 leave model unset for Default."
|
|
5904
|
+
});
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
return out;
|
|
5908
|
+
}
|
|
5909
|
+
var CLAUDE_MODEL_CATALOG;
|
|
5910
|
+
var init_list_models = __esm({
|
|
5911
|
+
"src/agents/list-models.ts"() {
|
|
5912
|
+
"use strict";
|
|
5913
|
+
init_brightsy();
|
|
5914
|
+
init_brightsy_targets();
|
|
5915
|
+
init_codex();
|
|
5916
|
+
init_cursor();
|
|
5917
|
+
init_opencode();
|
|
5918
|
+
CLAUDE_MODEL_CATALOG = [
|
|
5919
|
+
{ id: "fable", displayName: "Fable" },
|
|
5920
|
+
{ id: "opus", displayName: "Opus" },
|
|
5921
|
+
{ id: "sonnet", displayName: "Sonnet" },
|
|
5922
|
+
{ id: "haiku", displayName: "Haiku" }
|
|
5923
|
+
];
|
|
5924
|
+
}
|
|
5925
|
+
});
|
|
5926
|
+
|
|
5927
|
+
// src/agents/session-quota.ts
|
|
5928
|
+
function isSessionQuotaLimit(text) {
|
|
5929
|
+
const lower = text.trim().toLowerCase();
|
|
5930
|
+
if (!lower) return false;
|
|
5931
|
+
if (/credit balance is too low|out of credits|insufficient.?quota|billing/.test(lower)) {
|
|
5932
|
+
return false;
|
|
5933
|
+
}
|
|
5934
|
+
if (/prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower)) {
|
|
5935
|
+
return false;
|
|
5936
|
+
}
|
|
5937
|
+
return /you've hit your/.test(lower) || /hit your (session|weekly|opus) limit/.test(lower) || /usage limit/.test(lower) || /rate.?limit|too many requests|\b429\b/.test(lower) && /reset/i.test(text);
|
|
5938
|
+
}
|
|
5939
|
+
function parseSessionQuotaResetAt(text, now = /* @__PURE__ */ new Date()) {
|
|
5940
|
+
const absolute = text.match(
|
|
5941
|
+
/resets\s+(?:at\s+)?(\d{1,2}):(\d{2})\s*(am|pm)(?:\s*\(([^)]+)\))?/i
|
|
5942
|
+
);
|
|
5943
|
+
if (absolute) {
|
|
5944
|
+
const hour12 = Number(absolute[1]);
|
|
5945
|
+
const minute = Number(absolute[2]);
|
|
5946
|
+
const ampm = absolute[3].toLowerCase();
|
|
5947
|
+
const timeZone = absolute[4]?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
5948
|
+
let hour = hour12 % 12;
|
|
5949
|
+
if (ampm === "pm") hour += 12;
|
|
5950
|
+
const at = zonedWallTimeToUtc(now, hour, minute, timeZone);
|
|
5951
|
+
if (!at) return null;
|
|
5952
|
+
if (at.getTime() <= now.getTime() + 3e4) {
|
|
5953
|
+
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1e3);
|
|
5954
|
+
return zonedWallTimeToUtc(tomorrow, hour, minute, timeZone);
|
|
5955
|
+
}
|
|
5956
|
+
return at;
|
|
5957
|
+
}
|
|
5958
|
+
const relative = text.match(
|
|
5959
|
+
/resets\s+in\s+(\d+)\s*(minutes?|hours?|days?)/i
|
|
5960
|
+
);
|
|
5961
|
+
if (relative) {
|
|
5962
|
+
const n = Number(relative[1]);
|
|
5963
|
+
const unit = relative[2].toLowerCase();
|
|
5964
|
+
const ms = unit.startsWith("day") ? n * 24 * 60 * 60 * 1e3 : unit.startsWith("hour") ? n * 60 * 60 * 1e3 : n * 60 * 1e3;
|
|
5965
|
+
return new Date(now.getTime() + ms);
|
|
5966
|
+
}
|
|
5967
|
+
return null;
|
|
5968
|
+
}
|
|
5969
|
+
function zonedWallTimeToUtc(day, hour, minute, timeZone) {
|
|
5970
|
+
try {
|
|
5971
|
+
const cal = new Intl.DateTimeFormat("en-US", {
|
|
5972
|
+
timeZone,
|
|
5973
|
+
year: "numeric",
|
|
5974
|
+
month: "2-digit",
|
|
5975
|
+
day: "2-digit"
|
|
5976
|
+
});
|
|
5977
|
+
const parts = Object.fromEntries(
|
|
5978
|
+
cal.formatToParts(day).filter((p) => p.type !== "literal").map((p) => [p.type, p.value])
|
|
5979
|
+
);
|
|
5980
|
+
const year = Number(parts.year);
|
|
5981
|
+
const month = Number(parts.month);
|
|
5982
|
+
const date = Number(parts.day);
|
|
5983
|
+
if (![year, month, date].every((n) => Number.isFinite(n))) return null;
|
|
5984
|
+
const utcGuess = Date.UTC(year, month - 1, date, hour, minute, 0);
|
|
5985
|
+
const dtf = new Intl.DateTimeFormat("en-US", {
|
|
5986
|
+
timeZone,
|
|
5987
|
+
year: "numeric",
|
|
5988
|
+
month: "2-digit",
|
|
5989
|
+
day: "2-digit",
|
|
5990
|
+
hour: "2-digit",
|
|
5991
|
+
minute: "2-digit",
|
|
5992
|
+
second: "2-digit",
|
|
5993
|
+
hourCycle: "h23"
|
|
5994
|
+
});
|
|
5995
|
+
const asParts = Object.fromEntries(
|
|
5996
|
+
dtf.formatToParts(new Date(utcGuess)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value])
|
|
5997
|
+
);
|
|
5998
|
+
const asUtc = Date.UTC(
|
|
5999
|
+
Number(asParts.year),
|
|
6000
|
+
Number(asParts.month) - 1,
|
|
6001
|
+
Number(asParts.day),
|
|
6002
|
+
Number(asParts.hour),
|
|
6003
|
+
Number(asParts.minute),
|
|
6004
|
+
Number(asParts.second || "0")
|
|
6005
|
+
);
|
|
6006
|
+
const offset = asUtc - utcGuess;
|
|
6007
|
+
return new Date(utcGuess - offset);
|
|
6008
|
+
} catch {
|
|
6009
|
+
return null;
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
function resolveQuotaFallbackAgent(current, preferred) {
|
|
6013
|
+
const ordered = [
|
|
6014
|
+
...preferred && preferred !== "brightsy" ? [preferred] : [],
|
|
6015
|
+
...FALLBACK_ORDER.filter((a) => a !== preferred)
|
|
6016
|
+
];
|
|
6017
|
+
return ordered.find((a) => a !== current) ?? (current === "cursor" ? "codex" : "cursor");
|
|
6018
|
+
}
|
|
6019
|
+
var FALLBACK_ORDER;
|
|
6020
|
+
var init_session_quota = __esm({
|
|
6021
|
+
"src/agents/session-quota.ts"() {
|
|
6022
|
+
"use strict";
|
|
6023
|
+
FALLBACK_ORDER = [
|
|
6024
|
+
"cursor",
|
|
6025
|
+
"codex",
|
|
6026
|
+
"opencode",
|
|
6027
|
+
"claude"
|
|
6028
|
+
];
|
|
6029
|
+
}
|
|
6030
|
+
});
|
|
6031
|
+
|
|
5712
6032
|
// src/agents/install.ts
|
|
5713
6033
|
function getAgentSetupInfo(agent) {
|
|
5714
6034
|
return SETUP[agent];
|
|
@@ -5889,11 +6209,15 @@ var init_install = __esm({
|
|
|
5889
6209
|
// src/agents/index.ts
|
|
5890
6210
|
var agents_exports = {};
|
|
5891
6211
|
__export(agents_exports, {
|
|
6212
|
+
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
6213
|
+
ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
|
|
5892
6214
|
PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
|
|
5893
6215
|
allAdapters: () => allAdapters,
|
|
6216
|
+
assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
|
|
5894
6217
|
brightsyAdapter: () => brightsyAdapter,
|
|
5895
6218
|
claudeAdapter: () => claudeAdapter,
|
|
5896
6219
|
codexAdapter: () => codexAdapter,
|
|
6220
|
+
coerceOrchestratorAgent: () => coerceOrchestratorAgent,
|
|
5897
6221
|
cursorAdapter: () => cursorAdapter,
|
|
5898
6222
|
cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
|
|
5899
6223
|
decodeBrightsyTarget: () => decodeBrightsyTarget,
|
|
@@ -5903,17 +6227,22 @@ __export(agents_exports, {
|
|
|
5903
6227
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
5904
6228
|
installAgent: () => installAgent,
|
|
5905
6229
|
isCursorAutoModel: () => isCursorAutoModel,
|
|
6230
|
+
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6231
|
+
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
5906
6232
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
5907
6233
|
listBrightsyChatTargets: () => listBrightsyChatTargets,
|
|
5908
6234
|
listCodexModels: () => listCodexModels,
|
|
5909
6235
|
listCursorModels: () => listCursorModels,
|
|
6236
|
+
listModelsForAgent: () => listModelsForAgent,
|
|
5910
6237
|
listOpencodeModels: () => listOpencodeModels,
|
|
5911
6238
|
loginAgent: () => loginAgent,
|
|
5912
6239
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
5913
6240
|
opencodeAdapter: () => opencodeAdapter,
|
|
5914
6241
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6242
|
+
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
5915
6243
|
permissionMode: () => permissionMode,
|
|
5916
|
-
resolveCursorModelId: () => resolveCursorModelId
|
|
6244
|
+
resolveCursorModelId: () => resolveCursorModelId,
|
|
6245
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent
|
|
5917
6246
|
});
|
|
5918
6247
|
function getAdapter(kind) {
|
|
5919
6248
|
return adapters[kind];
|
|
@@ -5939,6 +6268,9 @@ var init_agents = __esm({
|
|
|
5939
6268
|
init_cursor_events();
|
|
5940
6269
|
init_cursor();
|
|
5941
6270
|
init_opencode();
|
|
6271
|
+
init_list_models();
|
|
6272
|
+
init_session_quota();
|
|
6273
|
+
init_orchestrator_capable();
|
|
5942
6274
|
init_path();
|
|
5943
6275
|
init_install();
|
|
5944
6276
|
adapters = {
|
|
@@ -6034,6 +6366,7 @@ var index_exports = {};
|
|
|
6034
6366
|
__export(index_exports, {
|
|
6035
6367
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
6036
6368
|
BrightsySideboardApi: () => BrightsySideboardApi,
|
|
6369
|
+
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
6037
6370
|
CLOUD_COORDINATOR_BUSY_REPLY: () => CLOUD_COORDINATOR_BUSY_REPLY,
|
|
6038
6371
|
CLOUD_COORDINATOR_STOPPED_REPLY: () => CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
6039
6372
|
CLOUD_COORDINATOR_TIMEOUT_REPLY: () => CLOUD_COORDINATOR_TIMEOUT_REPLY,
|
|
@@ -6047,6 +6380,7 @@ __export(index_exports, {
|
|
|
6047
6380
|
GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
|
|
6048
6381
|
HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
|
|
6049
6382
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS: () => MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
6383
|
+
ORCHESTRATOR_AGENT_KINDS: () => ORCHESTRATOR_AGENT_KINDS,
|
|
6050
6384
|
Orchestrator: () => Orchestrator,
|
|
6051
6385
|
PASTE_ATTACH_MIN_CHARS: () => PASTE_ATTACH_MIN_CHARS,
|
|
6052
6386
|
PASTE_ATTACH_MIN_LINES: () => PASTE_ATTACH_MIN_LINES,
|
|
@@ -6071,6 +6405,7 @@ __export(index_exports, {
|
|
|
6071
6405
|
applyAppEnvironment: () => applyAppEnvironment,
|
|
6072
6406
|
applyCompaction: () => applyCompaction,
|
|
6073
6407
|
applyThreadIntoMain: () => applyThreadIntoMain,
|
|
6408
|
+
assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
|
|
6074
6409
|
attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
|
|
6075
6410
|
attachmentsFromBuffers: () => attachmentsFromBuffers,
|
|
6076
6411
|
attachmentsFromWorktreePaths: () => attachmentsFromWorktreePaths,
|
|
@@ -6103,6 +6438,7 @@ __export(index_exports, {
|
|
|
6103
6438
|
cleanupOrphanWorktrees: () => cleanupOrphanWorktrees,
|
|
6104
6439
|
cloneRepoIntoSideboard: () => cloneRepoIntoSideboard,
|
|
6105
6440
|
codexAdapter: () => codexAdapter,
|
|
6441
|
+
coerceOrchestratorAgent: () => coerceOrchestratorAgent,
|
|
6106
6442
|
collectTakenTeamSlugs: () => collectTakenTeamSlugs,
|
|
6107
6443
|
commitAll: () => commitAll,
|
|
6108
6444
|
conductorDbPath: () => conductorDbPath,
|
|
@@ -6207,9 +6543,11 @@ __export(index_exports, {
|
|
|
6207
6543
|
isGlobalThread: () => isGlobalThread,
|
|
6208
6544
|
isImageFilePath: () => isImageFilePath,
|
|
6209
6545
|
isLinearConnected: () => isLinearConnected,
|
|
6546
|
+
isOrchestratorCapableAgent: () => isOrchestratorCapableAgent,
|
|
6210
6547
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
6211
6548
|
isPidAlive: () => isPidAlive,
|
|
6212
6549
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
6550
|
+
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
6213
6551
|
isThinkingEffort: () => isThinkingEffort,
|
|
6214
6552
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
6215
6553
|
listBranchCommits: () => listBranchCommits,
|
|
@@ -6225,6 +6563,7 @@ __export(index_exports, {
|
|
|
6225
6563
|
listIssues: () => listIssues,
|
|
6226
6564
|
listLinearIssues: () => listLinearIssues,
|
|
6227
6565
|
listLinearIssuesDirect: () => listLinearIssuesDirect,
|
|
6566
|
+
listModelsForAgent: () => listModelsForAgent,
|
|
6228
6567
|
listOpencodeModels: () => listOpencodeModels,
|
|
6229
6568
|
listPrs: () => listPrs,
|
|
6230
6569
|
listRunScripts: () => listRunScripts,
|
|
@@ -6256,6 +6595,8 @@ __export(index_exports, {
|
|
|
6256
6595
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
6257
6596
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
6258
6597
|
opencodeAdapter: () => opencodeAdapter,
|
|
6598
|
+
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
6599
|
+
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
6259
6600
|
orchestrationTitleNeedsSoccerNickname: () => orchestrationTitleNeedsSoccerNickname,
|
|
6260
6601
|
orchestratorSessionPoisonedByBuiltins: () => orchestratorSessionPoisonedByBuiltins,
|
|
6261
6602
|
originGhRepoEnv: () => originGhRepoEnv,
|
|
@@ -6263,6 +6604,7 @@ __export(index_exports, {
|
|
|
6263
6604
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
6264
6605
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
6265
6606
|
parseMcpList: () => parseMcpList,
|
|
6607
|
+
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6266
6608
|
partsToAssistantText: () => partsToAssistantText,
|
|
6267
6609
|
pastedTextStats: () => pastedTextStats,
|
|
6268
6610
|
permissionMode: () => permissionMode,
|
|
@@ -6289,6 +6631,7 @@ __export(index_exports, {
|
|
|
6289
6631
|
resolveFilesToCopy: () => resolveFilesToCopy,
|
|
6290
6632
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
6291
6633
|
resolvePrSelector: () => resolvePrSelector,
|
|
6634
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6292
6635
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
6293
6636
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
6294
6637
|
resolveThreadEffort: () => resolveThreadEffort,
|
|
@@ -6553,6 +6896,7 @@ init_app_settings();
|
|
|
6553
6896
|
init_global_workspace();
|
|
6554
6897
|
init_brightsy();
|
|
6555
6898
|
init_agents();
|
|
6899
|
+
init_orchestrator_capable();
|
|
6556
6900
|
|
|
6557
6901
|
// src/agents/message-parts.ts
|
|
6558
6902
|
function asRecord(input) {
|
|
@@ -6797,6 +7141,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
6797
7141
|
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await Promise.resolve().then(() => (init_coordinator_prompt(), coordinator_prompt_exports));
|
|
6798
7142
|
ensureGlobalCoordinatorCwd2();
|
|
6799
7143
|
}
|
|
7144
|
+
if (isOrchestratorThread(thread)) {
|
|
7145
|
+
assertOrchestratorCapableAgent(thread.agent);
|
|
7146
|
+
}
|
|
6800
7147
|
const adapter = getAdapter(thread.agent);
|
|
6801
7148
|
const cmd = await adapter.buildTurn(thread, input);
|
|
6802
7149
|
if (cmd.cwd !== thread.worktreePath) {
|
|
@@ -9089,6 +9436,7 @@ var import_node_crypto4 = require("crypto");
|
|
|
9089
9436
|
init_teams();
|
|
9090
9437
|
init_worktree_labels();
|
|
9091
9438
|
init_global_workspace();
|
|
9439
|
+
init_orchestrator_capable();
|
|
9092
9440
|
init_thread_store();
|
|
9093
9441
|
init_worktree_labels();
|
|
9094
9442
|
function sameWorktreePath(a, b) {
|
|
@@ -9152,6 +9500,10 @@ function createChatTab(input) {
|
|
|
9152
9500
|
const binding = worktreeBindingFrom(from);
|
|
9153
9501
|
const explicitTitle = input.title?.trim();
|
|
9154
9502
|
const title = explicitTitle || allocateTeamName(takenTeamSlugsForChatTab(binding.worktreePath)).name;
|
|
9503
|
+
const nextAgent = input.agent ?? from.agent;
|
|
9504
|
+
if (isOrchestratorThread(from) || binding.sourceType === "orchestration") {
|
|
9505
|
+
assertOrchestratorCapableAgent(nextAgent);
|
|
9506
|
+
}
|
|
9155
9507
|
const thread = createEmptyThread({
|
|
9156
9508
|
title,
|
|
9157
9509
|
// Chat-tab nicknames (soccer team or explicit) must stick. Post-turn
|
|
@@ -9159,7 +9511,7 @@ function createChatTab(input) {
|
|
|
9159
9511
|
// shared worktree folder name (e.g. fork "Arsenal" → "Monaco").
|
|
9160
9512
|
userSetTitle: true,
|
|
9161
9513
|
...binding,
|
|
9162
|
-
agent:
|
|
9514
|
+
agent: nextAgent,
|
|
9163
9515
|
model: input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model,
|
|
9164
9516
|
effort: input.effort !== void 0 ? input.effort : from.effort,
|
|
9165
9517
|
fast: input.fast !== void 0 ? Boolean(input.fast) : from.fast,
|
|
@@ -9175,16 +9527,24 @@ function forkChatTab(input) {
|
|
|
9175
9527
|
const from = requireThread(input.threadId);
|
|
9176
9528
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
9177
9529
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
9178
|
-
|
|
9530
|
+
const tab = createChatTab({
|
|
9179
9531
|
fromThreadId: input.threadId,
|
|
9180
9532
|
agent: input.agent ?? from.agent,
|
|
9533
|
+
model: input.model,
|
|
9181
9534
|
title: input.title?.trim() || void 0,
|
|
9182
9535
|
attachments: [attachment]
|
|
9183
9536
|
});
|
|
9537
|
+
if (isOrchestratorThread(from) && tab.parentThreadId !== from.id) {
|
|
9538
|
+
const next = { ...tab, parentThreadId: from.id };
|
|
9539
|
+
writeThread(next);
|
|
9540
|
+
return next;
|
|
9541
|
+
}
|
|
9542
|
+
return tab;
|
|
9184
9543
|
}
|
|
9185
9544
|
|
|
9186
9545
|
// src/threads/fork-worktree.ts
|
|
9187
9546
|
init_thread_store();
|
|
9547
|
+
init_global_workspace();
|
|
9188
9548
|
function requireThread2(idOrRef) {
|
|
9189
9549
|
const thread = findThreadByRef(idOrRef) ?? null;
|
|
9190
9550
|
if (!thread) throw new Error(`Thread not found: ${idOrRef}`);
|
|
@@ -9192,16 +9552,28 @@ function requireThread2(idOrRef) {
|
|
|
9192
9552
|
}
|
|
9193
9553
|
async function forkThreadWorktree(input, onSetupLine) {
|
|
9194
9554
|
const from = requireThread2(input.threadId);
|
|
9555
|
+
if (isOrchestratorThread(from)) {
|
|
9556
|
+
throw new Error(
|
|
9557
|
+
"fork_worktree targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
|
|
9558
|
+
);
|
|
9559
|
+
}
|
|
9560
|
+
if (!from.branchName?.trim() || !from.repoPath?.trim()) {
|
|
9561
|
+
throw new Error(
|
|
9562
|
+
`Cannot fork worktree: thread ${from.id} has no branch/repo (need a real worktree chat).`
|
|
9563
|
+
);
|
|
9564
|
+
}
|
|
9195
9565
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
9196
9566
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
9567
|
+
const nextAgent = input.agent ?? from.agent;
|
|
9568
|
+
const nextModel = input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model;
|
|
9197
9569
|
const thread = await createThread(
|
|
9198
9570
|
{
|
|
9199
9571
|
sourceType: "branch",
|
|
9200
9572
|
sourceRef: from.branchName,
|
|
9201
9573
|
repoPath: from.repoPath,
|
|
9202
|
-
agent:
|
|
9574
|
+
agent: nextAgent,
|
|
9203
9575
|
autonomy: from.autonomy,
|
|
9204
|
-
model:
|
|
9576
|
+
model: nextModel,
|
|
9205
9577
|
effort: from.effort,
|
|
9206
9578
|
fast: from.fast,
|
|
9207
9579
|
planMode: from.planMode,
|
|
@@ -9742,6 +10114,7 @@ async function cloneRepoIntoSideboard(opts) {
|
|
|
9742
10114
|
|
|
9743
10115
|
// src/orchestrator/orchestrator.ts
|
|
9744
10116
|
init_thread_store();
|
|
10117
|
+
init_orchestrator_capable();
|
|
9745
10118
|
|
|
9746
10119
|
// src/review/request-review.ts
|
|
9747
10120
|
var import_node_crypto5 = require("crypto");
|
|
@@ -9795,6 +10168,115 @@ async function requestReview(threadRef, send) {
|
|
|
9795
10168
|
return { tab: started, from };
|
|
9796
10169
|
}
|
|
9797
10170
|
|
|
10171
|
+
// src/orchestrator/quota-failover.ts
|
|
10172
|
+
var import_node_crypto6 = require("crypto");
|
|
10173
|
+
init_session_quota();
|
|
10174
|
+
init_app_settings();
|
|
10175
|
+
init_global_workspace();
|
|
10176
|
+
init_thread_store();
|
|
10177
|
+
function planOrchestrationQuotaFailover(thread, limitText, opts) {
|
|
10178
|
+
if (!isOrchestratorThread(thread)) return null;
|
|
10179
|
+
if (!isSessionQuotaLimit(limitText)) return null;
|
|
10180
|
+
const onLimit = opts?.onLimit ?? orchestrationQuotaOnLimit();
|
|
10181
|
+
const resumeAt = parseSessionQuotaResetAt(limitText, opts?.now);
|
|
10182
|
+
if (thread.quotaContinuedFromId) {
|
|
10183
|
+
if (resumeAt) {
|
|
10184
|
+
return {
|
|
10185
|
+
action: "wait_reset",
|
|
10186
|
+
reason: "Already continued once; waiting for quota reset instead.",
|
|
10187
|
+
limitText,
|
|
10188
|
+
resumeAt
|
|
10189
|
+
};
|
|
10190
|
+
}
|
|
10191
|
+
return {
|
|
10192
|
+
action: "none",
|
|
10193
|
+
reason: "Already continued once; no parseable reset time.",
|
|
10194
|
+
limitText
|
|
10195
|
+
};
|
|
10196
|
+
}
|
|
10197
|
+
if (onLimit === "wait_reset") {
|
|
10198
|
+
if (!resumeAt) {
|
|
10199
|
+
return {
|
|
10200
|
+
action: "none",
|
|
10201
|
+
reason: "wait_reset configured but reset time could not be parsed.",
|
|
10202
|
+
limitText
|
|
10203
|
+
};
|
|
10204
|
+
}
|
|
10205
|
+
return {
|
|
10206
|
+
action: "wait_reset",
|
|
10207
|
+
reason: "Settings: wait for quota reset.",
|
|
10208
|
+
limitText,
|
|
10209
|
+
resumeAt
|
|
10210
|
+
};
|
|
10211
|
+
}
|
|
10212
|
+
const preferred = opts?.fallbackAgent ?? orchestrationQuotaFallbackAgent();
|
|
10213
|
+
const fallbackAgent = resolveQuotaFallbackAgent(thread.agent, preferred);
|
|
10214
|
+
return {
|
|
10215
|
+
action: "switch_agent",
|
|
10216
|
+
reason: `Continue on ${fallbackAgent} (Auto) after ${thread.agent} session limit.`,
|
|
10217
|
+
limitText,
|
|
10218
|
+
fallbackAgent
|
|
10219
|
+
};
|
|
10220
|
+
}
|
|
10221
|
+
function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
10222
|
+
const children = listThreads({ includeArchived: false }).filter((t) => t.parentThreadId === from.id && t.status !== "archived").slice(0, 40).map(
|
|
10223
|
+
(t) => `- ${t.title} \xB7 ${t.status} \xB7 ${t.agent} \xB7 sideboard://thread/${t.id}`
|
|
10224
|
+
);
|
|
10225
|
+
const recent = from.messages.slice(-8).map((m) => {
|
|
10226
|
+
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
10227
|
+
const text = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
10228
|
+
return text ? `- ${role}: ${text}` : null;
|
|
10229
|
+
}).filter(Boolean);
|
|
10230
|
+
const body = [
|
|
10231
|
+
`# Orchestration handoff`,
|
|
10232
|
+
"",
|
|
10233
|
+
`Previous chat: ${from.title} (\`${from.id}\`) on **${from.agent}** hit a session/usage limit.`,
|
|
10234
|
+
`Limit: ${limitText.trim()}`,
|
|
10235
|
+
`Continuing on **${fallbackAgent}** with Auto model.`,
|
|
10236
|
+
"",
|
|
10237
|
+
`## Goal`,
|
|
10238
|
+
from.sourceRef?.trim() || "(none)",
|
|
10239
|
+
"",
|
|
10240
|
+
`## Child threads`,
|
|
10241
|
+
children.length ? children.join("\n") : "(none listed \u2014 call list_threads)",
|
|
10242
|
+
"",
|
|
10243
|
+
`## Recent turns (truncated)`,
|
|
10244
|
+
recent.length ? recent.join("\n") : "(none)",
|
|
10245
|
+
"",
|
|
10246
|
+
`## Instructions`,
|
|
10247
|
+
`- Continue fleet orchestration from this handoff.`,
|
|
10248
|
+
`- Prefer Sideboard MCP (list_threads, get_thread, send_to_thread, \u2026) for live status.`,
|
|
10249
|
+
`- Leave model Auto unless there is a specific reason to pin one.`,
|
|
10250
|
+
`- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
|
|
10251
|
+
].join("\n");
|
|
10252
|
+
return {
|
|
10253
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
10254
|
+
name: "Orchestration quota handoff.md",
|
|
10255
|
+
kind: "transcript",
|
|
10256
|
+
content: body
|
|
10257
|
+
};
|
|
10258
|
+
}
|
|
10259
|
+
var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
|
|
10260
|
+
`${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
|
|
10261
|
+
"Call list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
|
|
10262
|
+
].join(" ");
|
|
10263
|
+
var QUOTA_RESUME_PROMPT = "Session/usage limit window should have reset. Continue the orchestration from where you left off. Use list_threads for fleet status.";
|
|
10264
|
+
function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
10265
|
+
const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
|
|
10266
|
+
const tab = createChatTab({
|
|
10267
|
+
fromThreadId: from.id,
|
|
10268
|
+
agent: fallbackAgent,
|
|
10269
|
+
model: null,
|
|
10270
|
+
attachments: [handoff]
|
|
10271
|
+
});
|
|
10272
|
+
return updateThread(tab.id, {
|
|
10273
|
+
parentThreadId: from.id,
|
|
10274
|
+
quotaContinuedFromId: from.id,
|
|
10275
|
+
sourceRef: from.sourceRef,
|
|
10276
|
+
sourceType: "orchestration"
|
|
10277
|
+
});
|
|
10278
|
+
}
|
|
10279
|
+
|
|
9798
10280
|
// src/orchestrator/orchestrator.ts
|
|
9799
10281
|
init_types();
|
|
9800
10282
|
init_settings();
|
|
@@ -9865,6 +10347,8 @@ var Orchestrator = class {
|
|
|
9865
10347
|
haltDrain = /* @__PURE__ */ new Set();
|
|
9866
10348
|
/** WIP snapshot SHA at the start of the latest agent turn (per thread). */
|
|
9867
10349
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
10350
|
+
/** Timers for orchestration session-quota auto-resume. */
|
|
10351
|
+
quotaResumeTimers = /* @__PURE__ */ new Map();
|
|
9868
10352
|
maxConcurrent;
|
|
9869
10353
|
runningCount = 0;
|
|
9870
10354
|
constructor(opts) {
|
|
@@ -9944,6 +10428,113 @@ var Orchestrator = class {
|
|
|
9944
10428
|
void this.drainQueue(thread.id);
|
|
9945
10429
|
}
|
|
9946
10430
|
}
|
|
10431
|
+
this.schedulePendingQuotaResumes();
|
|
10432
|
+
}
|
|
10433
|
+
clearQuotaResumeTimer(threadId) {
|
|
10434
|
+
const timer = this.quotaResumeTimers.get(threadId);
|
|
10435
|
+
if (timer) clearTimeout(timer);
|
|
10436
|
+
this.quotaResumeTimers.delete(threadId);
|
|
10437
|
+
}
|
|
10438
|
+
/** Schedule (or fire) auto-retry after a provider session/usage limit reset. */
|
|
10439
|
+
scheduleQuotaResume(threadId, resumeAt) {
|
|
10440
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10441
|
+
updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
|
|
10442
|
+
const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
|
|
10443
|
+
const capped = Math.min(delay, 2147483647);
|
|
10444
|
+
const timer = setTimeout(() => {
|
|
10445
|
+
this.quotaResumeTimers.delete(threadId);
|
|
10446
|
+
void this.resumeAfterQuotaWait(threadId);
|
|
10447
|
+
}, capped);
|
|
10448
|
+
this.quotaResumeTimers.set(threadId, timer);
|
|
10449
|
+
}
|
|
10450
|
+
schedulePendingQuotaResumes() {
|
|
10451
|
+
for (const thread of listThreads({ includeArchived: false })) {
|
|
10452
|
+
if (!thread.quotaResumeAt) continue;
|
|
10453
|
+
const at = new Date(thread.quotaResumeAt);
|
|
10454
|
+
if (Number.isNaN(at.getTime())) continue;
|
|
10455
|
+
if (at.getTime() <= Date.now()) {
|
|
10456
|
+
void this.resumeAfterQuotaWait(thread.id);
|
|
10457
|
+
} else if (!this.quotaResumeTimers.has(thread.id)) {
|
|
10458
|
+
this.scheduleQuotaResume(thread.id, at);
|
|
10459
|
+
}
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10462
|
+
async resumeAfterQuotaWait(threadId) {
|
|
10463
|
+
const thread = findThreadByRef(threadId);
|
|
10464
|
+
if (!thread || thread.status === "archived") return;
|
|
10465
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10466
|
+
try {
|
|
10467
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
10468
|
+
} catch {
|
|
10469
|
+
return;
|
|
10470
|
+
}
|
|
10471
|
+
if (thread.status === "running" || this.activeTurns.has(threadId) || this.startingTurns.has(threadId)) {
|
|
10472
|
+
return;
|
|
10473
|
+
}
|
|
10474
|
+
await this.send(threadId, QUOTA_RESUME_PROMPT);
|
|
10475
|
+
}
|
|
10476
|
+
/**
|
|
10477
|
+
* Host-side continue when an orchestration chat hits a provider session/usage
|
|
10478
|
+
* limit (not context size): switch agent (Auto) or wait until reset.
|
|
10479
|
+
*/
|
|
10480
|
+
async maybeHandleOrchestrationQuotaFailover(threadId, limitText) {
|
|
10481
|
+
const thread = findThreadByRef(threadId);
|
|
10482
|
+
if (!thread) return;
|
|
10483
|
+
const plan = planOrchestrationQuotaFailover(thread, limitText);
|
|
10484
|
+
if (!plan || plan.action === "none") return;
|
|
10485
|
+
if (plan.action === "wait_reset" && plan.resumeAt) {
|
|
10486
|
+
this.haltDrain.add(threadId);
|
|
10487
|
+
this.scheduleQuotaResume(threadId, plan.resumeAt);
|
|
10488
|
+
setStatus(threadId, "idle", null);
|
|
10489
|
+
appendMessage(threadId, {
|
|
10490
|
+
role: "agent",
|
|
10491
|
+
text: `Sideboard will auto-retry this orchestration around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
|
|
10492
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10493
|
+
});
|
|
10494
|
+
this.emit({
|
|
10495
|
+
type: "quota_failover",
|
|
10496
|
+
threadId,
|
|
10497
|
+
action: "wait_reset",
|
|
10498
|
+
message: plan.reason,
|
|
10499
|
+
resumeAt: plan.resumeAt.toISOString()
|
|
10500
|
+
});
|
|
10501
|
+
this.emit({ type: "status_changed", threadId, status: "idle" });
|
|
10502
|
+
return;
|
|
10503
|
+
}
|
|
10504
|
+
if (plan.action === "switch_agent" && plan.fallbackAgent) {
|
|
10505
|
+
this.haltDrain.add(threadId);
|
|
10506
|
+
const next = createQuotaFailoverChat(
|
|
10507
|
+
thread,
|
|
10508
|
+
plan.fallbackAgent,
|
|
10509
|
+
plan.limitText
|
|
10510
|
+
);
|
|
10511
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10512
|
+
try {
|
|
10513
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
10514
|
+
} catch {
|
|
10515
|
+
}
|
|
10516
|
+
appendMessage(threadId, {
|
|
10517
|
+
role: "agent",
|
|
10518
|
+
text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
|
|
10519
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10520
|
+
});
|
|
10521
|
+
this.emit({
|
|
10522
|
+
type: "quota_failover",
|
|
10523
|
+
threadId,
|
|
10524
|
+
action: "switch_agent",
|
|
10525
|
+
toThreadId: next.id,
|
|
10526
|
+
message: plan.reason
|
|
10527
|
+
});
|
|
10528
|
+
this.emit({
|
|
10529
|
+
type: "status_changed",
|
|
10530
|
+
threadId: next.id,
|
|
10531
|
+
status: next.status
|
|
10532
|
+
});
|
|
10533
|
+
await this.send(
|
|
10534
|
+
next.id,
|
|
10535
|
+
QUOTA_CONTINUE_PROMPT(thread.agent, plan.fallbackAgent)
|
|
10536
|
+
);
|
|
10537
|
+
}
|
|
9947
10538
|
}
|
|
9948
10539
|
getThreads(includeArchived = false) {
|
|
9949
10540
|
return listThreads({ includeArchived });
|
|
@@ -10302,11 +10893,16 @@ var Orchestrator = class {
|
|
|
10302
10893
|
}
|
|
10303
10894
|
}
|
|
10304
10895
|
}
|
|
10305
|
-
const
|
|
10306
|
-
|
|
10896
|
+
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
10897
|
+
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
10898
|
+
let chatText = assistantText;
|
|
10899
|
+
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
10900
|
+
chatText = humanizeAgentFailDetail(detail);
|
|
10901
|
+
}
|
|
10902
|
+
if (chatText || parts.length > 0) {
|
|
10307
10903
|
appendMessage(threadId, {
|
|
10308
10904
|
role: "agent",
|
|
10309
|
-
text:
|
|
10905
|
+
text: chatText,
|
|
10310
10906
|
parts: parts.length > 0 ? parts : void 0,
|
|
10311
10907
|
durationMs: Math.max(0, Date.now() - turnStartedAt),
|
|
10312
10908
|
usage,
|
|
@@ -10325,13 +10921,12 @@ var Orchestrator = class {
|
|
|
10325
10921
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
10326
10922
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
10327
10923
|
} else {
|
|
10328
|
-
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
10329
|
-
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
10330
10924
|
const failDetail = formatTurnExitError(exitCode, detail);
|
|
10925
|
+
const explainedInChat = exitCode !== 0 && Boolean(chatText) && (looksLikeAgentFailureMessage(chatText) || failDetail && chatText.includes(failDetail.replace(/^exit\s*\d+:\s*/i, "").trim()));
|
|
10331
10926
|
setStatus(
|
|
10332
10927
|
threadId,
|
|
10333
10928
|
exitCode === 0 ? "idle" : "error",
|
|
10334
|
-
exitCode === 0 ? null : failDetail
|
|
10929
|
+
exitCode === 0 || explainedInChat ? null : failDetail
|
|
10335
10930
|
);
|
|
10336
10931
|
this.emit({
|
|
10337
10932
|
type: "status_changed",
|
|
@@ -10339,6 +10934,10 @@ var Orchestrator = class {
|
|
|
10339
10934
|
status: exitCode === 0 ? "idle" : "error"
|
|
10340
10935
|
});
|
|
10341
10936
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
10937
|
+
if (exitCode !== 0) {
|
|
10938
|
+
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
10939
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
10940
|
+
}
|
|
10342
10941
|
}
|
|
10343
10942
|
} catch (err) {
|
|
10344
10943
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -10352,6 +10951,7 @@ var Orchestrator = class {
|
|
|
10352
10951
|
this.emit({ type: "error", threadId, message });
|
|
10353
10952
|
this.emit({ type: "status_changed", threadId, status: "error" });
|
|
10354
10953
|
this.emit({ type: "turn_finished", threadId, exitCode: 1 });
|
|
10954
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
|
|
10355
10955
|
}
|
|
10356
10956
|
} finally {
|
|
10357
10957
|
this.startingTurns.delete(threadId);
|
|
@@ -10836,6 +11436,9 @@ var Orchestrator = class {
|
|
|
10836
11436
|
`Cannot switch agent provider mid-chat (${thread.agent} \u2192 ${patch.agent}). Start a new chat tab instead.`
|
|
10837
11437
|
);
|
|
10838
11438
|
}
|
|
11439
|
+
if (isOrchestratorThread(thread)) {
|
|
11440
|
+
assertOrchestratorCapableAgent(patch.agent);
|
|
11441
|
+
}
|
|
10839
11442
|
next.agent = patch.agent;
|
|
10840
11443
|
if (patch.agent !== "claude" && patch.model === void 0) next.model = null;
|
|
10841
11444
|
next.sessionId = null;
|
|
@@ -11062,6 +11665,7 @@ var import_zod = require("zod");
|
|
|
11062
11665
|
var import_node_path25 = require("path");
|
|
11063
11666
|
init_worktree();
|
|
11064
11667
|
init_global_workspace();
|
|
11668
|
+
init_list_models();
|
|
11065
11669
|
|
|
11066
11670
|
// src/mcp/archive-guard.ts
|
|
11067
11671
|
init_global_workspace();
|
|
@@ -11478,6 +12082,120 @@ async function startMcpServer() {
|
|
|
11478
12082
|
}
|
|
11479
12083
|
}
|
|
11480
12084
|
);
|
|
12085
|
+
const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
12086
|
+
server.tool(
|
|
12087
|
+
"list_models",
|
|
12088
|
+
"List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
|
|
12089
|
+
{
|
|
12090
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
12091
|
+
},
|
|
12092
|
+
async ({ agent }) => {
|
|
12093
|
+
try {
|
|
12094
|
+
const catalogs = await listModelsForAgent(agent);
|
|
12095
|
+
return {
|
|
12096
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
12097
|
+
};
|
|
12098
|
+
} catch (err) {
|
|
12099
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12100
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
);
|
|
12104
|
+
server.tool(
|
|
12105
|
+
"fork_worktree",
|
|
12106
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_thread / wait_for_turn on the returned id.",
|
|
12107
|
+
{
|
|
12108
|
+
ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
|
|
12109
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
12110
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
12111
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
12112
|
+
title: import_zod.z.string().optional()
|
|
12113
|
+
},
|
|
12114
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
12115
|
+
try {
|
|
12116
|
+
const source = orch.getThread(ref);
|
|
12117
|
+
if (source) await orch.reconcile(source.repoPath);
|
|
12118
|
+
const thread = await orch.forkThreadWorktree({
|
|
12119
|
+
threadId: ref,
|
|
12120
|
+
throughIndex: through_index,
|
|
12121
|
+
agent,
|
|
12122
|
+
model,
|
|
12123
|
+
title
|
|
12124
|
+
});
|
|
12125
|
+
return {
|
|
12126
|
+
content: [
|
|
12127
|
+
{
|
|
12128
|
+
type: "text",
|
|
12129
|
+
text: JSON.stringify({
|
|
12130
|
+
id: thread.id,
|
|
12131
|
+
title: thread.title,
|
|
12132
|
+
status: thread.status,
|
|
12133
|
+
agent: thread.agent,
|
|
12134
|
+
model: thread.model,
|
|
12135
|
+
branchName: thread.branchName,
|
|
12136
|
+
worktreePath: thread.worktreePath,
|
|
12137
|
+
fromThreadId: source?.id ?? ref,
|
|
12138
|
+
link: `sideboard://thread/${thread.id}`
|
|
12139
|
+
})
|
|
12140
|
+
}
|
|
12141
|
+
]
|
|
12142
|
+
};
|
|
12143
|
+
} catch (err) {
|
|
12144
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12145
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
12146
|
+
}
|
|
12147
|
+
}
|
|
12148
|
+
);
|
|
12149
|
+
server.tool(
|
|
12150
|
+
"fork_chat",
|
|
12151
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Remote coordinators use this to continue an orchestration chat on another agent after session limits. Then send_to_thread / wait_for_turn on the returned id. Use fork_worktree only for worktree agents that need a new git worktree.",
|
|
12152
|
+
{
|
|
12153
|
+
ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
12154
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
12155
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
12156
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
12157
|
+
title: import_zod.z.string().optional()
|
|
12158
|
+
},
|
|
12159
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
12160
|
+
try {
|
|
12161
|
+
const source = orch.getThread(ref);
|
|
12162
|
+
if (!source) {
|
|
12163
|
+
return {
|
|
12164
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
12165
|
+
isError: true
|
|
12166
|
+
};
|
|
12167
|
+
}
|
|
12168
|
+
const tab = orch.forkChatTab({
|
|
12169
|
+
threadId: source.id,
|
|
12170
|
+
throughIndex: through_index,
|
|
12171
|
+
agent,
|
|
12172
|
+
model,
|
|
12173
|
+
title
|
|
12174
|
+
});
|
|
12175
|
+
return {
|
|
12176
|
+
content: [
|
|
12177
|
+
{
|
|
12178
|
+
type: "text",
|
|
12179
|
+
text: JSON.stringify({
|
|
12180
|
+
id: tab.id,
|
|
12181
|
+
title: tab.title,
|
|
12182
|
+
status: tab.status,
|
|
12183
|
+
agent: tab.agent,
|
|
12184
|
+
model: tab.model,
|
|
12185
|
+
sourceType: tab.sourceType,
|
|
12186
|
+
worktreePath: tab.worktreePath,
|
|
12187
|
+
fromThreadId: source.id,
|
|
12188
|
+
link: `sideboard://thread/${tab.id}`
|
|
12189
|
+
})
|
|
12190
|
+
}
|
|
12191
|
+
]
|
|
12192
|
+
};
|
|
12193
|
+
} catch (err) {
|
|
12194
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12195
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
12196
|
+
}
|
|
12197
|
+
}
|
|
12198
|
+
);
|
|
11481
12199
|
server.tool(
|
|
11482
12200
|
"run_dev_script",
|
|
11483
12201
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
@@ -12004,6 +12722,7 @@ init_injected_mcp();
|
|
|
12004
12722
|
0 && (module.exports = {
|
|
12005
12723
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
12006
12724
|
BrightsySideboardApi,
|
|
12725
|
+
CLAUDE_MODEL_CATALOG,
|
|
12007
12726
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
12008
12727
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
12009
12728
|
CLOUD_COORDINATOR_TIMEOUT_REPLY,
|
|
@@ -12017,6 +12736,7 @@ init_injected_mcp();
|
|
|
12017
12736
|
GLOBAL_WORKSPACE_ID,
|
|
12018
12737
|
HARNESS_ENV_KEYS,
|
|
12019
12738
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
12739
|
+
ORCHESTRATOR_AGENT_KINDS,
|
|
12020
12740
|
Orchestrator,
|
|
12021
12741
|
PASTE_ATTACH_MIN_CHARS,
|
|
12022
12742
|
PASTE_ATTACH_MIN_LINES,
|
|
@@ -12041,6 +12761,7 @@ init_injected_mcp();
|
|
|
12041
12761
|
applyAppEnvironment,
|
|
12042
12762
|
applyCompaction,
|
|
12043
12763
|
applyThreadIntoMain,
|
|
12764
|
+
assertOrchestratorCapableAgent,
|
|
12044
12765
|
attachmentFromAbsolutePath,
|
|
12045
12766
|
attachmentsFromBuffers,
|
|
12046
12767
|
attachmentsFromWorktreePaths,
|
|
@@ -12073,6 +12794,7 @@ init_injected_mcp();
|
|
|
12073
12794
|
cleanupOrphanWorktrees,
|
|
12074
12795
|
cloneRepoIntoSideboard,
|
|
12075
12796
|
codexAdapter,
|
|
12797
|
+
coerceOrchestratorAgent,
|
|
12076
12798
|
collectTakenTeamSlugs,
|
|
12077
12799
|
commitAll,
|
|
12078
12800
|
conductorDbPath,
|
|
@@ -12177,9 +12899,11 @@ init_injected_mcp();
|
|
|
12177
12899
|
isGlobalThread,
|
|
12178
12900
|
isImageFilePath,
|
|
12179
12901
|
isLinearConnected,
|
|
12902
|
+
isOrchestratorCapableAgent,
|
|
12180
12903
|
isOrchestratorThread,
|
|
12181
12904
|
isPidAlive,
|
|
12182
12905
|
isPlaceholderBranch,
|
|
12906
|
+
isSessionQuotaLimit,
|
|
12183
12907
|
isThinkingEffort,
|
|
12184
12908
|
listAgentSetupInfo,
|
|
12185
12909
|
listBranchCommits,
|
|
@@ -12195,6 +12919,7 @@ init_injected_mcp();
|
|
|
12195
12919
|
listIssues,
|
|
12196
12920
|
listLinearIssues,
|
|
12197
12921
|
listLinearIssuesDirect,
|
|
12922
|
+
listModelsForAgent,
|
|
12198
12923
|
listOpencodeModels,
|
|
12199
12924
|
listPrs,
|
|
12200
12925
|
listRunScripts,
|
|
@@ -12226,6 +12951,8 @@ init_injected_mcp();
|
|
|
12226
12951
|
normalizeWorktreePath,
|
|
12227
12952
|
openInSystemTerminal,
|
|
12228
12953
|
opencodeAdapter,
|
|
12954
|
+
orchestrationQuotaFallbackAgent,
|
|
12955
|
+
orchestrationQuotaOnLimit,
|
|
12229
12956
|
orchestrationTitleNeedsSoccerNickname,
|
|
12230
12957
|
orchestratorSessionPoisonedByBuiltins,
|
|
12231
12958
|
originGhRepoEnv,
|
|
@@ -12233,6 +12960,7 @@ init_injected_mcp();
|
|
|
12233
12960
|
parseForceStopMessage,
|
|
12234
12961
|
parseGithubSlugFromRemoteUrl,
|
|
12235
12962
|
parseMcpList,
|
|
12963
|
+
parseSessionQuotaResetAt,
|
|
12236
12964
|
partsToAssistantText,
|
|
12237
12965
|
pastedTextStats,
|
|
12238
12966
|
permissionMode,
|
|
@@ -12259,6 +12987,7 @@ init_injected_mcp();
|
|
|
12259
12987
|
resolveFilesToCopy,
|
|
12260
12988
|
resolveGithubRepoSlug,
|
|
12261
12989
|
resolvePrSelector,
|
|
12990
|
+
resolveQuotaFallbackAgent,
|
|
12262
12991
|
resolveRepoRoot,
|
|
12263
12992
|
resolveThreadDefaults,
|
|
12264
12993
|
resolveThreadEffort,
|