@sideboard-ai/core 0.1.32 → 0.1.34
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 +53 -2
- package/dist/agents/cursor-runner.js +6 -3
- package/dist/{agents-HWIPJLC3.js → agents-2OX5XXYP.js} +2 -2
- package/dist/{chunk-OY47OEY2.js → chunk-AL2GL5HJ.js} +175 -28
- package/dist/chunk-BSZX63TV.js +212 -0
- package/dist/{chunk-4YLTMPEO.js → chunk-MULWZLDI.js} +83 -18
- package/dist/index.cjs +371 -44
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +3 -3
- package/dist/mcp/run-stdio.cjs +356 -39
- package/dist/mcp/run-stdio.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-3DKGI32Q.js +0 -92
package/dist/index.cjs
CHANGED
|
@@ -3607,6 +3607,120 @@ var init_brightsy_targets = __esm({
|
|
|
3607
3607
|
}
|
|
3608
3608
|
});
|
|
3609
3609
|
|
|
3610
|
+
// src/agents/error-detail.ts
|
|
3611
|
+
function formatUnknownDetail(err) {
|
|
3612
|
+
if (err == null) return "";
|
|
3613
|
+
if (typeof err === "string") return err.trim();
|
|
3614
|
+
if (err instanceof Error) {
|
|
3615
|
+
const base = err.message.trim() || err.name;
|
|
3616
|
+
const code = "code" in err && typeof err.code === "string" ? err.code.trim() : "";
|
|
3617
|
+
return code && !base.includes(code) ? `${base} (${code})` : base;
|
|
3618
|
+
}
|
|
3619
|
+
if (typeof err === "object") {
|
|
3620
|
+
const o = err;
|
|
3621
|
+
const nested = o.error != null && typeof o.error === "object" ? formatUnknownDetail(o.error) : "";
|
|
3622
|
+
const message = typeof o.message === "string" ? o.message.trim() : typeof o.error === "string" ? o.error.trim() : typeof o.result === "string" ? o.result.trim() : nested;
|
|
3623
|
+
const code = typeof o.code === "string" ? o.code.trim() : "";
|
|
3624
|
+
if (message) return code && !message.includes(code) ? `${message} (${code})` : message;
|
|
3625
|
+
try {
|
|
3626
|
+
const json = JSON.stringify(err);
|
|
3627
|
+
if (json && json !== "{}" && json !== "null") return json;
|
|
3628
|
+
} catch {
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
const fallback = String(err);
|
|
3632
|
+
return fallback === "[object Object]" ? "" : fallback;
|
|
3633
|
+
}
|
|
3634
|
+
function extractJsonErrorMessage(obj) {
|
|
3635
|
+
const nested = obj.error != null && typeof obj.error === "object" ? obj.error : null;
|
|
3636
|
+
const candidates = [
|
|
3637
|
+
typeof obj.message === "string" ? obj.message : null,
|
|
3638
|
+
typeof obj.error === "string" ? obj.error : null,
|
|
3639
|
+
nested && typeof nested.message === "string" ? nested.message : null,
|
|
3640
|
+
typeof obj.result === "string" ? obj.result : null,
|
|
3641
|
+
typeof obj.detail === "string" ? obj.detail : null
|
|
3642
|
+
];
|
|
3643
|
+
for (const c of candidates) {
|
|
3644
|
+
const t = c?.trim();
|
|
3645
|
+
if (t) return t;
|
|
3646
|
+
}
|
|
3647
|
+
if (Array.isArray(obj.errors)) {
|
|
3648
|
+
const parts = obj.errors.map((e) => formatUnknownDetail(e)).map((s) => s.trim()).filter(Boolean);
|
|
3649
|
+
if (parts.length) return parts.join("; ");
|
|
3650
|
+
}
|
|
3651
|
+
return null;
|
|
3652
|
+
}
|
|
3653
|
+
function pushTurnStderr(tail, line, maxLines = 12) {
|
|
3654
|
+
const trimmed = line.trim();
|
|
3655
|
+
if (!trimmed) return;
|
|
3656
|
+
if (NODE_VERSION_FOOTER.test(trimmed)) return;
|
|
3657
|
+
if (/^reconnecting\.\.\./i.test(trimmed)) return;
|
|
3658
|
+
tail.push(trimmed);
|
|
3659
|
+
while (tail.length > maxLines) tail.shift();
|
|
3660
|
+
}
|
|
3661
|
+
function summarizeTurnStderr(tail, maxChars = 500) {
|
|
3662
|
+
if (tail.length === 0) return "";
|
|
3663
|
+
const joined = tail.slice(-6).join("\n").trim();
|
|
3664
|
+
if (joined.length <= maxChars) return joined;
|
|
3665
|
+
return joined.slice(joined.length - maxChars);
|
|
3666
|
+
}
|
|
3667
|
+
function looksLikeAgentFailureMessage(text) {
|
|
3668
|
+
const lower = text.trim().toLowerCase();
|
|
3669
|
+
if (!lower) return false;
|
|
3670
|
+
return /you've hit your|hit your (session|weekly|opus) limit|usage limit/.test(lower) || /credit balance is too low|out of credits|insufficient.?quota|quota.?exceeded/.test(lower) || /invalid user api key|invalid api key|not logged in|not authenticated|unauthorized/.test(
|
|
3671
|
+
lower
|
|
3672
|
+
) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower);
|
|
3673
|
+
}
|
|
3674
|
+
function fallbackTurnFailDetail(assistantText) {
|
|
3675
|
+
const t = assistantText.trim();
|
|
3676
|
+
if (!t) return "";
|
|
3677
|
+
if (looksLikeAgentFailureMessage(t)) return t;
|
|
3678
|
+
if (t.length <= 400 && !/\n\n/.test(t)) return t;
|
|
3679
|
+
return "";
|
|
3680
|
+
}
|
|
3681
|
+
function humanizeAgentFailDetail(detail) {
|
|
3682
|
+
const raw = detail.trim();
|
|
3683
|
+
if (!raw) return raw;
|
|
3684
|
+
const lower = raw.toLowerCase();
|
|
3685
|
+
if (/credit balance is too low|out of credits|insufficient.?quota|quota.?exceeded|billing/.test(lower)) {
|
|
3686
|
+
return `${raw} \u2014 add credits or switch auth, then retry.`;
|
|
3687
|
+
}
|
|
3688
|
+
if (/hit your (session|weekly|opus) limit|usage limit|you've hit your/.test(lower)) {
|
|
3689
|
+
return raw.includes("reset") ? raw : `${raw} \u2014 wait for the limit window to reset, then retry.`;
|
|
3690
|
+
}
|
|
3691
|
+
if (/\b429\b|rate.?limit|too many requests/.test(lower)) {
|
|
3692
|
+
return `${raw} \u2014 wait a moment and retry.`;
|
|
3693
|
+
}
|
|
3694
|
+
if (/invalid user api key|invalid api key|not logged in|not authenticated|unauthorized|authentication|please run.*login|codex login|claude auth|cursor api/.test(
|
|
3695
|
+
lower
|
|
3696
|
+
)) {
|
|
3697
|
+
return `${raw} \u2014 check agent login / API key in Settings.`;
|
|
3698
|
+
}
|
|
3699
|
+
if (/model .{0,80}(not found|unavailable|unknown|invalid)/.test(lower)) {
|
|
3700
|
+
return `${raw} \u2014 pick another model in the agent options.`;
|
|
3701
|
+
}
|
|
3702
|
+
if (/context.*(too long|exceed)|prompt is too long|conversation too long/.test(lower)) {
|
|
3703
|
+
return `${raw} \u2014 start a new chat or compact context, then retry.`;
|
|
3704
|
+
}
|
|
3705
|
+
return raw;
|
|
3706
|
+
}
|
|
3707
|
+
function formatTurnExitError(exitCode, stderrSummary) {
|
|
3708
|
+
const code = exitCode ?? 1;
|
|
3709
|
+
const detail = humanizeAgentFailDetail(stderrSummary);
|
|
3710
|
+
if (!detail) {
|
|
3711
|
+
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
3712
|
+
}
|
|
3713
|
+
if (looksLikeAgentFailureMessage(stderrSummary)) return detail;
|
|
3714
|
+
return `exit ${code}: ${detail}`;
|
|
3715
|
+
}
|
|
3716
|
+
var NODE_VERSION_FOOTER;
|
|
3717
|
+
var init_error_detail = __esm({
|
|
3718
|
+
"src/agents/error-detail.ts"() {
|
|
3719
|
+
"use strict";
|
|
3720
|
+
NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
|
|
3721
|
+
}
|
|
3722
|
+
});
|
|
3723
|
+
|
|
3610
3724
|
// src/agents/turn-input.ts
|
|
3611
3725
|
function normalizeTurnInput(input) {
|
|
3612
3726
|
if (typeof input === "string") return { prompt: input };
|
|
@@ -3697,7 +3811,7 @@ function parseBrightsyCliLine(line) {
|
|
|
3697
3811
|
return { type: "thinking", data: obj.text };
|
|
3698
3812
|
}
|
|
3699
3813
|
if (obj.type === "error") {
|
|
3700
|
-
const msg =
|
|
3814
|
+
const msg = extractJsonErrorMessage(obj) || formatUnknownDetail(obj.error) || formatUnknownDetail(obj.message) || trimmed;
|
|
3701
3815
|
return [
|
|
3702
3816
|
{ type: "stderr", data: msg },
|
|
3703
3817
|
{ type: "stdout", data: `Error: ${msg}` }
|
|
@@ -3734,6 +3848,12 @@ function parseBrightsyCliLine(line) {
|
|
|
3734
3848
|
if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
|
|
3735
3849
|
return null;
|
|
3736
3850
|
}
|
|
3851
|
+
if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
|
|
3852
|
+
return [
|
|
3853
|
+
{ type: "stderr", data: trimmed },
|
|
3854
|
+
{ type: "stdout", data: `Error: ${trimmed}` }
|
|
3855
|
+
];
|
|
3856
|
+
}
|
|
3737
3857
|
return { type: "stdout", data: line };
|
|
3738
3858
|
}
|
|
3739
3859
|
}
|
|
@@ -3888,6 +4008,7 @@ var init_brightsy = __esm({
|
|
|
3888
4008
|
init_connected_teams();
|
|
3889
4009
|
init_config();
|
|
3890
4010
|
init_brightsy_targets();
|
|
4011
|
+
init_error_detail();
|
|
3891
4012
|
init_turn_input();
|
|
3892
4013
|
init_brightsy_targets();
|
|
3893
4014
|
brightsyAdapter = {
|
|
@@ -4226,6 +4347,30 @@ function usageFromClaude(usage) {
|
|
|
4226
4347
|
cacheWriteTokens: usage.cache_creation_input_tokens ? Number(usage.cache_creation_input_tokens) : void 0
|
|
4227
4348
|
};
|
|
4228
4349
|
}
|
|
4350
|
+
function claudeResultErrorDetail(obj) {
|
|
4351
|
+
const isError = Boolean(obj.is_error) || typeof obj.subtype === "string" && /^error/i.test(obj.subtype);
|
|
4352
|
+
const fromResult = typeof obj.result === "string" ? obj.result.trim() : "";
|
|
4353
|
+
if (fromResult && (isError || looksLikeAgentFailureMessage(fromResult))) {
|
|
4354
|
+
return fromResult;
|
|
4355
|
+
}
|
|
4356
|
+
if (!isError) return null;
|
|
4357
|
+
const errors = obj.errors;
|
|
4358
|
+
if (Array.isArray(errors)) {
|
|
4359
|
+
const parts = errors.map((e) => {
|
|
4360
|
+
if (typeof e === "string") return e.trim();
|
|
4361
|
+
if (e && typeof e === "object" && typeof e.message === "string") {
|
|
4362
|
+
return e.message.trim();
|
|
4363
|
+
}
|
|
4364
|
+
return "";
|
|
4365
|
+
}).filter(Boolean);
|
|
4366
|
+
if (parts.length) return parts.join("; ");
|
|
4367
|
+
}
|
|
4368
|
+
if (typeof obj.error === "string" && obj.error.trim()) return obj.error.trim();
|
|
4369
|
+
if (typeof obj.subtype === "string" && obj.subtype) {
|
|
4370
|
+
return obj.subtype.replace(/^error[_-]?/i, "").replace(/_/g, " ") || "Claude turn failed";
|
|
4371
|
+
}
|
|
4372
|
+
return "Claude turn failed";
|
|
4373
|
+
}
|
|
4229
4374
|
function eventsFromContentBlocks(blocks) {
|
|
4230
4375
|
if (!blocks?.length) return [];
|
|
4231
4376
|
const out = [];
|
|
@@ -4295,6 +4440,7 @@ var init_claude = __esm({
|
|
|
4295
4440
|
init_run();
|
|
4296
4441
|
init_app_settings();
|
|
4297
4442
|
init_claude_mcp();
|
|
4443
|
+
init_error_detail();
|
|
4298
4444
|
init_injected_mcp();
|
|
4299
4445
|
init_turn_input();
|
|
4300
4446
|
init_types();
|
|
@@ -4489,8 +4635,13 @@ var init_claude = __esm({
|
|
|
4489
4635
|
}
|
|
4490
4636
|
if (obj.type === "result") {
|
|
4491
4637
|
const events = [];
|
|
4492
|
-
const
|
|
4493
|
-
if (
|
|
4638
|
+
const errorDetail = claudeResultErrorDetail(obj);
|
|
4639
|
+
if (errorDetail) {
|
|
4640
|
+
events.push({ type: "stderr", data: errorDetail });
|
|
4641
|
+
} else {
|
|
4642
|
+
const text = obj.result;
|
|
4643
|
+
if (typeof text === "string" && text) events.push({ type: "stdout", data: text });
|
|
4644
|
+
}
|
|
4494
4645
|
const usage = usageFromClaude(obj.usage);
|
|
4495
4646
|
if (usage) events.push({ type: "usage", data: usage });
|
|
4496
4647
|
if (events.length === 0) return null;
|
|
@@ -4598,6 +4749,7 @@ var init_codex = __esm({
|
|
|
4598
4749
|
import_node_os7 = require("os");
|
|
4599
4750
|
import_node_path12 = require("path");
|
|
4600
4751
|
init_run();
|
|
4752
|
+
init_error_detail();
|
|
4601
4753
|
init_turn_input();
|
|
4602
4754
|
init_types();
|
|
4603
4755
|
CODEX_PROMPT_ARG_MAX = 2e5;
|
|
@@ -4682,23 +4834,49 @@ var init_codex = __esm({
|
|
|
4682
4834
|
if (!trimmed) return null;
|
|
4683
4835
|
try {
|
|
4684
4836
|
const obj = JSON.parse(trimmed);
|
|
4685
|
-
const
|
|
4686
|
-
if (
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4837
|
+
const type = typeof obj.type === "string" ? obj.type : "";
|
|
4838
|
+
if (type === "turn.failed" || type === "turn_failed") {
|
|
4839
|
+
const detail = extractJsonErrorMessage(obj) || extractJsonErrorMessage(obj.error ?? {}) || "Codex turn failed";
|
|
4840
|
+
return { type: "stderr", data: detail };
|
|
4841
|
+
}
|
|
4842
|
+
if (type === "error") {
|
|
4843
|
+
const detail = extractJsonErrorMessage(obj) || trimmed;
|
|
4844
|
+
if (/^reconnecting\.\.\./i.test(detail)) return null;
|
|
4845
|
+
return { type: "stderr", data: detail };
|
|
4690
4846
|
}
|
|
4691
4847
|
if (typeof obj.item === "object" && obj.item !== null) {
|
|
4692
4848
|
const item = obj.item;
|
|
4849
|
+
if (item.type === "error") {
|
|
4850
|
+
const detail = item.message?.trim() || extractJsonErrorMessage(obj) || "Codex item error";
|
|
4851
|
+
return { type: "stderr", data: detail };
|
|
4852
|
+
}
|
|
4693
4853
|
if (item.type === "agent_message" && item.text) {
|
|
4694
4854
|
return { type: "stdout", data: item.text };
|
|
4695
4855
|
}
|
|
4856
|
+
if (item.status === "failed") {
|
|
4857
|
+
const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${item.type ?? "item"} failed`;
|
|
4858
|
+
return { type: "stderr", data: detail };
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
const sid = typeof obj.session_id === "string" && obj.session_id || typeof obj.thread_id === "string" && obj.thread_id || typeof obj.session?.id === "string" && obj.session.id;
|
|
4862
|
+
if (sid && (type === "thread.started" || type === "session" || !type)) {
|
|
4863
|
+
return { type: "session_id", data: sid };
|
|
4864
|
+
}
|
|
4865
|
+
if (sid && type.endsWith(".started")) {
|
|
4866
|
+
return { type: "session_id", data: sid };
|
|
4696
4867
|
}
|
|
4697
|
-
if (
|
|
4868
|
+
if (type === "turn.completed" || type === "turn_completed") {
|
|
4869
|
+
const usage = usageFromCodex(obj.usage);
|
|
4870
|
+
return usage ? { type: "usage", data: usage } : null;
|
|
4871
|
+
}
|
|
4872
|
+
if (typeof obj.content === "string" && obj.content.trim()) {
|
|
4698
4873
|
return { type: "stdout", data: obj.content };
|
|
4699
4874
|
}
|
|
4700
|
-
return
|
|
4875
|
+
return null;
|
|
4701
4876
|
} catch {
|
|
4877
|
+
if (/error|failed|unauthorized|quota|limit/i.test(trimmed)) {
|
|
4878
|
+
return { type: "stderr", data: trimmed };
|
|
4879
|
+
}
|
|
4702
4880
|
return { type: "stdout", data: line };
|
|
4703
4881
|
}
|
|
4704
4882
|
},
|
|
@@ -4814,7 +4992,12 @@ function cursorSdkMessageToEvents(msg) {
|
|
|
4814
4992
|
if (usage) return [{ type: "usage", data: usage }];
|
|
4815
4993
|
}
|
|
4816
4994
|
if (msg.type === "status" && msg.status === "ERROR") {
|
|
4817
|
-
const
|
|
4995
|
+
const rawMessage = msg.message;
|
|
4996
|
+
const detail = (typeof rawMessage === "string" ? rawMessage.trim() : "") || extractJsonErrorMessage(msg) || formatUnknownDetail(msg.error) || msg.text || "Cursor run entered ERROR status";
|
|
4997
|
+
return [{ type: "stderr", data: detail }];
|
|
4998
|
+
}
|
|
4999
|
+
if (msg.type === "error") {
|
|
5000
|
+
const detail = extractJsonErrorMessage(msg) || formatUnknownDetail(msg.error) || msg.text || "Cursor run error";
|
|
4818
5001
|
return [{ type: "stderr", data: detail }];
|
|
4819
5002
|
}
|
|
4820
5003
|
return [];
|
|
@@ -4839,6 +5022,7 @@ function parseCursorRunnerLine(line) {
|
|
|
4839
5022
|
var init_cursor_events = __esm({
|
|
4840
5023
|
"src/agents/cursor-events.ts"() {
|
|
4841
5024
|
"use strict";
|
|
5025
|
+
init_error_detail();
|
|
4842
5026
|
}
|
|
4843
5027
|
});
|
|
4844
5028
|
|
|
@@ -5071,6 +5255,7 @@ var init_opencode = __esm({
|
|
|
5071
5255
|
"src/agents/opencode.ts"() {
|
|
5072
5256
|
"use strict";
|
|
5073
5257
|
init_run();
|
|
5258
|
+
init_error_detail();
|
|
5074
5259
|
init_turn_input();
|
|
5075
5260
|
init_types();
|
|
5076
5261
|
FALLBACK_OPENCODE_MODELS = [
|
|
@@ -5148,6 +5333,10 @@ var init_opencode = __esm({
|
|
|
5148
5333
|
if (!trimmed) return null;
|
|
5149
5334
|
try {
|
|
5150
5335
|
const obj = JSON.parse(trimmed);
|
|
5336
|
+
if (obj.type === "error") {
|
|
5337
|
+
const detail = extractJsonErrorMessage(obj) || formatUnknownDetail(obj.error) || formatUnknownDetail(obj.message) || trimmed;
|
|
5338
|
+
return { type: "stderr", data: detail };
|
|
5339
|
+
}
|
|
5151
5340
|
const sid = typeof obj.sessionID === "string" && obj.sessionID || typeof obj.sessionId === "string" && obj.sessionId || typeof obj.session_id === "string" && obj.session_id;
|
|
5152
5341
|
if (sid) return { type: "session_id", data: sid };
|
|
5153
5342
|
if (obj.type === "text") {
|
|
@@ -5171,12 +5360,6 @@ var init_opencode = __esm({
|
|
|
5171
5360
|
content: part?.output ?? part?.content ?? obj.output ?? obj.content
|
|
5172
5361
|
};
|
|
5173
5362
|
}
|
|
5174
|
-
if (obj.type === "error") {
|
|
5175
|
-
return {
|
|
5176
|
-
type: "stderr",
|
|
5177
|
-
data: String(obj.error ?? trimmed)
|
|
5178
|
-
};
|
|
5179
|
-
}
|
|
5180
5363
|
if (obj.type === "step_finish" || obj.type === "step-finish") {
|
|
5181
5364
|
const part = obj.part;
|
|
5182
5365
|
const usage = usageFromOpencode(
|
|
@@ -5184,8 +5367,11 @@ var init_opencode = __esm({
|
|
|
5184
5367
|
);
|
|
5185
5368
|
return usage ? { type: "usage", data: usage } : null;
|
|
5186
5369
|
}
|
|
5187
|
-
return
|
|
5370
|
+
return null;
|
|
5188
5371
|
} catch {
|
|
5372
|
+
if (/error|failed|unauthorized|quota|limit/i.test(trimmed)) {
|
|
5373
|
+
return { type: "stderr", data: trimmed };
|
|
5374
|
+
}
|
|
5189
5375
|
return { type: "stdout", data: line };
|
|
5190
5376
|
}
|
|
5191
5377
|
},
|
|
@@ -6667,6 +6853,49 @@ function pipeLines(stream, onLine) {
|
|
|
6667
6853
|
const rl = (0, import_node_readline2.createInterface)({ input: stream });
|
|
6668
6854
|
rl.on("line", onLine);
|
|
6669
6855
|
}
|
|
6856
|
+
function killScriptTree(child, ports = []) {
|
|
6857
|
+
const pid = child.pid;
|
|
6858
|
+
if (pid) {
|
|
6859
|
+
try {
|
|
6860
|
+
if (process.platform === "win32") {
|
|
6861
|
+
void (0, import_execa3.execa)("taskkill", ["/pid", String(pid), "/T", "/F"], { reject: false });
|
|
6862
|
+
} else {
|
|
6863
|
+
process.kill(-pid, "SIGTERM");
|
|
6864
|
+
setTimeout(() => {
|
|
6865
|
+
try {
|
|
6866
|
+
process.kill(-pid, "SIGKILL");
|
|
6867
|
+
} catch {
|
|
6868
|
+
}
|
|
6869
|
+
}, 2500).unref?.();
|
|
6870
|
+
}
|
|
6871
|
+
} catch {
|
|
6872
|
+
try {
|
|
6873
|
+
child.kill("SIGTERM");
|
|
6874
|
+
} catch {
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
for (const port of ports) {
|
|
6879
|
+
if (!Number.isFinite(port) || port <= 0) continue;
|
|
6880
|
+
if (process.platform === "win32") {
|
|
6881
|
+
void (0, import_execa3.execa)(
|
|
6882
|
+
"powershell",
|
|
6883
|
+
[
|
|
6884
|
+
"-NoProfile",
|
|
6885
|
+
"-Command",
|
|
6886
|
+
`Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
|
|
6887
|
+
],
|
|
6888
|
+
{ reject: false }
|
|
6889
|
+
);
|
|
6890
|
+
} else {
|
|
6891
|
+
void (0, import_execa3.execa)(
|
|
6892
|
+
"zsh",
|
|
6893
|
+
["-lc", `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`],
|
|
6894
|
+
{ reject: false }
|
|
6895
|
+
);
|
|
6896
|
+
}
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6670
6899
|
async function spawnWorkspaceScript(command, opts) {
|
|
6671
6900
|
const loginEnv = await captureLoginEnv();
|
|
6672
6901
|
const env = buildWorkspaceScriptEnv(
|
|
@@ -6683,18 +6912,17 @@ async function spawnWorkspaceScript(command, opts) {
|
|
|
6683
6912
|
const child = (0, import_execa3.execa)(shell, ["-lc", command], {
|
|
6684
6913
|
cwd: opts.worktreePath,
|
|
6685
6914
|
reject: false,
|
|
6686
|
-
env
|
|
6915
|
+
env,
|
|
6916
|
+
// Own process group so Stop can tear down the whole tree (not just the shell).
|
|
6917
|
+
// There is no settings.toml `stop=` / teardown hook for run scripts.
|
|
6918
|
+
...process.platform === "win32" ? {} : { detached: true }
|
|
6687
6919
|
});
|
|
6688
6920
|
pipeLines(child.stdout, opts.onLine);
|
|
6689
6921
|
pipeLines(child.stderr, opts.onLine);
|
|
6922
|
+
const ports = opts.ports ?? [];
|
|
6690
6923
|
return {
|
|
6691
6924
|
pid: child.pid,
|
|
6692
|
-
kill: () =>
|
|
6693
|
-
try {
|
|
6694
|
-
child.kill("SIGTERM");
|
|
6695
|
-
} catch {
|
|
6696
|
-
}
|
|
6697
|
-
},
|
|
6925
|
+
kill: () => killScriptTree(child, ports),
|
|
6698
6926
|
done: child.then((r) => r.exitCode ?? null),
|
|
6699
6927
|
child
|
|
6700
6928
|
};
|
|
@@ -8860,6 +9088,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
|
|
|
8860
9088
|
// src/orchestrator/orchestrator.ts
|
|
8861
9089
|
var import_node_events = require("events");
|
|
8862
9090
|
var import_node_fs24 = require("fs");
|
|
9091
|
+
init_error_detail();
|
|
8863
9092
|
init_agents();
|
|
8864
9093
|
init_worktree();
|
|
8865
9094
|
|
|
@@ -9155,6 +9384,11 @@ var Orchestrator = class {
|
|
|
9155
9384
|
* the killed turn's handle.done resolves.
|
|
9156
9385
|
*/
|
|
9157
9386
|
stoppedTurns = /* @__PURE__ */ new Set();
|
|
9387
|
+
/**
|
|
9388
|
+
* Pause drainQueue after the in-flight turn unwinds (Stop with a preserved
|
|
9389
|
+
* queue). Cleared when the user sends or promotes a queued message again.
|
|
9390
|
+
*/
|
|
9391
|
+
haltDrain = /* @__PURE__ */ new Set();
|
|
9158
9392
|
/** WIP snapshot SHA at the start of the latest agent turn (per thread). */
|
|
9159
9393
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
9160
9394
|
maxConcurrent;
|
|
@@ -9222,7 +9456,7 @@ var Orchestrator = class {
|
|
|
9222
9456
|
} catch {
|
|
9223
9457
|
}
|
|
9224
9458
|
for (const thread of listThreads()) {
|
|
9225
|
-
if (thread.queue.length > 0) {
|
|
9459
|
+
if (thread.queue.length > 0 && thread.status !== "stopped") {
|
|
9226
9460
|
void this.drainQueue(thread.id);
|
|
9227
9461
|
}
|
|
9228
9462
|
}
|
|
@@ -9283,6 +9517,7 @@ var Orchestrator = class {
|
|
|
9283
9517
|
return withThreadLock(thread.id, async () => {
|
|
9284
9518
|
const current = this.requireThread(thread.id);
|
|
9285
9519
|
const queue = [...current.queue, prompt];
|
|
9520
|
+
this.haltDrain.delete(thread.id);
|
|
9286
9521
|
updateThread(thread.id, { queue, status: "queued" });
|
|
9287
9522
|
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
9288
9523
|
this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
|
|
@@ -9297,11 +9532,75 @@ var Orchestrator = class {
|
|
|
9297
9532
|
}
|
|
9298
9533
|
return results;
|
|
9299
9534
|
}
|
|
9535
|
+
/** Edit the text of a not-yet-started queued message. */
|
|
9536
|
+
async editQueuedMessage(threadRef, index, text) {
|
|
9537
|
+
const thread = this.requireThread(threadRef);
|
|
9538
|
+
return withThreadLock(thread.id, async () => {
|
|
9539
|
+
const current = this.requireThread(thread.id);
|
|
9540
|
+
const trimmed = text.trim();
|
|
9541
|
+
if (!trimmed || index < 0 || index >= current.queue.length) {
|
|
9542
|
+
return current;
|
|
9543
|
+
}
|
|
9544
|
+
const queue = current.queue.map((p, i) => i === index ? trimmed : p);
|
|
9545
|
+
updateThread(thread.id, { queue });
|
|
9546
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
9547
|
+
return this.requireThread(thread.id);
|
|
9548
|
+
});
|
|
9549
|
+
}
|
|
9550
|
+
/** Remove a not-yet-started queued message. */
|
|
9551
|
+
async removeQueuedMessage(threadRef, index) {
|
|
9552
|
+
const thread = this.requireThread(threadRef);
|
|
9553
|
+
return withThreadLock(thread.id, async () => {
|
|
9554
|
+
const current = this.requireThread(thread.id);
|
|
9555
|
+
if (index < 0 || index >= current.queue.length) return current;
|
|
9556
|
+
const queue = current.queue.filter((_, i) => i !== index);
|
|
9557
|
+
const stillQueued = queue.length > 0;
|
|
9558
|
+
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
9559
|
+
updateThread(thread.id, {
|
|
9560
|
+
queue,
|
|
9561
|
+
status: !stillQueued && !inFlight && current.status === "queued" ? "idle" : current.status
|
|
9562
|
+
});
|
|
9563
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
9564
|
+
const next = this.requireThread(thread.id);
|
|
9565
|
+
this.emit({ type: "status_changed", threadId: thread.id, status: next.status });
|
|
9566
|
+
return next;
|
|
9567
|
+
});
|
|
9568
|
+
}
|
|
9569
|
+
/**
|
|
9570
|
+
* Promote a queued message to run next, interrupting the in-flight turn (if any).
|
|
9571
|
+
* The current turn is stopped without clearing the rest of the queue — drainQueue
|
|
9572
|
+
* picks the promoted message up as soon as the interrupted turn unwinds.
|
|
9573
|
+
*/
|
|
9574
|
+
async sendQueuedMessageNow(threadRef, index) {
|
|
9575
|
+
const thread = this.requireThread(threadRef);
|
|
9576
|
+
const promoted = await withThreadLock(thread.id, async () => {
|
|
9577
|
+
const current = this.requireThread(thread.id);
|
|
9578
|
+
if (index < 0 || index >= current.queue.length) return false;
|
|
9579
|
+
const item = current.queue[index];
|
|
9580
|
+
const rest = current.queue.filter((_, i) => i !== index);
|
|
9581
|
+
const queue = [item, ...rest];
|
|
9582
|
+
this.haltDrain.delete(thread.id);
|
|
9583
|
+
updateThread(thread.id, { queue });
|
|
9584
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
9585
|
+
return true;
|
|
9586
|
+
});
|
|
9587
|
+
if (!promoted) return this.requireThread(thread.id);
|
|
9588
|
+
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
9589
|
+
if (inFlight) {
|
|
9590
|
+
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
9591
|
+
} else {
|
|
9592
|
+
void this.drainQueue(thread.id);
|
|
9593
|
+
}
|
|
9594
|
+
return this.requireThread(thread.id);
|
|
9595
|
+
}
|
|
9300
9596
|
async drainQueue(threadId) {
|
|
9301
9597
|
if (this.draining.has(threadId)) return;
|
|
9302
9598
|
this.draining.add(threadId);
|
|
9303
9599
|
try {
|
|
9304
9600
|
while (true) {
|
|
9601
|
+
if (this.haltDrain.has(threadId)) {
|
|
9602
|
+
break;
|
|
9603
|
+
}
|
|
9305
9604
|
const thread = readThread(threadId);
|
|
9306
9605
|
if (!thread || thread.queue.length === 0) {
|
|
9307
9606
|
if (thread && thread.status === "queued") {
|
|
@@ -9314,7 +9613,7 @@ var Orchestrator = class {
|
|
|
9314
9613
|
await new Promise((r) => setTimeout(r, 250));
|
|
9315
9614
|
continue;
|
|
9316
9615
|
}
|
|
9317
|
-
if (this.activeTurns.has(threadId)) {
|
|
9616
|
+
if (this.activeTurns.has(threadId) || this.startingTurns.has(threadId)) {
|
|
9318
9617
|
await new Promise((r) => setTimeout(r, 100));
|
|
9319
9618
|
continue;
|
|
9320
9619
|
}
|
|
@@ -9449,7 +9748,7 @@ var Orchestrator = class {
|
|
|
9449
9748
|
...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
|
|
9450
9749
|
].filter(Boolean).join("\n\n---\n\n");
|
|
9451
9750
|
try {
|
|
9452
|
-
|
|
9751
|
+
const stderrTail = [];
|
|
9453
9752
|
const handle = await spawnAgentTurn(
|
|
9454
9753
|
fresh,
|
|
9455
9754
|
{ cachedPrefix, prompt: agentPrompt },
|
|
@@ -9458,8 +9757,8 @@ var Orchestrator = class {
|
|
|
9458
9757
|
if (event.type === "session_id") {
|
|
9459
9758
|
updateThread(threadId, { sessionId: event.data });
|
|
9460
9759
|
}
|
|
9461
|
-
if (event.type === "stderr" && typeof event.data === "string"
|
|
9462
|
-
|
|
9760
|
+
if (event.type === "stderr" && typeof event.data === "string") {
|
|
9761
|
+
pushTurnStderr(stderrTail, event.data);
|
|
9463
9762
|
}
|
|
9464
9763
|
}
|
|
9465
9764
|
);
|
|
@@ -9484,10 +9783,12 @@ var Orchestrator = class {
|
|
|
9484
9783
|
if (result.sessionId) {
|
|
9485
9784
|
updateThread(threadId, { sessionId: result.sessionId });
|
|
9486
9785
|
}
|
|
9487
|
-
|
|
9786
|
+
const assistantText = result.assistantText.trim();
|
|
9787
|
+
const failureOnlyMessage = result.exitCode !== 0 && looksLikeAgentFailureMessage(assistantText) && !result.parts.some((p) => p.type === "tool" || p.type === "thinking");
|
|
9788
|
+
if (!failureOnlyMessage && (assistantText || result.parts.length > 0)) {
|
|
9488
9789
|
appendMessage(threadId, {
|
|
9489
9790
|
role: "agent",
|
|
9490
|
-
text:
|
|
9791
|
+
text: assistantText,
|
|
9491
9792
|
parts: result.parts.length > 0 ? result.parts : void 0,
|
|
9492
9793
|
durationMs: Math.max(0, Date.now() - turnStartedAt),
|
|
9493
9794
|
usage: result.usage ?? void 0,
|
|
@@ -9506,7 +9807,9 @@ var Orchestrator = class {
|
|
|
9506
9807
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
9507
9808
|
this.emit({ type: "turn_finished", threadId, exitCode: result.exitCode });
|
|
9508
9809
|
} else {
|
|
9509
|
-
const
|
|
9810
|
+
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
9811
|
+
const detail = lastStderr || (result.exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
9812
|
+
const failDetail = formatTurnExitError(result.exitCode, detail);
|
|
9510
9813
|
setStatus(
|
|
9511
9814
|
threadId,
|
|
9512
9815
|
result.exitCode === 0 ? "idle" : "error",
|
|
@@ -9542,20 +9845,30 @@ var Orchestrator = class {
|
|
|
9542
9845
|
}
|
|
9543
9846
|
/**
|
|
9544
9847
|
* Stop an in-flight agent turn.
|
|
9545
|
-
*
|
|
9546
|
-
*
|
|
9547
|
-
*
|
|
9848
|
+
*
|
|
9849
|
+
* - Default `clearQueue: true` (force-stop): empties queued prompts so nothing
|
|
9850
|
+
* resumes. Used by MCP force-stop, archive, and cloud-connect.
|
|
9851
|
+
* - Desktop Stop uses `{ clearQueue: false }` so follow-ups stay editable.
|
|
9852
|
+
* - `continueQueue: true` (Send now): keep the queue and let drainQueue resume
|
|
9853
|
+
* after the interrupted turn unwinds. Without it, drain pauses until send /
|
|
9854
|
+
* promote.
|
|
9548
9855
|
*/
|
|
9549
9856
|
stop(threadRef, opts) {
|
|
9550
9857
|
const clearQueue = opts?.clearQueue !== false;
|
|
9858
|
+
const continueQueue = opts?.continueQueue === true;
|
|
9551
9859
|
const thread = this.requireThread(threadRef);
|
|
9552
9860
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
9553
9861
|
if (inFlight) {
|
|
9554
9862
|
this.stoppedTurns.add(thread.id);
|
|
9555
9863
|
}
|
|
9556
9864
|
if (clearQueue && thread.queue.length > 0) {
|
|
9865
|
+
this.haltDrain.delete(thread.id);
|
|
9557
9866
|
updateThread(thread.id, { queue: [] });
|
|
9558
9867
|
this.emit({ type: "queue_changed", threadId: thread.id, queue: [] });
|
|
9868
|
+
} else if (!clearQueue && !continueQueue) {
|
|
9869
|
+
this.haltDrain.add(thread.id);
|
|
9870
|
+
} else if (continueQueue) {
|
|
9871
|
+
this.haltDrain.delete(thread.id);
|
|
9559
9872
|
}
|
|
9560
9873
|
const handle = this.activeTurns.get(thread.id);
|
|
9561
9874
|
if (handle) handle.kill();
|
|
@@ -10150,9 +10463,11 @@ function getOrchestrator() {
|
|
|
10150
10463
|
}
|
|
10151
10464
|
async function startOrchestration(opts) {
|
|
10152
10465
|
const repoPath = opts.repoPath?.trim();
|
|
10466
|
+
const goal = opts.goal.trim();
|
|
10467
|
+
const orch = getOrchestrator();
|
|
10153
10468
|
if (!repoPath || isGlobalRepoPath(repoPath)) {
|
|
10154
|
-
|
|
10155
|
-
sourceRef:
|
|
10469
|
+
const thread2 = createGlobalChat({
|
|
10470
|
+
sourceRef: goal,
|
|
10156
10471
|
agent: opts.agent,
|
|
10157
10472
|
autonomy: opts.autonomy,
|
|
10158
10473
|
model: opts.model,
|
|
@@ -10160,9 +10475,13 @@ async function startOrchestration(opts) {
|
|
|
10160
10475
|
planMode: opts.planMode,
|
|
10161
10476
|
attachments: opts.attachments
|
|
10162
10477
|
});
|
|
10478
|
+
if (goal) {
|
|
10479
|
+
return orch.send(thread2.id, goal);
|
|
10480
|
+
}
|
|
10481
|
+
return thread2;
|
|
10163
10482
|
}
|
|
10164
10483
|
const { titleFromPrompt: titleFromPrompt2 } = await Promise.resolve().then(() => (init_title(), title_exports));
|
|
10165
|
-
const title = titleFromPrompt2(
|
|
10484
|
+
const title = titleFromPrompt2(goal) || "Orchestration";
|
|
10166
10485
|
const createOpts = {
|
|
10167
10486
|
agent: opts.agent,
|
|
10168
10487
|
repoPath,
|
|
@@ -10189,10 +10508,14 @@ async function startOrchestration(opts) {
|
|
|
10189
10508
|
});
|
|
10190
10509
|
});
|
|
10191
10510
|
const { updateThread: upd } = await Promise.resolve().then(() => (init_thread_store(), thread_store_exports));
|
|
10192
|
-
|
|
10511
|
+
const updated = upd(thread.id, {
|
|
10193
10512
|
sourceType: "orchestration",
|
|
10194
|
-
sourceRef:
|
|
10513
|
+
sourceRef: goal
|
|
10195
10514
|
});
|
|
10515
|
+
if (goal) {
|
|
10516
|
+
return orch.send(updated.id, goal);
|
|
10517
|
+
}
|
|
10518
|
+
return updated;
|
|
10196
10519
|
}
|
|
10197
10520
|
|
|
10198
10521
|
// src/index.ts
|
|
@@ -10288,11 +10611,15 @@ async function startMcpServer() {
|
|
|
10288
10611
|
);
|
|
10289
10612
|
server.tool(
|
|
10290
10613
|
"present_artifact",
|
|
10291
|
-
"Show an HTML, SVG, or
|
|
10614
|
+
"Show an HTML, SVG, markdown, or React document in Sideboard\u2019s Claude-style side column (desktop). Use instead of claude.ai\u2019s artifact tool \u2014 pass the full document content. Prefer type=html for interactive pages. For type=react, pass a single component module that `export default`s a component (JSX/TSX ok) \u2014 Sideboard bootstraps React/ReactDOM/Babel and renders it; only `react`/`react-dom` imports are available, no other npm packages.",
|
|
10292
10615
|
{
|
|
10293
10616
|
title: import_zod.z.string().describe("Short title shown in the artifact pane header"),
|
|
10294
|
-
type: import_zod.z.enum(["html", "svg", "markdown"]).describe(
|
|
10295
|
-
|
|
10617
|
+
type: import_zod.z.enum(["html", "svg", "markdown", "react"]).describe(
|
|
10618
|
+
"Artifact kind \u2014 html opens an iframe preview; react transpiles and renders a default-exported component in a sandboxed iframe"
|
|
10619
|
+
),
|
|
10620
|
+
content: import_zod.z.string().describe(
|
|
10621
|
+
"Full document body (complete HTML page, SVG markup, markdown, or a React component module with a default export)"
|
|
10622
|
+
),
|
|
10296
10623
|
artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
10297
10624
|
},
|
|
10298
10625
|
async ({ title, type, content, artifact_id }) => {
|