@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/mcp/run-stdio.cjs
CHANGED
|
@@ -30,6 +30,120 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
mod
|
|
31
31
|
));
|
|
32
32
|
|
|
33
|
+
// src/agents/error-detail.ts
|
|
34
|
+
function formatUnknownDetail(err) {
|
|
35
|
+
if (err == null) return "";
|
|
36
|
+
if (typeof err === "string") return err.trim();
|
|
37
|
+
if (err instanceof Error) {
|
|
38
|
+
const base = err.message.trim() || err.name;
|
|
39
|
+
const code = "code" in err && typeof err.code === "string" ? err.code.trim() : "";
|
|
40
|
+
return code && !base.includes(code) ? `${base} (${code})` : base;
|
|
41
|
+
}
|
|
42
|
+
if (typeof err === "object") {
|
|
43
|
+
const o = err;
|
|
44
|
+
const nested = o.error != null && typeof o.error === "object" ? formatUnknownDetail(o.error) : "";
|
|
45
|
+
const message = typeof o.message === "string" ? o.message.trim() : typeof o.error === "string" ? o.error.trim() : typeof o.result === "string" ? o.result.trim() : nested;
|
|
46
|
+
const code = typeof o.code === "string" ? o.code.trim() : "";
|
|
47
|
+
if (message) return code && !message.includes(code) ? `${message} (${code})` : message;
|
|
48
|
+
try {
|
|
49
|
+
const json = JSON.stringify(err);
|
|
50
|
+
if (json && json !== "{}" && json !== "null") return json;
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const fallback = String(err);
|
|
55
|
+
return fallback === "[object Object]" ? "" : fallback;
|
|
56
|
+
}
|
|
57
|
+
function extractJsonErrorMessage(obj) {
|
|
58
|
+
const nested = obj.error != null && typeof obj.error === "object" ? obj.error : null;
|
|
59
|
+
const candidates = [
|
|
60
|
+
typeof obj.message === "string" ? obj.message : null,
|
|
61
|
+
typeof obj.error === "string" ? obj.error : null,
|
|
62
|
+
nested && typeof nested.message === "string" ? nested.message : null,
|
|
63
|
+
typeof obj.result === "string" ? obj.result : null,
|
|
64
|
+
typeof obj.detail === "string" ? obj.detail : null
|
|
65
|
+
];
|
|
66
|
+
for (const c of candidates) {
|
|
67
|
+
const t = c?.trim();
|
|
68
|
+
if (t) return t;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(obj.errors)) {
|
|
71
|
+
const parts = obj.errors.map((e) => formatUnknownDetail(e)).map((s) => s.trim()).filter(Boolean);
|
|
72
|
+
if (parts.length) return parts.join("; ");
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function pushTurnStderr(tail, line, maxLines = 12) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed) return;
|
|
79
|
+
if (NODE_VERSION_FOOTER.test(trimmed)) return;
|
|
80
|
+
if (/^reconnecting\.\.\./i.test(trimmed)) return;
|
|
81
|
+
tail.push(trimmed);
|
|
82
|
+
while (tail.length > maxLines) tail.shift();
|
|
83
|
+
}
|
|
84
|
+
function summarizeTurnStderr(tail, maxChars = 500) {
|
|
85
|
+
if (tail.length === 0) return "";
|
|
86
|
+
const joined = tail.slice(-6).join("\n").trim();
|
|
87
|
+
if (joined.length <= maxChars) return joined;
|
|
88
|
+
return joined.slice(joined.length - maxChars);
|
|
89
|
+
}
|
|
90
|
+
function looksLikeAgentFailureMessage(text) {
|
|
91
|
+
const lower = text.trim().toLowerCase();
|
|
92
|
+
if (!lower) return false;
|
|
93
|
+
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(
|
|
94
|
+
lower
|
|
95
|
+
) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower);
|
|
96
|
+
}
|
|
97
|
+
function fallbackTurnFailDetail(assistantText) {
|
|
98
|
+
const t = assistantText.trim();
|
|
99
|
+
if (!t) return "";
|
|
100
|
+
if (looksLikeAgentFailureMessage(t)) return t;
|
|
101
|
+
if (t.length <= 400 && !/\n\n/.test(t)) return t;
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
function humanizeAgentFailDetail(detail) {
|
|
105
|
+
const raw = detail.trim();
|
|
106
|
+
if (!raw) return raw;
|
|
107
|
+
const lower = raw.toLowerCase();
|
|
108
|
+
if (/credit balance is too low|out of credits|insufficient.?quota|quota.?exceeded|billing/.test(lower)) {
|
|
109
|
+
return `${raw} \u2014 add credits or switch auth, then retry.`;
|
|
110
|
+
}
|
|
111
|
+
if (/hit your (session|weekly|opus) limit|usage limit|you've hit your/.test(lower)) {
|
|
112
|
+
return raw.includes("reset") ? raw : `${raw} \u2014 wait for the limit window to reset, then retry.`;
|
|
113
|
+
}
|
|
114
|
+
if (/\b429\b|rate.?limit|too many requests/.test(lower)) {
|
|
115
|
+
return `${raw} \u2014 wait a moment and retry.`;
|
|
116
|
+
}
|
|
117
|
+
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(
|
|
118
|
+
lower
|
|
119
|
+
)) {
|
|
120
|
+
return `${raw} \u2014 check agent login / API key in Settings.`;
|
|
121
|
+
}
|
|
122
|
+
if (/model .{0,80}(not found|unavailable|unknown|invalid)/.test(lower)) {
|
|
123
|
+
return `${raw} \u2014 pick another model in the agent options.`;
|
|
124
|
+
}
|
|
125
|
+
if (/context.*(too long|exceed)|prompt is too long|conversation too long/.test(lower)) {
|
|
126
|
+
return `${raw} \u2014 start a new chat or compact context, then retry.`;
|
|
127
|
+
}
|
|
128
|
+
return raw;
|
|
129
|
+
}
|
|
130
|
+
function formatTurnExitError(exitCode, stderrSummary) {
|
|
131
|
+
const code = exitCode ?? 1;
|
|
132
|
+
const detail = humanizeAgentFailDetail(stderrSummary);
|
|
133
|
+
if (!detail) {
|
|
134
|
+
return `exit ${code}: agent exited without details (credits, auth, rate limits, or a CLI error)`;
|
|
135
|
+
}
|
|
136
|
+
if (looksLikeAgentFailureMessage(stderrSummary)) return detail;
|
|
137
|
+
return `exit ${code}: ${detail}`;
|
|
138
|
+
}
|
|
139
|
+
var NODE_VERSION_FOOTER;
|
|
140
|
+
var init_error_detail = __esm({
|
|
141
|
+
"src/agents/error-detail.ts"() {
|
|
142
|
+
"use strict";
|
|
143
|
+
NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
33
147
|
// src/hook/settings.ts
|
|
34
148
|
function expandHome(path) {
|
|
35
149
|
if (path.startsWith("~/") || path === "~") {
|
|
@@ -3343,7 +3457,7 @@ function parseBrightsyCliLine(line) {
|
|
|
3343
3457
|
return { type: "thinking", data: obj.text };
|
|
3344
3458
|
}
|
|
3345
3459
|
if (obj.type === "error") {
|
|
3346
|
-
const msg =
|
|
3460
|
+
const msg = extractJsonErrorMessage(obj) || formatUnknownDetail(obj.error) || formatUnknownDetail(obj.message) || trimmed;
|
|
3347
3461
|
return [
|
|
3348
3462
|
{ type: "stderr", data: msg },
|
|
3349
3463
|
{ type: "stdout", data: `Error: ${msg}` }
|
|
@@ -3380,6 +3494,12 @@ function parseBrightsyCliLine(line) {
|
|
|
3380
3494
|
if (trimmed.startsWith("{") && /"type"\s*:\s*"(tool_use|tool_result|tool|text|thinking|usage|done|error)"/.test(trimmed)) {
|
|
3381
3495
|
return null;
|
|
3382
3496
|
}
|
|
3497
|
+
if (/error|failed|unauthorized|quota|limit|not logged in/i.test(trimmed)) {
|
|
3498
|
+
return [
|
|
3499
|
+
{ type: "stderr", data: trimmed },
|
|
3500
|
+
{ type: "stdout", data: `Error: ${trimmed}` }
|
|
3501
|
+
];
|
|
3502
|
+
}
|
|
3383
3503
|
return { type: "stdout", data: line };
|
|
3384
3504
|
}
|
|
3385
3505
|
}
|
|
@@ -3534,6 +3654,7 @@ var init_brightsy = __esm({
|
|
|
3534
3654
|
init_connected_teams();
|
|
3535
3655
|
init_config();
|
|
3536
3656
|
init_brightsy_targets();
|
|
3657
|
+
init_error_detail();
|
|
3537
3658
|
init_turn_input();
|
|
3538
3659
|
init_brightsy_targets();
|
|
3539
3660
|
brightsyAdapter = {
|
|
@@ -3865,6 +3986,30 @@ function usageFromClaude(usage) {
|
|
|
3865
3986
|
cacheWriteTokens: usage.cache_creation_input_tokens ? Number(usage.cache_creation_input_tokens) : void 0
|
|
3866
3987
|
};
|
|
3867
3988
|
}
|
|
3989
|
+
function claudeResultErrorDetail(obj) {
|
|
3990
|
+
const isError = Boolean(obj.is_error) || typeof obj.subtype === "string" && /^error/i.test(obj.subtype);
|
|
3991
|
+
const fromResult = typeof obj.result === "string" ? obj.result.trim() : "";
|
|
3992
|
+
if (fromResult && (isError || looksLikeAgentFailureMessage(fromResult))) {
|
|
3993
|
+
return fromResult;
|
|
3994
|
+
}
|
|
3995
|
+
if (!isError) return null;
|
|
3996
|
+
const errors = obj.errors;
|
|
3997
|
+
if (Array.isArray(errors)) {
|
|
3998
|
+
const parts = errors.map((e) => {
|
|
3999
|
+
if (typeof e === "string") return e.trim();
|
|
4000
|
+
if (e && typeof e === "object" && typeof e.message === "string") {
|
|
4001
|
+
return e.message.trim();
|
|
4002
|
+
}
|
|
4003
|
+
return "";
|
|
4004
|
+
}).filter(Boolean);
|
|
4005
|
+
if (parts.length) return parts.join("; ");
|
|
4006
|
+
}
|
|
4007
|
+
if (typeof obj.error === "string" && obj.error.trim()) return obj.error.trim();
|
|
4008
|
+
if (typeof obj.subtype === "string" && obj.subtype) {
|
|
4009
|
+
return obj.subtype.replace(/^error[_-]?/i, "").replace(/_/g, " ") || "Claude turn failed";
|
|
4010
|
+
}
|
|
4011
|
+
return "Claude turn failed";
|
|
4012
|
+
}
|
|
3868
4013
|
function eventsFromContentBlocks(blocks) {
|
|
3869
4014
|
if (!blocks?.length) return [];
|
|
3870
4015
|
const out = [];
|
|
@@ -3934,6 +4079,7 @@ var init_claude = __esm({
|
|
|
3934
4079
|
init_run();
|
|
3935
4080
|
init_app_settings();
|
|
3936
4081
|
init_claude_mcp();
|
|
4082
|
+
init_error_detail();
|
|
3937
4083
|
init_injected_mcp();
|
|
3938
4084
|
init_turn_input();
|
|
3939
4085
|
init_types();
|
|
@@ -4128,8 +4274,13 @@ var init_claude = __esm({
|
|
|
4128
4274
|
}
|
|
4129
4275
|
if (obj.type === "result") {
|
|
4130
4276
|
const events = [];
|
|
4131
|
-
const
|
|
4132
|
-
if (
|
|
4277
|
+
const errorDetail = claudeResultErrorDetail(obj);
|
|
4278
|
+
if (errorDetail) {
|
|
4279
|
+
events.push({ type: "stderr", data: errorDetail });
|
|
4280
|
+
} else {
|
|
4281
|
+
const text = obj.result;
|
|
4282
|
+
if (typeof text === "string" && text) events.push({ type: "stdout", data: text });
|
|
4283
|
+
}
|
|
4133
4284
|
const usage = usageFromClaude(obj.usage);
|
|
4134
4285
|
if (usage) events.push({ type: "usage", data: usage });
|
|
4135
4286
|
if (events.length === 0) return null;
|
|
@@ -4237,6 +4388,7 @@ var init_codex = __esm({
|
|
|
4237
4388
|
import_node_os7 = require("os");
|
|
4238
4389
|
import_node_path11 = require("path");
|
|
4239
4390
|
init_run();
|
|
4391
|
+
init_error_detail();
|
|
4240
4392
|
init_turn_input();
|
|
4241
4393
|
init_types();
|
|
4242
4394
|
CODEX_PROMPT_ARG_MAX = 2e5;
|
|
@@ -4321,23 +4473,49 @@ var init_codex = __esm({
|
|
|
4321
4473
|
if (!trimmed) return null;
|
|
4322
4474
|
try {
|
|
4323
4475
|
const obj = JSON.parse(trimmed);
|
|
4324
|
-
const
|
|
4325
|
-
if (
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4476
|
+
const type = typeof obj.type === "string" ? obj.type : "";
|
|
4477
|
+
if (type === "turn.failed" || type === "turn_failed") {
|
|
4478
|
+
const detail = extractJsonErrorMessage(obj) || extractJsonErrorMessage(obj.error ?? {}) || "Codex turn failed";
|
|
4479
|
+
return { type: "stderr", data: detail };
|
|
4480
|
+
}
|
|
4481
|
+
if (type === "error") {
|
|
4482
|
+
const detail = extractJsonErrorMessage(obj) || trimmed;
|
|
4483
|
+
if (/^reconnecting\.\.\./i.test(detail)) return null;
|
|
4484
|
+
return { type: "stderr", data: detail };
|
|
4329
4485
|
}
|
|
4330
4486
|
if (typeof obj.item === "object" && obj.item !== null) {
|
|
4331
4487
|
const item = obj.item;
|
|
4488
|
+
if (item.type === "error") {
|
|
4489
|
+
const detail = item.message?.trim() || extractJsonErrorMessage(obj) || "Codex item error";
|
|
4490
|
+
return { type: "stderr", data: detail };
|
|
4491
|
+
}
|
|
4332
4492
|
if (item.type === "agent_message" && item.text) {
|
|
4333
4493
|
return { type: "stdout", data: item.text };
|
|
4334
4494
|
}
|
|
4495
|
+
if (item.status === "failed") {
|
|
4496
|
+
const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${item.type ?? "item"} failed`;
|
|
4497
|
+
return { type: "stderr", data: detail };
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
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;
|
|
4501
|
+
if (sid && (type === "thread.started" || type === "session" || !type)) {
|
|
4502
|
+
return { type: "session_id", data: sid };
|
|
4503
|
+
}
|
|
4504
|
+
if (sid && type.endsWith(".started")) {
|
|
4505
|
+
return { type: "session_id", data: sid };
|
|
4506
|
+
}
|
|
4507
|
+
if (type === "turn.completed" || type === "turn_completed") {
|
|
4508
|
+
const usage = usageFromCodex(obj.usage);
|
|
4509
|
+
return usage ? { type: "usage", data: usage } : null;
|
|
4335
4510
|
}
|
|
4336
|
-
if (typeof obj.content === "string") {
|
|
4511
|
+
if (typeof obj.content === "string" && obj.content.trim()) {
|
|
4337
4512
|
return { type: "stdout", data: obj.content };
|
|
4338
4513
|
}
|
|
4339
|
-
return
|
|
4514
|
+
return null;
|
|
4340
4515
|
} catch {
|
|
4516
|
+
if (/error|failed|unauthorized|quota|limit/i.test(trimmed)) {
|
|
4517
|
+
return { type: "stderr", data: trimmed };
|
|
4518
|
+
}
|
|
4341
4519
|
return { type: "stdout", data: line };
|
|
4342
4520
|
}
|
|
4343
4521
|
},
|
|
@@ -4453,7 +4631,12 @@ function cursorSdkMessageToEvents(msg) {
|
|
|
4453
4631
|
if (usage) return [{ type: "usage", data: usage }];
|
|
4454
4632
|
}
|
|
4455
4633
|
if (msg.type === "status" && msg.status === "ERROR") {
|
|
4456
|
-
const
|
|
4634
|
+
const rawMessage = msg.message;
|
|
4635
|
+
const detail = (typeof rawMessage === "string" ? rawMessage.trim() : "") || extractJsonErrorMessage(msg) || formatUnknownDetail(msg.error) || msg.text || "Cursor run entered ERROR status";
|
|
4636
|
+
return [{ type: "stderr", data: detail }];
|
|
4637
|
+
}
|
|
4638
|
+
if (msg.type === "error") {
|
|
4639
|
+
const detail = extractJsonErrorMessage(msg) || formatUnknownDetail(msg.error) || msg.text || "Cursor run error";
|
|
4457
4640
|
return [{ type: "stderr", data: detail }];
|
|
4458
4641
|
}
|
|
4459
4642
|
return [];
|
|
@@ -4478,6 +4661,7 @@ function parseCursorRunnerLine(line) {
|
|
|
4478
4661
|
var init_cursor_events = __esm({
|
|
4479
4662
|
"src/agents/cursor-events.ts"() {
|
|
4480
4663
|
"use strict";
|
|
4664
|
+
init_error_detail();
|
|
4481
4665
|
}
|
|
4482
4666
|
});
|
|
4483
4667
|
|
|
@@ -4710,6 +4894,7 @@ var init_opencode = __esm({
|
|
|
4710
4894
|
"src/agents/opencode.ts"() {
|
|
4711
4895
|
"use strict";
|
|
4712
4896
|
init_run();
|
|
4897
|
+
init_error_detail();
|
|
4713
4898
|
init_turn_input();
|
|
4714
4899
|
init_types();
|
|
4715
4900
|
FALLBACK_OPENCODE_MODELS = [
|
|
@@ -4787,6 +4972,10 @@ var init_opencode = __esm({
|
|
|
4787
4972
|
if (!trimmed) return null;
|
|
4788
4973
|
try {
|
|
4789
4974
|
const obj = JSON.parse(trimmed);
|
|
4975
|
+
if (obj.type === "error") {
|
|
4976
|
+
const detail = extractJsonErrorMessage(obj) || formatUnknownDetail(obj.error) || formatUnknownDetail(obj.message) || trimmed;
|
|
4977
|
+
return { type: "stderr", data: detail };
|
|
4978
|
+
}
|
|
4790
4979
|
const sid = typeof obj.sessionID === "string" && obj.sessionID || typeof obj.sessionId === "string" && obj.sessionId || typeof obj.session_id === "string" && obj.session_id;
|
|
4791
4980
|
if (sid) return { type: "session_id", data: sid };
|
|
4792
4981
|
if (obj.type === "text") {
|
|
@@ -4810,12 +4999,6 @@ var init_opencode = __esm({
|
|
|
4810
4999
|
content: part?.output ?? part?.content ?? obj.output ?? obj.content
|
|
4811
5000
|
};
|
|
4812
5001
|
}
|
|
4813
|
-
if (obj.type === "error") {
|
|
4814
|
-
return {
|
|
4815
|
-
type: "stderr",
|
|
4816
|
-
data: String(obj.error ?? trimmed)
|
|
4817
|
-
};
|
|
4818
|
-
}
|
|
4819
5002
|
if (obj.type === "step_finish" || obj.type === "step-finish") {
|
|
4820
5003
|
const part = obj.part;
|
|
4821
5004
|
const usage = usageFromOpencode(
|
|
@@ -4823,8 +5006,11 @@ var init_opencode = __esm({
|
|
|
4823
5006
|
);
|
|
4824
5007
|
return usage ? { type: "usage", data: usage } : null;
|
|
4825
5008
|
}
|
|
4826
|
-
return
|
|
5009
|
+
return null;
|
|
4827
5010
|
} catch {
|
|
5011
|
+
if (/error|failed|unauthorized|quota|limit/i.test(trimmed)) {
|
|
5012
|
+
return { type: "stderr", data: trimmed };
|
|
5013
|
+
}
|
|
4828
5014
|
return { type: "stdout", data: line };
|
|
4829
5015
|
}
|
|
4830
5016
|
},
|
|
@@ -5267,6 +5453,7 @@ var import_node_path23 = require("path");
|
|
|
5267
5453
|
// src/orchestrator/orchestrator.ts
|
|
5268
5454
|
var import_node_events = require("events");
|
|
5269
5455
|
var import_node_fs24 = require("fs");
|
|
5456
|
+
init_error_detail();
|
|
5270
5457
|
|
|
5271
5458
|
// src/agents/spawn.ts
|
|
5272
5459
|
var import_node_readline = require("readline");
|
|
@@ -5729,6 +5916,49 @@ function pipeLines(stream, onLine) {
|
|
|
5729
5916
|
const rl = (0, import_node_readline2.createInterface)({ input: stream });
|
|
5730
5917
|
rl.on("line", onLine);
|
|
5731
5918
|
}
|
|
5919
|
+
function killScriptTree(child, ports = []) {
|
|
5920
|
+
const pid = child.pid;
|
|
5921
|
+
if (pid) {
|
|
5922
|
+
try {
|
|
5923
|
+
if (process.platform === "win32") {
|
|
5924
|
+
void (0, import_execa3.execa)("taskkill", ["/pid", String(pid), "/T", "/F"], { reject: false });
|
|
5925
|
+
} else {
|
|
5926
|
+
process.kill(-pid, "SIGTERM");
|
|
5927
|
+
setTimeout(() => {
|
|
5928
|
+
try {
|
|
5929
|
+
process.kill(-pid, "SIGKILL");
|
|
5930
|
+
} catch {
|
|
5931
|
+
}
|
|
5932
|
+
}, 2500).unref?.();
|
|
5933
|
+
}
|
|
5934
|
+
} catch {
|
|
5935
|
+
try {
|
|
5936
|
+
child.kill("SIGTERM");
|
|
5937
|
+
} catch {
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
}
|
|
5941
|
+
for (const port of ports) {
|
|
5942
|
+
if (!Number.isFinite(port) || port <= 0) continue;
|
|
5943
|
+
if (process.platform === "win32") {
|
|
5944
|
+
void (0, import_execa3.execa)(
|
|
5945
|
+
"powershell",
|
|
5946
|
+
[
|
|
5947
|
+
"-NoProfile",
|
|
5948
|
+
"-Command",
|
|
5949
|
+
`Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
|
|
5950
|
+
],
|
|
5951
|
+
{ reject: false }
|
|
5952
|
+
);
|
|
5953
|
+
} else {
|
|
5954
|
+
void (0, import_execa3.execa)(
|
|
5955
|
+
"zsh",
|
|
5956
|
+
["-lc", `pids=$(lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null); [ -n "$pids" ] && kill -TERM $pids 2>/dev/null; true`],
|
|
5957
|
+
{ reject: false }
|
|
5958
|
+
);
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
}
|
|
5732
5962
|
async function spawnWorkspaceScript(command, opts) {
|
|
5733
5963
|
const loginEnv = await captureLoginEnv();
|
|
5734
5964
|
const env = buildWorkspaceScriptEnv(
|
|
@@ -5745,18 +5975,17 @@ async function spawnWorkspaceScript(command, opts) {
|
|
|
5745
5975
|
const child = (0, import_execa3.execa)(shell, ["-lc", command], {
|
|
5746
5976
|
cwd: opts.worktreePath,
|
|
5747
5977
|
reject: false,
|
|
5748
|
-
env
|
|
5978
|
+
env,
|
|
5979
|
+
// Own process group so Stop can tear down the whole tree (not just the shell).
|
|
5980
|
+
// There is no settings.toml `stop=` / teardown hook for run scripts.
|
|
5981
|
+
...process.platform === "win32" ? {} : { detached: true }
|
|
5749
5982
|
});
|
|
5750
5983
|
pipeLines(child.stdout, opts.onLine);
|
|
5751
5984
|
pipeLines(child.stderr, opts.onLine);
|
|
5985
|
+
const ports = opts.ports ?? [];
|
|
5752
5986
|
return {
|
|
5753
5987
|
pid: child.pid,
|
|
5754
|
-
kill: () =>
|
|
5755
|
-
try {
|
|
5756
|
-
child.kill("SIGTERM");
|
|
5757
|
-
} catch {
|
|
5758
|
-
}
|
|
5759
|
-
},
|
|
5988
|
+
kill: () => killScriptTree(child, ports),
|
|
5760
5989
|
done: child.then((r) => r.exitCode ?? null),
|
|
5761
5990
|
child
|
|
5762
5991
|
};
|
|
@@ -8282,6 +8511,11 @@ var Orchestrator = class {
|
|
|
8282
8511
|
* the killed turn's handle.done resolves.
|
|
8283
8512
|
*/
|
|
8284
8513
|
stoppedTurns = /* @__PURE__ */ new Set();
|
|
8514
|
+
/**
|
|
8515
|
+
* Pause drainQueue after the in-flight turn unwinds (Stop with a preserved
|
|
8516
|
+
* queue). Cleared when the user sends or promotes a queued message again.
|
|
8517
|
+
*/
|
|
8518
|
+
haltDrain = /* @__PURE__ */ new Set();
|
|
8285
8519
|
/** WIP snapshot SHA at the start of the latest agent turn (per thread). */
|
|
8286
8520
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
8287
8521
|
maxConcurrent;
|
|
@@ -8349,7 +8583,7 @@ var Orchestrator = class {
|
|
|
8349
8583
|
} catch {
|
|
8350
8584
|
}
|
|
8351
8585
|
for (const thread of listThreads()) {
|
|
8352
|
-
if (thread.queue.length > 0) {
|
|
8586
|
+
if (thread.queue.length > 0 && thread.status !== "stopped") {
|
|
8353
8587
|
void this.drainQueue(thread.id);
|
|
8354
8588
|
}
|
|
8355
8589
|
}
|
|
@@ -8410,6 +8644,7 @@ var Orchestrator = class {
|
|
|
8410
8644
|
return withThreadLock(thread.id, async () => {
|
|
8411
8645
|
const current = this.requireThread(thread.id);
|
|
8412
8646
|
const queue = [...current.queue, prompt];
|
|
8647
|
+
this.haltDrain.delete(thread.id);
|
|
8413
8648
|
updateThread(thread.id, { queue, status: "queued" });
|
|
8414
8649
|
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
8415
8650
|
this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
|
|
@@ -8424,11 +8659,75 @@ var Orchestrator = class {
|
|
|
8424
8659
|
}
|
|
8425
8660
|
return results;
|
|
8426
8661
|
}
|
|
8662
|
+
/** Edit the text of a not-yet-started queued message. */
|
|
8663
|
+
async editQueuedMessage(threadRef, index, text) {
|
|
8664
|
+
const thread = this.requireThread(threadRef);
|
|
8665
|
+
return withThreadLock(thread.id, async () => {
|
|
8666
|
+
const current = this.requireThread(thread.id);
|
|
8667
|
+
const trimmed = text.trim();
|
|
8668
|
+
if (!trimmed || index < 0 || index >= current.queue.length) {
|
|
8669
|
+
return current;
|
|
8670
|
+
}
|
|
8671
|
+
const queue = current.queue.map((p, i) => i === index ? trimmed : p);
|
|
8672
|
+
updateThread(thread.id, { queue });
|
|
8673
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
8674
|
+
return this.requireThread(thread.id);
|
|
8675
|
+
});
|
|
8676
|
+
}
|
|
8677
|
+
/** Remove a not-yet-started queued message. */
|
|
8678
|
+
async removeQueuedMessage(threadRef, index) {
|
|
8679
|
+
const thread = this.requireThread(threadRef);
|
|
8680
|
+
return withThreadLock(thread.id, async () => {
|
|
8681
|
+
const current = this.requireThread(thread.id);
|
|
8682
|
+
if (index < 0 || index >= current.queue.length) return current;
|
|
8683
|
+
const queue = current.queue.filter((_, i) => i !== index);
|
|
8684
|
+
const stillQueued = queue.length > 0;
|
|
8685
|
+
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
8686
|
+
updateThread(thread.id, {
|
|
8687
|
+
queue,
|
|
8688
|
+
status: !stillQueued && !inFlight && current.status === "queued" ? "idle" : current.status
|
|
8689
|
+
});
|
|
8690
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
8691
|
+
const next = this.requireThread(thread.id);
|
|
8692
|
+
this.emit({ type: "status_changed", threadId: thread.id, status: next.status });
|
|
8693
|
+
return next;
|
|
8694
|
+
});
|
|
8695
|
+
}
|
|
8696
|
+
/**
|
|
8697
|
+
* Promote a queued message to run next, interrupting the in-flight turn (if any).
|
|
8698
|
+
* The current turn is stopped without clearing the rest of the queue — drainQueue
|
|
8699
|
+
* picks the promoted message up as soon as the interrupted turn unwinds.
|
|
8700
|
+
*/
|
|
8701
|
+
async sendQueuedMessageNow(threadRef, index) {
|
|
8702
|
+
const thread = this.requireThread(threadRef);
|
|
8703
|
+
const promoted = await withThreadLock(thread.id, async () => {
|
|
8704
|
+
const current = this.requireThread(thread.id);
|
|
8705
|
+
if (index < 0 || index >= current.queue.length) return false;
|
|
8706
|
+
const item = current.queue[index];
|
|
8707
|
+
const rest = current.queue.filter((_, i) => i !== index);
|
|
8708
|
+
const queue = [item, ...rest];
|
|
8709
|
+
this.haltDrain.delete(thread.id);
|
|
8710
|
+
updateThread(thread.id, { queue });
|
|
8711
|
+
this.emit({ type: "queue_changed", threadId: thread.id, queue });
|
|
8712
|
+
return true;
|
|
8713
|
+
});
|
|
8714
|
+
if (!promoted) return this.requireThread(thread.id);
|
|
8715
|
+
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
8716
|
+
if (inFlight) {
|
|
8717
|
+
this.stop(thread.id, { clearQueue: false, continueQueue: true });
|
|
8718
|
+
} else {
|
|
8719
|
+
void this.drainQueue(thread.id);
|
|
8720
|
+
}
|
|
8721
|
+
return this.requireThread(thread.id);
|
|
8722
|
+
}
|
|
8427
8723
|
async drainQueue(threadId) {
|
|
8428
8724
|
if (this.draining.has(threadId)) return;
|
|
8429
8725
|
this.draining.add(threadId);
|
|
8430
8726
|
try {
|
|
8431
8727
|
while (true) {
|
|
8728
|
+
if (this.haltDrain.has(threadId)) {
|
|
8729
|
+
break;
|
|
8730
|
+
}
|
|
8432
8731
|
const thread = readThread(threadId);
|
|
8433
8732
|
if (!thread || thread.queue.length === 0) {
|
|
8434
8733
|
if (thread && thread.status === "queued") {
|
|
@@ -8441,7 +8740,7 @@ var Orchestrator = class {
|
|
|
8441
8740
|
await new Promise((r) => setTimeout(r, 250));
|
|
8442
8741
|
continue;
|
|
8443
8742
|
}
|
|
8444
|
-
if (this.activeTurns.has(threadId)) {
|
|
8743
|
+
if (this.activeTurns.has(threadId) || this.startingTurns.has(threadId)) {
|
|
8445
8744
|
await new Promise((r) => setTimeout(r, 100));
|
|
8446
8745
|
continue;
|
|
8447
8746
|
}
|
|
@@ -8576,7 +8875,7 @@ var Orchestrator = class {
|
|
|
8576
8875
|
...fresh.agent === "claude" && fresh.sessionId ? [] : [instructions, seed]
|
|
8577
8876
|
].filter(Boolean).join("\n\n---\n\n");
|
|
8578
8877
|
try {
|
|
8579
|
-
|
|
8878
|
+
const stderrTail = [];
|
|
8580
8879
|
const handle = await spawnAgentTurn(
|
|
8581
8880
|
fresh,
|
|
8582
8881
|
{ cachedPrefix, prompt: agentPrompt },
|
|
@@ -8585,8 +8884,8 @@ var Orchestrator = class {
|
|
|
8585
8884
|
if (event.type === "session_id") {
|
|
8586
8885
|
updateThread(threadId, { sessionId: event.data });
|
|
8587
8886
|
}
|
|
8588
|
-
if (event.type === "stderr" && typeof event.data === "string"
|
|
8589
|
-
|
|
8887
|
+
if (event.type === "stderr" && typeof event.data === "string") {
|
|
8888
|
+
pushTurnStderr(stderrTail, event.data);
|
|
8590
8889
|
}
|
|
8591
8890
|
}
|
|
8592
8891
|
);
|
|
@@ -8611,10 +8910,12 @@ var Orchestrator = class {
|
|
|
8611
8910
|
if (result.sessionId) {
|
|
8612
8911
|
updateThread(threadId, { sessionId: result.sessionId });
|
|
8613
8912
|
}
|
|
8614
|
-
|
|
8913
|
+
const assistantText = result.assistantText.trim();
|
|
8914
|
+
const failureOnlyMessage = result.exitCode !== 0 && looksLikeAgentFailureMessage(assistantText) && !result.parts.some((p) => p.type === "tool" || p.type === "thinking");
|
|
8915
|
+
if (!failureOnlyMessage && (assistantText || result.parts.length > 0)) {
|
|
8615
8916
|
appendMessage(threadId, {
|
|
8616
8917
|
role: "agent",
|
|
8617
|
-
text:
|
|
8918
|
+
text: assistantText,
|
|
8618
8919
|
parts: result.parts.length > 0 ? result.parts : void 0,
|
|
8619
8920
|
durationMs: Math.max(0, Date.now() - turnStartedAt),
|
|
8620
8921
|
usage: result.usage ?? void 0,
|
|
@@ -8633,7 +8934,9 @@ var Orchestrator = class {
|
|
|
8633
8934
|
this.emit({ type: "status_changed", threadId, status: "stopped" });
|
|
8634
8935
|
this.emit({ type: "turn_finished", threadId, exitCode: result.exitCode });
|
|
8635
8936
|
} else {
|
|
8636
|
-
const
|
|
8937
|
+
const lastStderr = summarizeTurnStderr(stderrTail);
|
|
8938
|
+
const detail = lastStderr || (result.exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
|
|
8939
|
+
const failDetail = formatTurnExitError(result.exitCode, detail);
|
|
8637
8940
|
setStatus(
|
|
8638
8941
|
threadId,
|
|
8639
8942
|
result.exitCode === 0 ? "idle" : "error",
|
|
@@ -8669,20 +8972,30 @@ var Orchestrator = class {
|
|
|
8669
8972
|
}
|
|
8670
8973
|
/**
|
|
8671
8974
|
* Stop an in-flight agent turn.
|
|
8672
|
-
*
|
|
8673
|
-
*
|
|
8674
|
-
*
|
|
8975
|
+
*
|
|
8976
|
+
* - Default `clearQueue: true` (force-stop): empties queued prompts so nothing
|
|
8977
|
+
* resumes. Used by MCP force-stop, archive, and cloud-connect.
|
|
8978
|
+
* - Desktop Stop uses `{ clearQueue: false }` so follow-ups stay editable.
|
|
8979
|
+
* - `continueQueue: true` (Send now): keep the queue and let drainQueue resume
|
|
8980
|
+
* after the interrupted turn unwinds. Without it, drain pauses until send /
|
|
8981
|
+
* promote.
|
|
8675
8982
|
*/
|
|
8676
8983
|
stop(threadRef, opts) {
|
|
8677
8984
|
const clearQueue = opts?.clearQueue !== false;
|
|
8985
|
+
const continueQueue = opts?.continueQueue === true;
|
|
8678
8986
|
const thread = this.requireThread(threadRef);
|
|
8679
8987
|
const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id);
|
|
8680
8988
|
if (inFlight) {
|
|
8681
8989
|
this.stoppedTurns.add(thread.id);
|
|
8682
8990
|
}
|
|
8683
8991
|
if (clearQueue && thread.queue.length > 0) {
|
|
8992
|
+
this.haltDrain.delete(thread.id);
|
|
8684
8993
|
updateThread(thread.id, { queue: [] });
|
|
8685
8994
|
this.emit({ type: "queue_changed", threadId: thread.id, queue: [] });
|
|
8995
|
+
} else if (!clearQueue && !continueQueue) {
|
|
8996
|
+
this.haltDrain.add(thread.id);
|
|
8997
|
+
} else if (continueQueue) {
|
|
8998
|
+
this.haltDrain.delete(thread.id);
|
|
8686
8999
|
}
|
|
8687
9000
|
const handle = this.activeTurns.get(thread.id);
|
|
8688
9001
|
if (handle) handle.kill();
|
|
@@ -9483,11 +9796,15 @@ async function startMcpServer() {
|
|
|
9483
9796
|
);
|
|
9484
9797
|
server.tool(
|
|
9485
9798
|
"present_artifact",
|
|
9486
|
-
"Show an HTML, SVG, or
|
|
9799
|
+
"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.",
|
|
9487
9800
|
{
|
|
9488
9801
|
title: import_zod.z.string().describe("Short title shown in the artifact pane header"),
|
|
9489
|
-
type: import_zod.z.enum(["html", "svg", "markdown"]).describe(
|
|
9490
|
-
|
|
9802
|
+
type: import_zod.z.enum(["html", "svg", "markdown", "react"]).describe(
|
|
9803
|
+
"Artifact kind \u2014 html opens an iframe preview; react transpiles and renders a default-exported component in a sandboxed iframe"
|
|
9804
|
+
),
|
|
9805
|
+
content: import_zod.z.string().describe(
|
|
9806
|
+
"Full document body (complete HTML page, SVG markup, markdown, or a React component module with a default export)"
|
|
9807
|
+
),
|
|
9491
9808
|
artifact_id: import_zod.z.string().optional().describe("Stable id when updating the same artifact across turns")
|
|
9492
9809
|
},
|
|
9493
9810
|
async ({ title, type, content, artifact_id }) => {
|
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startMcpServer
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-AL2GL5HJ.js";
|
|
5
5
|
import "../chunk-PER4N6LS.js";
|
|
6
6
|
import "../chunk-Y2EWQ4TL.js";
|
|
7
7
|
import "../chunk-BMB7WCGF.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-MULWZLDI.js";
|
|
9
9
|
import "../chunk-ILQK4P5R.js";
|
|
10
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-BSZX63TV.js";
|
|
11
11
|
import "../chunk-3WF3X46L.js";
|
|
12
12
|
import "../chunk-UFWEANLU.js";
|
|
13
13
|
import "../chunk-HYRHI3QU.js";
|