@sideboard-ai/core 0.1.44 → 0.1.45
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.js +1 -1
- package/dist/{agents-PP3URTSF.js → agents-JSHCAZUZ.js} +15 -5
- package/dist/{app-settings-ZKVZHJPQ.js → app-settings-LYGVDGZY.js} +5 -1
- package/dist/{chunk-JM2TVGNW.js → chunk-6NAPN2N5.js} +4 -1
- package/dist/{chunk-XX26NCB6.js → chunk-ANZ566Z5.js} +184 -3
- package/dist/{chunk-7PCTK4WO.js → chunk-FV6FN6V5.js} +1 -1
- package/dist/{chunk-V3S4NF5F.js → chunk-I6QGZOOS.js} +392 -29
- package/dist/{chunk-PU27NUO4.js → chunk-J5JTEJ5O.js} +7 -2
- package/dist/{chunk-ENSD62HW.js → chunk-O6W3P7V3.js} +3 -1
- package/dist/{chunk-TXFJEXFB.js → chunk-U3EQKJHA.js} +2 -2
- package/dist/{chunk-FSIK442J.js → chunk-WYY3J7GR.js} +21 -0
- package/dist/{chunk-I3PKMLFW.js → chunk-ZNSM2DDD.js} +3 -3
- package/dist/{coordinator-prompt-I5YNS27B.js → coordinator-prompt-2XFYG3C5.js} +3 -3
- package/dist/{global-workspace-FU6UIPDY.js → global-workspace-YFOQUGWD.js} +4 -4
- package/dist/index.cjs +619 -13
- package/dist/index.d.cts +76 -2
- package/dist/index.d.ts +76 -2
- package/dist/index.js +23 -9
- package/dist/mcp/run-stdio.cjs +615 -23
- package/dist/mcp/run-stdio.js +9 -9
- package/dist/{thread-store-EHROA3VZ.js → thread-store-OV2X6PYO.js} +1 -1
- package/dist/{workspaces-RSEDTBGJ.js → workspaces-TIKLNDW3.js} +5 -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)) {
|
|
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)) {
|
|
772
|
+
advanced.orchestrationQuotaFallbackAgent = patch.orchestrationQuotaFallbackAgent;
|
|
773
|
+
}
|
|
760
774
|
return saveAppSettings({ ...current, advanced });
|
|
761
775
|
}
|
|
762
776
|
function autoRenameBranchEnabled(settings = loadAppSettings()) {
|
|
@@ -777,6 +791,13 @@ 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
|
+
return preferred && DEFAULT_AGENTS.has(preferred) ? preferred : "cursor";
|
|
800
|
+
}
|
|
780
801
|
function maxConcurrentAgents(settings = loadAppSettings()) {
|
|
781
802
|
const n = settings.advanced.maxConcurrent;
|
|
782
803
|
if (typeof n === "number" && Number.isFinite(n)) {
|
|
@@ -893,7 +914,9 @@ function normalizeThread(raw) {
|
|
|
893
914
|
attachments: Array.isArray(raw.attachments) ? raw.attachments : [],
|
|
894
915
|
prTitle: raw.prTitle ?? null,
|
|
895
916
|
userSetTitle: Boolean(raw.userSetTitle),
|
|
896
|
-
activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : []
|
|
917
|
+
activeRuns: Array.isArray(raw.activeRuns) ? raw.activeRuns : [],
|
|
918
|
+
quotaResumeAt: raw.quotaResumeAt ?? null,
|
|
919
|
+
quotaContinuedFromId: raw.quotaContinuedFromId ?? null
|
|
897
920
|
};
|
|
898
921
|
}
|
|
899
922
|
function createEmptyThread(partial) {
|
|
@@ -3184,11 +3207,14 @@ var init_coordinator_prompt = __esm({
|
|
|
3184
3207
|
"Discover:",
|
|
3185
3208
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
3186
3209
|
"- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
|
|
3210
|
+
"- list_models \u2014 only when you need a specific model (rare); otherwise leave model unset = Auto",
|
|
3187
3211
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
3188
3212
|
"Workspaces:",
|
|
3189
3213
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
3190
3214
|
"Worktree threads (chats):",
|
|
3191
3215
|
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId",
|
|
3216
|
+
"- 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.",
|
|
3217
|
+
"- 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
3218
|
"- 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
3219
|
"- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply",
|
|
3194
3220
|
"- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
|
|
@@ -3941,11 +3967,15 @@ function humanizeAgentFailDetail(detail) {
|
|
|
3941
3967
|
}
|
|
3942
3968
|
function formatTurnExitError(exitCode, stderrSummary) {
|
|
3943
3969
|
const code = exitCode ?? 1;
|
|
3944
|
-
const
|
|
3970
|
+
const raw = stderrSummary.trim();
|
|
3971
|
+
if (/^exit\s*\d+$/i.test(raw)) {
|
|
3972
|
+
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
3973
|
+
}
|
|
3974
|
+
const detail = humanizeAgentFailDetail(raw);
|
|
3945
3975
|
if (!detail) {
|
|
3946
3976
|
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
3947
3977
|
}
|
|
3948
|
-
if (looksLikeAgentFailureMessage(
|
|
3978
|
+
if (looksLikeAgentFailureMessage(raw)) return detail;
|
|
3949
3979
|
return `exit ${code}: ${detail}`;
|
|
3950
3980
|
}
|
|
3951
3981
|
var NODE_VERSION_FOOTER;
|
|
@@ -5709,6 +5739,199 @@ var init_opencode = __esm({
|
|
|
5709
5739
|
}
|
|
5710
5740
|
});
|
|
5711
5741
|
|
|
5742
|
+
// src/agents/list-models.ts
|
|
5743
|
+
async function listBrightsyModels() {
|
|
5744
|
+
try {
|
|
5745
|
+
const targets = await listBrightsyChatTargets();
|
|
5746
|
+
const accountId = targets.activeAccountId;
|
|
5747
|
+
const models = (targets.models ?? []).map((m) => ({
|
|
5748
|
+
id: encodeBrightsyTarget("model", m.id, accountId),
|
|
5749
|
+
displayName: m.name || m.id,
|
|
5750
|
+
description: m.description ?? void 0
|
|
5751
|
+
}));
|
|
5752
|
+
const agents = (targets.agents ?? []).map((a) => ({
|
|
5753
|
+
id: encodeBrightsyTarget("agent", a.id, accountId),
|
|
5754
|
+
displayName: a.name || a.id,
|
|
5755
|
+
description: a.description ?? "Brightsy agent target"
|
|
5756
|
+
}));
|
|
5757
|
+
return [...models, ...agents].slice(0, 80);
|
|
5758
|
+
} catch {
|
|
5759
|
+
return [];
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
async function listModelsForAgent(agent) {
|
|
5763
|
+
const kinds = agent ? [agent] : ["claude", "codex", "opencode", "cursor", "brightsy"];
|
|
5764
|
+
const out = [];
|
|
5765
|
+
for (const kind of kinds) {
|
|
5766
|
+
if (kind === "claude") {
|
|
5767
|
+
out.push({
|
|
5768
|
+
agent: kind,
|
|
5769
|
+
auto: true,
|
|
5770
|
+
models: CLAUDE_MODEL_CATALOG,
|
|
5771
|
+
note: "Default Auto \u2014 only pass a model id when you have a reason."
|
|
5772
|
+
});
|
|
5773
|
+
continue;
|
|
5774
|
+
}
|
|
5775
|
+
if (kind === "codex") {
|
|
5776
|
+
out.push({
|
|
5777
|
+
agent: kind,
|
|
5778
|
+
auto: true,
|
|
5779
|
+
models: await listCodexModels(),
|
|
5780
|
+
note: "Default Auto \u2014 only pass a model slug when you have a reason."
|
|
5781
|
+
});
|
|
5782
|
+
continue;
|
|
5783
|
+
}
|
|
5784
|
+
if (kind === "opencode") {
|
|
5785
|
+
out.push({
|
|
5786
|
+
agent: kind,
|
|
5787
|
+
auto: true,
|
|
5788
|
+
models: await listOpencodeModels(),
|
|
5789
|
+
note: "Default Auto \u2014 only pass a provider/model id when you have a reason."
|
|
5790
|
+
});
|
|
5791
|
+
continue;
|
|
5792
|
+
}
|
|
5793
|
+
if (kind === "cursor") {
|
|
5794
|
+
out.push({
|
|
5795
|
+
agent: kind,
|
|
5796
|
+
auto: true,
|
|
5797
|
+
models: await listCursorModels(),
|
|
5798
|
+
note: 'Default Auto \u2014 only pass a model id when you have a reason (or use "default").'
|
|
5799
|
+
});
|
|
5800
|
+
continue;
|
|
5801
|
+
}
|
|
5802
|
+
if (kind === "brightsy") {
|
|
5803
|
+
const models = await listBrightsyModels();
|
|
5804
|
+
out.push({
|
|
5805
|
+
agent: kind,
|
|
5806
|
+
auto: true,
|
|
5807
|
+
models,
|
|
5808
|
+
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."
|
|
5809
|
+
});
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
return out;
|
|
5813
|
+
}
|
|
5814
|
+
var CLAUDE_MODEL_CATALOG;
|
|
5815
|
+
var init_list_models = __esm({
|
|
5816
|
+
"src/agents/list-models.ts"() {
|
|
5817
|
+
"use strict";
|
|
5818
|
+
init_brightsy();
|
|
5819
|
+
init_brightsy_targets();
|
|
5820
|
+
init_codex();
|
|
5821
|
+
init_cursor();
|
|
5822
|
+
init_opencode();
|
|
5823
|
+
CLAUDE_MODEL_CATALOG = [
|
|
5824
|
+
{ id: "fable", displayName: "Fable" },
|
|
5825
|
+
{ id: "opus", displayName: "Opus" },
|
|
5826
|
+
{ id: "sonnet", displayName: "Sonnet" },
|
|
5827
|
+
{ id: "haiku", displayName: "Haiku" }
|
|
5828
|
+
];
|
|
5829
|
+
}
|
|
5830
|
+
});
|
|
5831
|
+
|
|
5832
|
+
// src/agents/session-quota.ts
|
|
5833
|
+
function isSessionQuotaLimit(text) {
|
|
5834
|
+
const lower = text.trim().toLowerCase();
|
|
5835
|
+
if (!lower) return false;
|
|
5836
|
+
if (/credit balance is too low|out of credits|insufficient.?quota|billing/.test(lower)) {
|
|
5837
|
+
return false;
|
|
5838
|
+
}
|
|
5839
|
+
if (/prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower)) {
|
|
5840
|
+
return false;
|
|
5841
|
+
}
|
|
5842
|
+
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);
|
|
5843
|
+
}
|
|
5844
|
+
function parseSessionQuotaResetAt(text, now = /* @__PURE__ */ new Date()) {
|
|
5845
|
+
const absolute = text.match(
|
|
5846
|
+
/resets\s+(?:at\s+)?(\d{1,2}):(\d{2})\s*(am|pm)(?:\s*\(([^)]+)\))?/i
|
|
5847
|
+
);
|
|
5848
|
+
if (absolute) {
|
|
5849
|
+
const hour12 = Number(absolute[1]);
|
|
5850
|
+
const minute = Number(absolute[2]);
|
|
5851
|
+
const ampm = absolute[3].toLowerCase();
|
|
5852
|
+
const timeZone = absolute[4]?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
5853
|
+
let hour = hour12 % 12;
|
|
5854
|
+
if (ampm === "pm") hour += 12;
|
|
5855
|
+
const at = zonedWallTimeToUtc(now, hour, minute, timeZone);
|
|
5856
|
+
if (!at) return null;
|
|
5857
|
+
if (at.getTime() <= now.getTime() + 3e4) {
|
|
5858
|
+
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1e3);
|
|
5859
|
+
return zonedWallTimeToUtc(tomorrow, hour, minute, timeZone);
|
|
5860
|
+
}
|
|
5861
|
+
return at;
|
|
5862
|
+
}
|
|
5863
|
+
const relative = text.match(
|
|
5864
|
+
/resets\s+in\s+(\d+)\s*(minutes?|hours?|days?)/i
|
|
5865
|
+
);
|
|
5866
|
+
if (relative) {
|
|
5867
|
+
const n = Number(relative[1]);
|
|
5868
|
+
const unit = relative[2].toLowerCase();
|
|
5869
|
+
const ms = unit.startsWith("day") ? n * 24 * 60 * 60 * 1e3 : unit.startsWith("hour") ? n * 60 * 60 * 1e3 : n * 60 * 1e3;
|
|
5870
|
+
return new Date(now.getTime() + ms);
|
|
5871
|
+
}
|
|
5872
|
+
return null;
|
|
5873
|
+
}
|
|
5874
|
+
function zonedWallTimeToUtc(day, hour, minute, timeZone) {
|
|
5875
|
+
try {
|
|
5876
|
+
const cal = new Intl.DateTimeFormat("en-US", {
|
|
5877
|
+
timeZone,
|
|
5878
|
+
year: "numeric",
|
|
5879
|
+
month: "2-digit",
|
|
5880
|
+
day: "2-digit"
|
|
5881
|
+
});
|
|
5882
|
+
const parts = Object.fromEntries(
|
|
5883
|
+
cal.formatToParts(day).filter((p) => p.type !== "literal").map((p) => [p.type, p.value])
|
|
5884
|
+
);
|
|
5885
|
+
const year = Number(parts.year);
|
|
5886
|
+
const month = Number(parts.month);
|
|
5887
|
+
const date = Number(parts.day);
|
|
5888
|
+
if (![year, month, date].every((n) => Number.isFinite(n))) return null;
|
|
5889
|
+
const utcGuess = Date.UTC(year, month - 1, date, hour, minute, 0);
|
|
5890
|
+
const dtf = new Intl.DateTimeFormat("en-US", {
|
|
5891
|
+
timeZone,
|
|
5892
|
+
year: "numeric",
|
|
5893
|
+
month: "2-digit",
|
|
5894
|
+
day: "2-digit",
|
|
5895
|
+
hour: "2-digit",
|
|
5896
|
+
minute: "2-digit",
|
|
5897
|
+
second: "2-digit",
|
|
5898
|
+
hourCycle: "h23"
|
|
5899
|
+
});
|
|
5900
|
+
const asParts = Object.fromEntries(
|
|
5901
|
+
dtf.formatToParts(new Date(utcGuess)).filter((p) => p.type !== "literal").map((p) => [p.type, p.value])
|
|
5902
|
+
);
|
|
5903
|
+
const asUtc = Date.UTC(
|
|
5904
|
+
Number(asParts.year),
|
|
5905
|
+
Number(asParts.month) - 1,
|
|
5906
|
+
Number(asParts.day),
|
|
5907
|
+
Number(asParts.hour),
|
|
5908
|
+
Number(asParts.minute),
|
|
5909
|
+
Number(asParts.second || "0")
|
|
5910
|
+
);
|
|
5911
|
+
const offset = asUtc - utcGuess;
|
|
5912
|
+
return new Date(utcGuess - offset);
|
|
5913
|
+
} catch {
|
|
5914
|
+
return null;
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
function resolveQuotaFallbackAgent(current, preferred) {
|
|
5918
|
+
const ordered = preferred ? [preferred, ...FALLBACK_ORDER.filter((a) => a !== preferred)] : FALLBACK_ORDER;
|
|
5919
|
+
return ordered.find((a) => a !== current) ?? (current === "cursor" ? "codex" : "cursor");
|
|
5920
|
+
}
|
|
5921
|
+
var FALLBACK_ORDER;
|
|
5922
|
+
var init_session_quota = __esm({
|
|
5923
|
+
"src/agents/session-quota.ts"() {
|
|
5924
|
+
"use strict";
|
|
5925
|
+
FALLBACK_ORDER = [
|
|
5926
|
+
"cursor",
|
|
5927
|
+
"codex",
|
|
5928
|
+
"opencode",
|
|
5929
|
+
"brightsy",
|
|
5930
|
+
"claude"
|
|
5931
|
+
];
|
|
5932
|
+
}
|
|
5933
|
+
});
|
|
5934
|
+
|
|
5712
5935
|
// src/agents/install.ts
|
|
5713
5936
|
function getAgentSetupInfo(agent) {
|
|
5714
5937
|
return SETUP[agent];
|
|
@@ -5889,6 +6112,7 @@ var init_install = __esm({
|
|
|
5889
6112
|
// src/agents/index.ts
|
|
5890
6113
|
var agents_exports = {};
|
|
5891
6114
|
__export(agents_exports, {
|
|
6115
|
+
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
5892
6116
|
PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
|
|
5893
6117
|
allAdapters: () => allAdapters,
|
|
5894
6118
|
brightsyAdapter: () => brightsyAdapter,
|
|
@@ -5903,17 +6127,21 @@ __export(agents_exports, {
|
|
|
5903
6127
|
getAgentSetupInfo: () => getAgentSetupInfo,
|
|
5904
6128
|
installAgent: () => installAgent,
|
|
5905
6129
|
isCursorAutoModel: () => isCursorAutoModel,
|
|
6130
|
+
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
5906
6131
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
5907
6132
|
listBrightsyChatTargets: () => listBrightsyChatTargets,
|
|
5908
6133
|
listCodexModels: () => listCodexModels,
|
|
5909
6134
|
listCursorModels: () => listCursorModels,
|
|
6135
|
+
listModelsForAgent: () => listModelsForAgent,
|
|
5910
6136
|
listOpencodeModels: () => listOpencodeModels,
|
|
5911
6137
|
loginAgent: () => loginAgent,
|
|
5912
6138
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
5913
6139
|
opencodeAdapter: () => opencodeAdapter,
|
|
5914
6140
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
6141
|
+
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
5915
6142
|
permissionMode: () => permissionMode,
|
|
5916
|
-
resolveCursorModelId: () => resolveCursorModelId
|
|
6143
|
+
resolveCursorModelId: () => resolveCursorModelId,
|
|
6144
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent
|
|
5917
6145
|
});
|
|
5918
6146
|
function getAdapter(kind) {
|
|
5919
6147
|
return adapters[kind];
|
|
@@ -5939,6 +6167,8 @@ var init_agents = __esm({
|
|
|
5939
6167
|
init_cursor_events();
|
|
5940
6168
|
init_cursor();
|
|
5941
6169
|
init_opencode();
|
|
6170
|
+
init_list_models();
|
|
6171
|
+
init_session_quota();
|
|
5942
6172
|
init_path();
|
|
5943
6173
|
init_install();
|
|
5944
6174
|
adapters = {
|
|
@@ -6034,6 +6264,7 @@ var index_exports = {};
|
|
|
6034
6264
|
__export(index_exports, {
|
|
6035
6265
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
6036
6266
|
BrightsySideboardApi: () => BrightsySideboardApi,
|
|
6267
|
+
CLAUDE_MODEL_CATALOG: () => CLAUDE_MODEL_CATALOG,
|
|
6037
6268
|
CLOUD_COORDINATOR_BUSY_REPLY: () => CLOUD_COORDINATOR_BUSY_REPLY,
|
|
6038
6269
|
CLOUD_COORDINATOR_STOPPED_REPLY: () => CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
6039
6270
|
CLOUD_COORDINATOR_TIMEOUT_REPLY: () => CLOUD_COORDINATOR_TIMEOUT_REPLY,
|
|
@@ -6210,6 +6441,7 @@ __export(index_exports, {
|
|
|
6210
6441
|
isOrchestratorThread: () => isOrchestratorThread,
|
|
6211
6442
|
isPidAlive: () => isPidAlive,
|
|
6212
6443
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
6444
|
+
isSessionQuotaLimit: () => isSessionQuotaLimit,
|
|
6213
6445
|
isThinkingEffort: () => isThinkingEffort,
|
|
6214
6446
|
listAgentSetupInfo: () => listAgentSetupInfo,
|
|
6215
6447
|
listBranchCommits: () => listBranchCommits,
|
|
@@ -6225,6 +6457,7 @@ __export(index_exports, {
|
|
|
6225
6457
|
listIssues: () => listIssues,
|
|
6226
6458
|
listLinearIssues: () => listLinearIssues,
|
|
6227
6459
|
listLinearIssuesDirect: () => listLinearIssuesDirect,
|
|
6460
|
+
listModelsForAgent: () => listModelsForAgent,
|
|
6228
6461
|
listOpencodeModels: () => listOpencodeModels,
|
|
6229
6462
|
listPrs: () => listPrs,
|
|
6230
6463
|
listRunScripts: () => listRunScripts,
|
|
@@ -6256,6 +6489,8 @@ __export(index_exports, {
|
|
|
6256
6489
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
6257
6490
|
openInSystemTerminal: () => openInSystemTerminal,
|
|
6258
6491
|
opencodeAdapter: () => opencodeAdapter,
|
|
6492
|
+
orchestrationQuotaFallbackAgent: () => orchestrationQuotaFallbackAgent,
|
|
6493
|
+
orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
|
|
6259
6494
|
orchestrationTitleNeedsSoccerNickname: () => orchestrationTitleNeedsSoccerNickname,
|
|
6260
6495
|
orchestratorSessionPoisonedByBuiltins: () => orchestratorSessionPoisonedByBuiltins,
|
|
6261
6496
|
originGhRepoEnv: () => originGhRepoEnv,
|
|
@@ -6263,6 +6498,7 @@ __export(index_exports, {
|
|
|
6263
6498
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
6264
6499
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
6265
6500
|
parseMcpList: () => parseMcpList,
|
|
6501
|
+
parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
|
|
6266
6502
|
partsToAssistantText: () => partsToAssistantText,
|
|
6267
6503
|
pastedTextStats: () => pastedTextStats,
|
|
6268
6504
|
permissionMode: () => permissionMode,
|
|
@@ -6289,6 +6525,7 @@ __export(index_exports, {
|
|
|
6289
6525
|
resolveFilesToCopy: () => resolveFilesToCopy,
|
|
6290
6526
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
6291
6527
|
resolvePrSelector: () => resolvePrSelector,
|
|
6528
|
+
resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
|
|
6292
6529
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
6293
6530
|
resolveThreadDefaults: () => resolveThreadDefaults,
|
|
6294
6531
|
resolveThreadEffort: () => resolveThreadEffort,
|
|
@@ -9175,16 +9412,24 @@ function forkChatTab(input) {
|
|
|
9175
9412
|
const from = requireThread(input.threadId);
|
|
9176
9413
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
9177
9414
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
9178
|
-
|
|
9415
|
+
const tab = createChatTab({
|
|
9179
9416
|
fromThreadId: input.threadId,
|
|
9180
9417
|
agent: input.agent ?? from.agent,
|
|
9418
|
+
model: input.model,
|
|
9181
9419
|
title: input.title?.trim() || void 0,
|
|
9182
9420
|
attachments: [attachment]
|
|
9183
9421
|
});
|
|
9422
|
+
if (isOrchestratorThread(from) && tab.parentThreadId !== from.id) {
|
|
9423
|
+
const next = { ...tab, parentThreadId: from.id };
|
|
9424
|
+
writeThread(next);
|
|
9425
|
+
return next;
|
|
9426
|
+
}
|
|
9427
|
+
return tab;
|
|
9184
9428
|
}
|
|
9185
9429
|
|
|
9186
9430
|
// src/threads/fork-worktree.ts
|
|
9187
9431
|
init_thread_store();
|
|
9432
|
+
init_global_workspace();
|
|
9188
9433
|
function requireThread2(idOrRef) {
|
|
9189
9434
|
const thread = findThreadByRef(idOrRef) ?? null;
|
|
9190
9435
|
if (!thread) throw new Error(`Thread not found: ${idOrRef}`);
|
|
@@ -9192,16 +9437,28 @@ function requireThread2(idOrRef) {
|
|
|
9192
9437
|
}
|
|
9193
9438
|
async function forkThreadWorktree(input, onSetupLine) {
|
|
9194
9439
|
const from = requireThread2(input.threadId);
|
|
9440
|
+
if (isOrchestratorThread(from)) {
|
|
9441
|
+
throw new Error(
|
|
9442
|
+
"fork_worktree targets a worktree agent thread (not the orchestrator). Pass a child/worktree thread ref."
|
|
9443
|
+
);
|
|
9444
|
+
}
|
|
9445
|
+
if (!from.branchName?.trim() || !from.repoPath?.trim()) {
|
|
9446
|
+
throw new Error(
|
|
9447
|
+
`Cannot fork worktree: thread ${from.id} has no branch/repo (need a real worktree chat).`
|
|
9448
|
+
);
|
|
9449
|
+
}
|
|
9195
9450
|
const slice = forkMessageSlice(from, input.throughIndex);
|
|
9196
9451
|
const attachment = buildForkTranscriptAttachment(from.title || "Chat", slice);
|
|
9452
|
+
const nextAgent = input.agent ?? from.agent;
|
|
9453
|
+
const nextModel = input.model !== void 0 ? input.model : input.agent && input.agent !== from.agent ? null : from.model;
|
|
9197
9454
|
const thread = await createThread(
|
|
9198
9455
|
{
|
|
9199
9456
|
sourceType: "branch",
|
|
9200
9457
|
sourceRef: from.branchName,
|
|
9201
9458
|
repoPath: from.repoPath,
|
|
9202
|
-
agent:
|
|
9459
|
+
agent: nextAgent,
|
|
9203
9460
|
autonomy: from.autonomy,
|
|
9204
|
-
model:
|
|
9461
|
+
model: nextModel,
|
|
9205
9462
|
effort: from.effort,
|
|
9206
9463
|
fast: from.fast,
|
|
9207
9464
|
planMode: from.planMode,
|
|
@@ -9795,6 +10052,115 @@ async function requestReview(threadRef, send) {
|
|
|
9795
10052
|
return { tab: started, from };
|
|
9796
10053
|
}
|
|
9797
10054
|
|
|
10055
|
+
// src/orchestrator/quota-failover.ts
|
|
10056
|
+
var import_node_crypto6 = require("crypto");
|
|
10057
|
+
init_session_quota();
|
|
10058
|
+
init_app_settings();
|
|
10059
|
+
init_global_workspace();
|
|
10060
|
+
init_thread_store();
|
|
10061
|
+
function planOrchestrationQuotaFailover(thread, limitText, opts) {
|
|
10062
|
+
if (!isOrchestratorThread(thread)) return null;
|
|
10063
|
+
if (!isSessionQuotaLimit(limitText)) return null;
|
|
10064
|
+
const onLimit = opts?.onLimit ?? orchestrationQuotaOnLimit();
|
|
10065
|
+
const resumeAt = parseSessionQuotaResetAt(limitText, opts?.now);
|
|
10066
|
+
if (thread.quotaContinuedFromId) {
|
|
10067
|
+
if (resumeAt) {
|
|
10068
|
+
return {
|
|
10069
|
+
action: "wait_reset",
|
|
10070
|
+
reason: "Already continued once; waiting for quota reset instead.",
|
|
10071
|
+
limitText,
|
|
10072
|
+
resumeAt
|
|
10073
|
+
};
|
|
10074
|
+
}
|
|
10075
|
+
return {
|
|
10076
|
+
action: "none",
|
|
10077
|
+
reason: "Already continued once; no parseable reset time.",
|
|
10078
|
+
limitText
|
|
10079
|
+
};
|
|
10080
|
+
}
|
|
10081
|
+
if (onLimit === "wait_reset") {
|
|
10082
|
+
if (!resumeAt) {
|
|
10083
|
+
return {
|
|
10084
|
+
action: "none",
|
|
10085
|
+
reason: "wait_reset configured but reset time could not be parsed.",
|
|
10086
|
+
limitText
|
|
10087
|
+
};
|
|
10088
|
+
}
|
|
10089
|
+
return {
|
|
10090
|
+
action: "wait_reset",
|
|
10091
|
+
reason: "Settings: wait for quota reset.",
|
|
10092
|
+
limitText,
|
|
10093
|
+
resumeAt
|
|
10094
|
+
};
|
|
10095
|
+
}
|
|
10096
|
+
const preferred = opts?.fallbackAgent ?? orchestrationQuotaFallbackAgent();
|
|
10097
|
+
const fallbackAgent = resolveQuotaFallbackAgent(thread.agent, preferred);
|
|
10098
|
+
return {
|
|
10099
|
+
action: "switch_agent",
|
|
10100
|
+
reason: `Continue on ${fallbackAgent} (Auto) after ${thread.agent} session limit.`,
|
|
10101
|
+
limitText,
|
|
10102
|
+
fallbackAgent
|
|
10103
|
+
};
|
|
10104
|
+
}
|
|
10105
|
+
function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
10106
|
+
const children = listThreads({ includeArchived: false }).filter((t) => t.parentThreadId === from.id && t.status !== "archived").slice(0, 40).map(
|
|
10107
|
+
(t) => `- ${t.title} \xB7 ${t.status} \xB7 ${t.agent} \xB7 sideboard://thread/${t.id}`
|
|
10108
|
+
);
|
|
10109
|
+
const recent = from.messages.slice(-8).map((m) => {
|
|
10110
|
+
const role = m.role === "user" ? "User" : m.role === "agent" ? "Agent" : "Summary";
|
|
10111
|
+
const text = m.text.trim().replace(/\s+/g, " ").slice(0, 280);
|
|
10112
|
+
return text ? `- ${role}: ${text}` : null;
|
|
10113
|
+
}).filter(Boolean);
|
|
10114
|
+
const body = [
|
|
10115
|
+
`# Orchestration handoff`,
|
|
10116
|
+
"",
|
|
10117
|
+
`Previous chat: ${from.title} (\`${from.id}\`) on **${from.agent}** hit a session/usage limit.`,
|
|
10118
|
+
`Limit: ${limitText.trim()}`,
|
|
10119
|
+
`Continuing on **${fallbackAgent}** with Auto model.`,
|
|
10120
|
+
"",
|
|
10121
|
+
`## Goal`,
|
|
10122
|
+
from.sourceRef?.trim() || "(none)",
|
|
10123
|
+
"",
|
|
10124
|
+
`## Child threads`,
|
|
10125
|
+
children.length ? children.join("\n") : "(none listed \u2014 call list_threads)",
|
|
10126
|
+
"",
|
|
10127
|
+
`## Recent turns (truncated)`,
|
|
10128
|
+
recent.length ? recent.join("\n") : "(none)",
|
|
10129
|
+
"",
|
|
10130
|
+
`## Instructions`,
|
|
10131
|
+
`- Continue fleet orchestration from this handoff.`,
|
|
10132
|
+
`- Prefer Sideboard MCP (list_threads, get_thread, send_to_thread, \u2026) for live status.`,
|
|
10133
|
+
`- Leave model Auto unless there is a specific reason to pin one.`,
|
|
10134
|
+
`- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
|
|
10135
|
+
].join("\n");
|
|
10136
|
+
return {
|
|
10137
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
10138
|
+
name: "Orchestration quota handoff.md",
|
|
10139
|
+
kind: "transcript",
|
|
10140
|
+
content: body
|
|
10141
|
+
};
|
|
10142
|
+
}
|
|
10143
|
+
var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
|
|
10144
|
+
`${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
|
|
10145
|
+
"Call list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
|
|
10146
|
+
].join(" ");
|
|
10147
|
+
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.";
|
|
10148
|
+
function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
10149
|
+
const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
|
|
10150
|
+
const tab = createChatTab({
|
|
10151
|
+
fromThreadId: from.id,
|
|
10152
|
+
agent: fallbackAgent,
|
|
10153
|
+
model: null,
|
|
10154
|
+
attachments: [handoff]
|
|
10155
|
+
});
|
|
10156
|
+
return updateThread(tab.id, {
|
|
10157
|
+
parentThreadId: from.id,
|
|
10158
|
+
quotaContinuedFromId: from.id,
|
|
10159
|
+
sourceRef: from.sourceRef,
|
|
10160
|
+
sourceType: "orchestration"
|
|
10161
|
+
});
|
|
10162
|
+
}
|
|
10163
|
+
|
|
9798
10164
|
// src/orchestrator/orchestrator.ts
|
|
9799
10165
|
init_types();
|
|
9800
10166
|
init_settings();
|
|
@@ -9865,6 +10231,8 @@ var Orchestrator = class {
|
|
|
9865
10231
|
haltDrain = /* @__PURE__ */ new Set();
|
|
9866
10232
|
/** WIP snapshot SHA at the start of the latest agent turn (per thread). */
|
|
9867
10233
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
10234
|
+
/** Timers for orchestration session-quota auto-resume. */
|
|
10235
|
+
quotaResumeTimers = /* @__PURE__ */ new Map();
|
|
9868
10236
|
maxConcurrent;
|
|
9869
10237
|
runningCount = 0;
|
|
9870
10238
|
constructor(opts) {
|
|
@@ -9944,6 +10312,113 @@ var Orchestrator = class {
|
|
|
9944
10312
|
void this.drainQueue(thread.id);
|
|
9945
10313
|
}
|
|
9946
10314
|
}
|
|
10315
|
+
this.schedulePendingQuotaResumes();
|
|
10316
|
+
}
|
|
10317
|
+
clearQuotaResumeTimer(threadId) {
|
|
10318
|
+
const timer = this.quotaResumeTimers.get(threadId);
|
|
10319
|
+
if (timer) clearTimeout(timer);
|
|
10320
|
+
this.quotaResumeTimers.delete(threadId);
|
|
10321
|
+
}
|
|
10322
|
+
/** Schedule (or fire) auto-retry after a provider session/usage limit reset. */
|
|
10323
|
+
scheduleQuotaResume(threadId, resumeAt) {
|
|
10324
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10325
|
+
updateThread(threadId, { quotaResumeAt: resumeAt.toISOString() });
|
|
10326
|
+
const delay = Math.max(5e3, resumeAt.getTime() - Date.now());
|
|
10327
|
+
const capped = Math.min(delay, 2147483647);
|
|
10328
|
+
const timer = setTimeout(() => {
|
|
10329
|
+
this.quotaResumeTimers.delete(threadId);
|
|
10330
|
+
void this.resumeAfterQuotaWait(threadId);
|
|
10331
|
+
}, capped);
|
|
10332
|
+
this.quotaResumeTimers.set(threadId, timer);
|
|
10333
|
+
}
|
|
10334
|
+
schedulePendingQuotaResumes() {
|
|
10335
|
+
for (const thread of listThreads({ includeArchived: false })) {
|
|
10336
|
+
if (!thread.quotaResumeAt) continue;
|
|
10337
|
+
const at = new Date(thread.quotaResumeAt);
|
|
10338
|
+
if (Number.isNaN(at.getTime())) continue;
|
|
10339
|
+
if (at.getTime() <= Date.now()) {
|
|
10340
|
+
void this.resumeAfterQuotaWait(thread.id);
|
|
10341
|
+
} else if (!this.quotaResumeTimers.has(thread.id)) {
|
|
10342
|
+
this.scheduleQuotaResume(thread.id, at);
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
}
|
|
10346
|
+
async resumeAfterQuotaWait(threadId) {
|
|
10347
|
+
const thread = findThreadByRef(threadId);
|
|
10348
|
+
if (!thread || thread.status === "archived") return;
|
|
10349
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10350
|
+
try {
|
|
10351
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
10352
|
+
} catch {
|
|
10353
|
+
return;
|
|
10354
|
+
}
|
|
10355
|
+
if (thread.status === "running" || this.activeTurns.has(threadId) || this.startingTurns.has(threadId)) {
|
|
10356
|
+
return;
|
|
10357
|
+
}
|
|
10358
|
+
await this.send(threadId, QUOTA_RESUME_PROMPT);
|
|
10359
|
+
}
|
|
10360
|
+
/**
|
|
10361
|
+
* Host-side continue when an orchestration chat hits a provider session/usage
|
|
10362
|
+
* limit (not context size): switch agent (Auto) or wait until reset.
|
|
10363
|
+
*/
|
|
10364
|
+
async maybeHandleOrchestrationQuotaFailover(threadId, limitText) {
|
|
10365
|
+
const thread = findThreadByRef(threadId);
|
|
10366
|
+
if (!thread) return;
|
|
10367
|
+
const plan = planOrchestrationQuotaFailover(thread, limitText);
|
|
10368
|
+
if (!plan || plan.action === "none") return;
|
|
10369
|
+
if (plan.action === "wait_reset" && plan.resumeAt) {
|
|
10370
|
+
this.haltDrain.add(threadId);
|
|
10371
|
+
this.scheduleQuotaResume(threadId, plan.resumeAt);
|
|
10372
|
+
setStatus(threadId, "idle", null);
|
|
10373
|
+
appendMessage(threadId, {
|
|
10374
|
+
role: "agent",
|
|
10375
|
+
text: `Sideboard will auto-retry this orchestration around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
|
|
10376
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10377
|
+
});
|
|
10378
|
+
this.emit({
|
|
10379
|
+
type: "quota_failover",
|
|
10380
|
+
threadId,
|
|
10381
|
+
action: "wait_reset",
|
|
10382
|
+
message: plan.reason,
|
|
10383
|
+
resumeAt: plan.resumeAt.toISOString()
|
|
10384
|
+
});
|
|
10385
|
+
this.emit({ type: "status_changed", threadId, status: "idle" });
|
|
10386
|
+
return;
|
|
10387
|
+
}
|
|
10388
|
+
if (plan.action === "switch_agent" && plan.fallbackAgent) {
|
|
10389
|
+
this.haltDrain.add(threadId);
|
|
10390
|
+
const next = createQuotaFailoverChat(
|
|
10391
|
+
thread,
|
|
10392
|
+
plan.fallbackAgent,
|
|
10393
|
+
plan.limitText
|
|
10394
|
+
);
|
|
10395
|
+
this.clearQuotaResumeTimer(threadId);
|
|
10396
|
+
try {
|
|
10397
|
+
updateThread(threadId, { quotaResumeAt: null });
|
|
10398
|
+
} catch {
|
|
10399
|
+
}
|
|
10400
|
+
appendMessage(threadId, {
|
|
10401
|
+
role: "agent",
|
|
10402
|
+
text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
|
|
10403
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10404
|
+
});
|
|
10405
|
+
this.emit({
|
|
10406
|
+
type: "quota_failover",
|
|
10407
|
+
threadId,
|
|
10408
|
+
action: "switch_agent",
|
|
10409
|
+
toThreadId: next.id,
|
|
10410
|
+
message: plan.reason
|
|
10411
|
+
});
|
|
10412
|
+
this.emit({
|
|
10413
|
+
type: "status_changed",
|
|
10414
|
+
threadId: next.id,
|
|
10415
|
+
status: next.status
|
|
10416
|
+
});
|
|
10417
|
+
await this.send(
|
|
10418
|
+
next.id,
|
|
10419
|
+
QUOTA_CONTINUE_PROMPT(thread.agent, plan.fallbackAgent)
|
|
10420
|
+
);
|
|
10421
|
+
}
|
|
9947
10422
|
}
|
|
9948
10423
|
getThreads(includeArchived = false) {
|
|
9949
10424
|
return listThreads({ includeArchived });
|
|
@@ -10302,11 +10777,16 @@ var Orchestrator = class {
|
|
|
10302
10777
|
}
|
|
10303
10778
|
}
|
|
10304
10779
|
}
|
|
10305
|
-
const
|
|
10306
|
-
|
|
10780
|
+
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
10781
|
+
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
10782
|
+
let chatText = assistantText;
|
|
10783
|
+
if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
|
|
10784
|
+
chatText = humanizeAgentFailDetail(detail);
|
|
10785
|
+
}
|
|
10786
|
+
if (chatText || parts.length > 0) {
|
|
10307
10787
|
appendMessage(threadId, {
|
|
10308
10788
|
role: "agent",
|
|
10309
|
-
text:
|
|
10789
|
+
text: chatText,
|
|
10310
10790
|
parts: parts.length > 0 ? parts : void 0,
|
|
10311
10791
|
durationMs: Math.max(0, Date.now() - turnStartedAt),
|
|
10312
10792
|
usage,
|
|
@@ -10325,13 +10805,12 @@ var Orchestrator = class {
|
|
|
10325
10805
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
10326
10806
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
10327
10807
|
} else {
|
|
10328
|
-
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
10329
|
-
const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
10330
10808
|
const failDetail = formatTurnExitError(exitCode, detail);
|
|
10809
|
+
const explainedInChat = exitCode !== 0 && Boolean(chatText) && (looksLikeAgentFailureMessage(chatText) || failDetail && chatText.includes(failDetail.replace(/^exit\s*\d+:\s*/i, "").trim()));
|
|
10331
10810
|
setStatus(
|
|
10332
10811
|
threadId,
|
|
10333
10812
|
exitCode === 0 ? "idle" : "error",
|
|
10334
|
-
exitCode === 0 ? null : failDetail
|
|
10813
|
+
exitCode === 0 || explainedInChat ? null : failDetail
|
|
10335
10814
|
);
|
|
10336
10815
|
this.emit({
|
|
10337
10816
|
type: "status_changed",
|
|
@@ -10339,6 +10818,10 @@ var Orchestrator = class {
|
|
|
10339
10818
|
status: exitCode === 0 ? "idle" : "error"
|
|
10340
10819
|
});
|
|
10341
10820
|
this.emit({ type: "turn_finished", threadId, exitCode });
|
|
10821
|
+
if (exitCode !== 0) {
|
|
10822
|
+
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
10823
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, blob);
|
|
10824
|
+
}
|
|
10342
10825
|
}
|
|
10343
10826
|
} catch (err) {
|
|
10344
10827
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -10352,6 +10835,7 @@ var Orchestrator = class {
|
|
|
10352
10835
|
this.emit({ type: "error", threadId, message });
|
|
10353
10836
|
this.emit({ type: "status_changed", threadId, status: "error" });
|
|
10354
10837
|
this.emit({ type: "turn_finished", threadId, exitCode: 1 });
|
|
10838
|
+
void this.maybeHandleOrchestrationQuotaFailover(threadId, message);
|
|
10355
10839
|
}
|
|
10356
10840
|
} finally {
|
|
10357
10841
|
this.startingTurns.delete(threadId);
|
|
@@ -11062,6 +11546,7 @@ var import_zod = require("zod");
|
|
|
11062
11546
|
var import_node_path25 = require("path");
|
|
11063
11547
|
init_worktree();
|
|
11064
11548
|
init_global_workspace();
|
|
11549
|
+
init_list_models();
|
|
11065
11550
|
|
|
11066
11551
|
// src/mcp/archive-guard.ts
|
|
11067
11552
|
init_global_workspace();
|
|
@@ -11478,6 +11963,120 @@ async function startMcpServer() {
|
|
|
11478
11963
|
}
|
|
11479
11964
|
}
|
|
11480
11965
|
);
|
|
11966
|
+
const agentEnum = import_zod.z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
11967
|
+
server.tool(
|
|
11968
|
+
"list_models",
|
|
11969
|
+
"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.",
|
|
11970
|
+
{
|
|
11971
|
+
agent: agentEnum.optional().describe("Limit to one agent; omit for all")
|
|
11972
|
+
},
|
|
11973
|
+
async ({ agent }) => {
|
|
11974
|
+
try {
|
|
11975
|
+
const catalogs = await listModelsForAgent(agent);
|
|
11976
|
+
return {
|
|
11977
|
+
content: [{ type: "text", text: JSON.stringify(catalogs, null, 2) }]
|
|
11978
|
+
};
|
|
11979
|
+
} catch (err) {
|
|
11980
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11981
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
11982
|
+
}
|
|
11983
|
+
}
|
|
11984
|
+
);
|
|
11985
|
+
server.tool(
|
|
11986
|
+
"fork_worktree",
|
|
11987
|
+
"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.",
|
|
11988
|
+
{
|
|
11989
|
+
ref: import_zod.z.string().describe("Worktree thread id/ref to fork"),
|
|
11990
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
11991
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
11992
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
11993
|
+
title: import_zod.z.string().optional()
|
|
11994
|
+
},
|
|
11995
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
11996
|
+
try {
|
|
11997
|
+
const source = orch.getThread(ref);
|
|
11998
|
+
if (source) await orch.reconcile(source.repoPath);
|
|
11999
|
+
const thread = await orch.forkThreadWorktree({
|
|
12000
|
+
threadId: ref,
|
|
12001
|
+
throughIndex: through_index,
|
|
12002
|
+
agent,
|
|
12003
|
+
model,
|
|
12004
|
+
title
|
|
12005
|
+
});
|
|
12006
|
+
return {
|
|
12007
|
+
content: [
|
|
12008
|
+
{
|
|
12009
|
+
type: "text",
|
|
12010
|
+
text: JSON.stringify({
|
|
12011
|
+
id: thread.id,
|
|
12012
|
+
title: thread.title,
|
|
12013
|
+
status: thread.status,
|
|
12014
|
+
agent: thread.agent,
|
|
12015
|
+
model: thread.model,
|
|
12016
|
+
branchName: thread.branchName,
|
|
12017
|
+
worktreePath: thread.worktreePath,
|
|
12018
|
+
fromThreadId: source?.id ?? ref,
|
|
12019
|
+
link: `sideboard://thread/${thread.id}`
|
|
12020
|
+
})
|
|
12021
|
+
}
|
|
12022
|
+
]
|
|
12023
|
+
};
|
|
12024
|
+
} catch (err) {
|
|
12025
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12026
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
12027
|
+
}
|
|
12028
|
+
}
|
|
12029
|
+
);
|
|
12030
|
+
server.tool(
|
|
12031
|
+
"fork_chat",
|
|
12032
|
+
"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. 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.",
|
|
12033
|
+
{
|
|
12034
|
+
ref: import_zod.z.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
12035
|
+
through_index: import_zod.z.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
12036
|
+
agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
|
|
12037
|
+
model: import_zod.z.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
|
|
12038
|
+
title: import_zod.z.string().optional()
|
|
12039
|
+
},
|
|
12040
|
+
async ({ ref, through_index, agent, model, title }) => {
|
|
12041
|
+
try {
|
|
12042
|
+
const source = orch.getThread(ref);
|
|
12043
|
+
if (!source) {
|
|
12044
|
+
return {
|
|
12045
|
+
content: [{ type: "text", text: `Thread not found: ${ref}` }],
|
|
12046
|
+
isError: true
|
|
12047
|
+
};
|
|
12048
|
+
}
|
|
12049
|
+
const tab = orch.forkChatTab({
|
|
12050
|
+
threadId: source.id,
|
|
12051
|
+
throughIndex: through_index,
|
|
12052
|
+
agent,
|
|
12053
|
+
model,
|
|
12054
|
+
title
|
|
12055
|
+
});
|
|
12056
|
+
return {
|
|
12057
|
+
content: [
|
|
12058
|
+
{
|
|
12059
|
+
type: "text",
|
|
12060
|
+
text: JSON.stringify({
|
|
12061
|
+
id: tab.id,
|
|
12062
|
+
title: tab.title,
|
|
12063
|
+
status: tab.status,
|
|
12064
|
+
agent: tab.agent,
|
|
12065
|
+
model: tab.model,
|
|
12066
|
+
sourceType: tab.sourceType,
|
|
12067
|
+
worktreePath: tab.worktreePath,
|
|
12068
|
+
fromThreadId: source.id,
|
|
12069
|
+
link: `sideboard://thread/${tab.id}`
|
|
12070
|
+
})
|
|
12071
|
+
}
|
|
12072
|
+
]
|
|
12073
|
+
};
|
|
12074
|
+
} catch (err) {
|
|
12075
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12076
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
12077
|
+
}
|
|
12078
|
+
}
|
|
12079
|
+
);
|
|
11481
12080
|
server.tool(
|
|
11482
12081
|
"run_dev_script",
|
|
11483
12082
|
"Start a .sideboard/.conductor run script for a thread (default script if name omitted); returns port",
|
|
@@ -12004,6 +12603,7 @@ init_injected_mcp();
|
|
|
12004
12603
|
0 && (module.exports = {
|
|
12005
12604
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
12006
12605
|
BrightsySideboardApi,
|
|
12606
|
+
CLAUDE_MODEL_CATALOG,
|
|
12007
12607
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
12008
12608
|
CLOUD_COORDINATOR_STOPPED_REPLY,
|
|
12009
12609
|
CLOUD_COORDINATOR_TIMEOUT_REPLY,
|
|
@@ -12180,6 +12780,7 @@ init_injected_mcp();
|
|
|
12180
12780
|
isOrchestratorThread,
|
|
12181
12781
|
isPidAlive,
|
|
12182
12782
|
isPlaceholderBranch,
|
|
12783
|
+
isSessionQuotaLimit,
|
|
12183
12784
|
isThinkingEffort,
|
|
12184
12785
|
listAgentSetupInfo,
|
|
12185
12786
|
listBranchCommits,
|
|
@@ -12195,6 +12796,7 @@ init_injected_mcp();
|
|
|
12195
12796
|
listIssues,
|
|
12196
12797
|
listLinearIssues,
|
|
12197
12798
|
listLinearIssuesDirect,
|
|
12799
|
+
listModelsForAgent,
|
|
12198
12800
|
listOpencodeModels,
|
|
12199
12801
|
listPrs,
|
|
12200
12802
|
listRunScripts,
|
|
@@ -12226,6 +12828,8 @@ init_injected_mcp();
|
|
|
12226
12828
|
normalizeWorktreePath,
|
|
12227
12829
|
openInSystemTerminal,
|
|
12228
12830
|
opencodeAdapter,
|
|
12831
|
+
orchestrationQuotaFallbackAgent,
|
|
12832
|
+
orchestrationQuotaOnLimit,
|
|
12229
12833
|
orchestrationTitleNeedsSoccerNickname,
|
|
12230
12834
|
orchestratorSessionPoisonedByBuiltins,
|
|
12231
12835
|
originGhRepoEnv,
|
|
@@ -12233,6 +12837,7 @@ init_injected_mcp();
|
|
|
12233
12837
|
parseForceStopMessage,
|
|
12234
12838
|
parseGithubSlugFromRemoteUrl,
|
|
12235
12839
|
parseMcpList,
|
|
12840
|
+
parseSessionQuotaResetAt,
|
|
12236
12841
|
partsToAssistantText,
|
|
12237
12842
|
pastedTextStats,
|
|
12238
12843
|
permissionMode,
|
|
@@ -12259,6 +12864,7 @@ init_injected_mcp();
|
|
|
12259
12864
|
resolveFilesToCopy,
|
|
12260
12865
|
resolveGithubRepoSlug,
|
|
12261
12866
|
resolvePrSelector,
|
|
12867
|
+
resolveQuotaFallbackAgent,
|
|
12262
12868
|
resolveRepoRoot,
|
|
12263
12869
|
resolveThreadDefaults,
|
|
12264
12870
|
resolveThreadEffort,
|