@sideboard-ai/core 0.1.53 → 0.1.56
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 +83 -7
- package/dist/agents/cursor-runner.js +1 -1
- package/dist/{agents-KYACODJ3.js → agents-2YYWW723.js} +3 -3
- package/dist/{agents-KP7UJEHJ.js → agents-AQFEFKBL.js} +2 -2
- package/dist/{app-settings-LZP632KI.js → app-settings-2LQNRTBE.js} +3 -1
- package/dist/{app-settings-7XVDQJ7F.js → app-settings-73DI4B6T.js} +3 -1
- package/dist/{chunk-VG22SETP.js → chunk-3WJAUKIL.js} +83 -7
- package/dist/{chunk-GNML24AW.js → chunk-5DMYULLC.js} +1 -1
- package/dist/{chunk-A6HVEMIB.js → chunk-7F454EE2.js} +86 -11
- package/dist/{chunk-BZST4HMJ.js → chunk-C3J4GDW4.js} +172 -21
- package/dist/{chunk-HBJSHRY2.js → chunk-C4BCC5X5.js} +17 -1
- package/dist/{chunk-T5QQVXK3.js → chunk-DIOF73S2.js} +17 -1
- package/dist/{chunk-UEAHMGHW.js → chunk-DQJ5D42H.js} +16 -2
- package/dist/{chunk-YOWIYAVA.js → chunk-EBWEKG52.js} +36 -11
- package/dist/{chunk-XX5BB7NV.js → chunk-S42XDFKF.js} +36 -11
- package/dist/{chunk-LRLKJM3O.js → chunk-UA5NDAHL.js} +16 -2
- package/dist/{chunk-YDXQ72MD.js → chunk-V2DUTUNC.js} +1 -1
- package/dist/{coordinator-prompt-S6JZD5EF.js → coordinator-prompt-ICF36NUQ.js} +2 -1
- package/dist/{coordinator-prompt-6FXVTSFN.js → coordinator-prompt-RBKUNRPR.js} +2 -1
- package/dist/{global-workspace-EV4G2WMQ.js → global-workspace-667XLBW6.js} +3 -2
- package/dist/{global-workspace-MSX2K27Y.js → global-workspace-S7VLWPWG.js} +3 -2
- package/dist/index.cjs +298 -50
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +81 -35
- package/dist/mcp/run-stdio.cjs +292 -46
- package/dist/mcp/run-stdio.js +81 -36
- package/dist/{workspaces-AYTBR6KQ.js → workspaces-FPJJXXAE.js} +4 -3
- package/dist/{workspaces-3RQQZQRO.js → workspaces-LYIDE2VR.js} +4 -3
- package/package.json +1 -1
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
ensureConnectedBrightsyTeamTokens,
|
|
8
8
|
loadBrightsyConfig
|
|
9
9
|
} from "./chunk-H6GGDLYS.js";
|
|
10
|
-
import {
|
|
11
|
-
claudeChromeEnabled,
|
|
12
|
-
loadAppSettings,
|
|
13
|
-
resolveClaudeExecutable
|
|
14
|
-
} from "./chunk-HBJSHRY2.js";
|
|
15
10
|
import {
|
|
16
11
|
ensureAgentPath,
|
|
17
12
|
run
|
|
18
13
|
} from "./chunk-ZNM4MAN4.js";
|
|
14
|
+
import {
|
|
15
|
+
claudeChromeEnabled,
|
|
16
|
+
loadAppSettings,
|
|
17
|
+
resolveClaudeExecutable
|
|
18
|
+
} from "./chunk-C4BCC5X5.js";
|
|
19
19
|
|
|
20
20
|
// src/agents/brightsy-targets.ts
|
|
21
21
|
function encodeBrightsyTarget(type, id, accountId) {
|
|
@@ -693,6 +693,7 @@ function toCodexMcpConfigArgs(servers) {
|
|
|
693
693
|
args.push("-c", `${prefix}.env.${key}=${JSON.stringify(value)}`);
|
|
694
694
|
}
|
|
695
695
|
}
|
|
696
|
+
args.push("-c", `${prefix}.default_tools_approval_mode=${JSON.stringify("approve")}`);
|
|
696
697
|
}
|
|
697
698
|
return args;
|
|
698
699
|
}
|
|
@@ -906,7 +907,7 @@ var claudeAdapter = {
|
|
|
906
907
|
);
|
|
907
908
|
}
|
|
908
909
|
const mode = permissionMode(thread);
|
|
909
|
-
const { isOrchestratorThread } = await import("./global-workspace-
|
|
910
|
+
const { isOrchestratorThread } = await import("./global-workspace-667XLBW6.js");
|
|
910
911
|
const isOrchestrator = isOrchestratorThread(thread);
|
|
911
912
|
const injectedServers = await buildInjectedMcpServers({
|
|
912
913
|
includeSideboard: true,
|
|
@@ -1166,6 +1167,32 @@ function codexConfigHasNetworkAccess() {
|
|
|
1166
1167
|
}
|
|
1167
1168
|
return false;
|
|
1168
1169
|
}
|
|
1170
|
+
function unwrapCodexMcpResult(result) {
|
|
1171
|
+
if (result == null) return void 0;
|
|
1172
|
+
if (typeof result === "string") return result;
|
|
1173
|
+
if (typeof result !== "object") return String(result);
|
|
1174
|
+
const rec = result;
|
|
1175
|
+
if (Array.isArray(rec.content)) {
|
|
1176
|
+
const texts = [];
|
|
1177
|
+
for (const item of rec.content) {
|
|
1178
|
+
if (!item || typeof item !== "object") continue;
|
|
1179
|
+
const text = item.text;
|
|
1180
|
+
if (typeof text === "string") texts.push(text);
|
|
1181
|
+
}
|
|
1182
|
+
if (texts.length) return texts.join("\n");
|
|
1183
|
+
}
|
|
1184
|
+
try {
|
|
1185
|
+
return JSON.stringify(result);
|
|
1186
|
+
} catch {
|
|
1187
|
+
return String(result);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function asRecord(value) {
|
|
1191
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1192
|
+
return value;
|
|
1193
|
+
}
|
|
1194
|
+
return void 0;
|
|
1195
|
+
}
|
|
1169
1196
|
var codexAdapter = {
|
|
1170
1197
|
kind: "codex",
|
|
1171
1198
|
async detect() {
|
|
@@ -1256,15 +1283,49 @@ var codexAdapter = {
|
|
|
1256
1283
|
}
|
|
1257
1284
|
if (typeof obj.item === "object" && obj.item !== null) {
|
|
1258
1285
|
const item = obj.item;
|
|
1259
|
-
|
|
1286
|
+
const itemType = item.type ?? item.item_type;
|
|
1287
|
+
if (itemType === "error") {
|
|
1260
1288
|
const detail = item.message?.trim() || extractJsonErrorMessage(obj) || "Codex item error";
|
|
1261
1289
|
return { type: "stderr", data: detail };
|
|
1262
1290
|
}
|
|
1263
|
-
if (
|
|
1291
|
+
if (itemType === "agent_message" && item.text) {
|
|
1264
1292
|
return { type: "stdout", data: item.text };
|
|
1265
1293
|
}
|
|
1294
|
+
if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
|
|
1295
|
+
const name = `mcp__${item.server}__${item.tool}`;
|
|
1296
|
+
const input = asRecord(item.arguments);
|
|
1297
|
+
const events = [
|
|
1298
|
+
{ type: "tool_use", id: item.id, name, input }
|
|
1299
|
+
];
|
|
1300
|
+
const finished = type === "item.completed" || item.status === "completed" || item.status === "failed";
|
|
1301
|
+
if (finished) {
|
|
1302
|
+
const errMsg = item.error && typeof item.error.message === "string" ? item.error.message : void 0;
|
|
1303
|
+
events.push({
|
|
1304
|
+
type: "tool_result",
|
|
1305
|
+
id: item.id,
|
|
1306
|
+
content: unwrapCodexMcpResult(item.result) ?? errMsg,
|
|
1307
|
+
isError: item.status === "failed" || Boolean(errMsg)
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
return events.length === 1 ? events[0] : events;
|
|
1311
|
+
}
|
|
1312
|
+
if (itemType === "command_execution" && item.id) {
|
|
1313
|
+
const input = item.command ? { command: item.command } : void 0;
|
|
1314
|
+
const events = [
|
|
1315
|
+
{ type: "tool_use", id: item.id, name: "Bash", input }
|
|
1316
|
+
];
|
|
1317
|
+
if (type === "item.completed" || item.status === "completed" || item.status === "failed") {
|
|
1318
|
+
events.push({
|
|
1319
|
+
type: "tool_result",
|
|
1320
|
+
id: item.id,
|
|
1321
|
+
content: item.aggregated_output,
|
|
1322
|
+
isError: item.status === "failed"
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
return events.length === 1 ? events[0] : events;
|
|
1326
|
+
}
|
|
1266
1327
|
if (item.status === "failed") {
|
|
1267
|
-
const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${
|
|
1328
|
+
const detail = item.message?.trim() || extractJsonErrorMessage(item) || `Codex ${itemType ?? "item"} failed`;
|
|
1268
1329
|
return { type: "stderr", data: detail };
|
|
1269
1330
|
}
|
|
1270
1331
|
}
|
|
@@ -1355,6 +1416,75 @@ function usageFromCursor(usage) {
|
|
|
1355
1416
|
cacheWriteTokens: usage.cacheWriteTokens ? Number(usage.cacheWriteTokens) : void 0
|
|
1356
1417
|
};
|
|
1357
1418
|
}
|
|
1419
|
+
function asRecord2(value) {
|
|
1420
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1421
|
+
return value;
|
|
1422
|
+
}
|
|
1423
|
+
return void 0;
|
|
1424
|
+
}
|
|
1425
|
+
function normalizeCursorToolCall(name, args) {
|
|
1426
|
+
const raw = asRecord2(args);
|
|
1427
|
+
const toolName = typeof raw?.toolName === "string" && raw.toolName.trim() ? raw.toolName.trim() : null;
|
|
1428
|
+
const looksLikeMcp = name === "mcp" || toolName != null && (typeof raw?.providerIdentifier === "string" || asRecord2(raw?.args) != null);
|
|
1429
|
+
if (looksLikeMcp && toolName) {
|
|
1430
|
+
const providerRaw = typeof raw.providerIdentifier === "string" && raw.providerIdentifier.trim() ? raw.providerIdentifier.trim() : "sideboard";
|
|
1431
|
+
const provider = providerRaw.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1432
|
+
const nested = asRecord2(raw.args);
|
|
1433
|
+
return {
|
|
1434
|
+
name: `mcp__${provider}__${toolName}`,
|
|
1435
|
+
input: nested ?? { toolName }
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
return { name, input: raw };
|
|
1439
|
+
}
|
|
1440
|
+
function unwrapCursorToolResult(result) {
|
|
1441
|
+
if (result == null) return void 0;
|
|
1442
|
+
if (typeof result === "string") return result;
|
|
1443
|
+
const rec = asRecord2(result);
|
|
1444
|
+
if (!rec) {
|
|
1445
|
+
try {
|
|
1446
|
+
return JSON.stringify(result);
|
|
1447
|
+
} catch {
|
|
1448
|
+
return String(result);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
const collectTexts = (items) => {
|
|
1452
|
+
const texts = [];
|
|
1453
|
+
for (const item of items) {
|
|
1454
|
+
const row = asRecord2(item);
|
|
1455
|
+
if (!row) continue;
|
|
1456
|
+
if (typeof row.text === "string") {
|
|
1457
|
+
texts.push(row.text);
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
const nested = asRecord2(row.text);
|
|
1461
|
+
if (typeof nested?.text === "string") texts.push(nested.text);
|
|
1462
|
+
}
|
|
1463
|
+
return texts;
|
|
1464
|
+
};
|
|
1465
|
+
const value = asRecord2(rec.value);
|
|
1466
|
+
if (value && Array.isArray(value.content)) {
|
|
1467
|
+
const texts = collectTexts(value.content);
|
|
1468
|
+
if (texts.length) return texts.join("\n");
|
|
1469
|
+
}
|
|
1470
|
+
if (Array.isArray(rec.content)) {
|
|
1471
|
+
const texts = collectTexts(rec.content);
|
|
1472
|
+
if (texts.length) return texts.join("\n");
|
|
1473
|
+
}
|
|
1474
|
+
try {
|
|
1475
|
+
return JSON.stringify(result);
|
|
1476
|
+
} catch {
|
|
1477
|
+
return String(result);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
function cursorToolResultIsError(status, result) {
|
|
1481
|
+
if (status === "error") return true;
|
|
1482
|
+
const rec = asRecord2(result);
|
|
1483
|
+
if (!rec) return false;
|
|
1484
|
+
if (rec.status === "error") return true;
|
|
1485
|
+
const value = asRecord2(rec.value);
|
|
1486
|
+
return value?.isError === true;
|
|
1487
|
+
}
|
|
1358
1488
|
function cursorSdkMessageToEvents(msg) {
|
|
1359
1489
|
if (!msg?.type) return [];
|
|
1360
1490
|
if (msg.type === "system" && msg.agent_id) {
|
|
@@ -1369,35 +1499,42 @@ function cursorSdkMessageToEvents(msg) {
|
|
|
1369
1499
|
if (block?.type === "text" && block.text) {
|
|
1370
1500
|
out.push({ type: "stdout", data: block.text });
|
|
1371
1501
|
} else if (block?.type === "tool_use" && block.id && block.name) {
|
|
1502
|
+
const normalized = normalizeCursorToolCall(block.name, block.input);
|
|
1372
1503
|
out.push({
|
|
1373
1504
|
type: "tool_use",
|
|
1374
1505
|
id: block.id,
|
|
1375
|
-
name:
|
|
1376
|
-
input:
|
|
1506
|
+
name: normalized.name,
|
|
1507
|
+
input: normalized.input
|
|
1377
1508
|
});
|
|
1378
1509
|
}
|
|
1379
1510
|
}
|
|
1380
1511
|
return out;
|
|
1381
1512
|
}
|
|
1382
1513
|
if (msg.type === "tool_call" && msg.call_id && msg.name) {
|
|
1514
|
+
const normalized = normalizeCursorToolCall(msg.name, msg.args);
|
|
1383
1515
|
if (msg.status === "running") {
|
|
1384
1516
|
return [
|
|
1385
1517
|
{
|
|
1386
1518
|
type: "tool_use",
|
|
1387
1519
|
id: msg.call_id,
|
|
1388
|
-
name:
|
|
1389
|
-
input:
|
|
1520
|
+
name: normalized.name,
|
|
1521
|
+
input: normalized.input
|
|
1390
1522
|
}
|
|
1391
1523
|
];
|
|
1392
1524
|
}
|
|
1393
1525
|
if (msg.status === "completed" || msg.status === "error") {
|
|
1394
|
-
const content = typeof msg.result === "string" ? msg.result : msg.result != null ? JSON.stringify(msg.result) : void 0;
|
|
1395
1526
|
return [
|
|
1527
|
+
{
|
|
1528
|
+
type: "tool_use",
|
|
1529
|
+
id: msg.call_id,
|
|
1530
|
+
name: normalized.name,
|
|
1531
|
+
input: normalized.input
|
|
1532
|
+
},
|
|
1396
1533
|
{
|
|
1397
1534
|
type: "tool_result",
|
|
1398
1535
|
id: msg.call_id,
|
|
1399
|
-
content,
|
|
1400
|
-
isError: msg.status
|
|
1536
|
+
content: unwrapCursorToolResult(msg.result),
|
|
1537
|
+
isError: cursorToolResultIsError(msg.status, msg.result)
|
|
1401
1538
|
}
|
|
1402
1539
|
];
|
|
1403
1540
|
}
|
|
@@ -1733,17 +1870,31 @@ var opencodeAdapter = {
|
|
|
1733
1870
|
return { type: "stderr", data: detail };
|
|
1734
1871
|
}
|
|
1735
1872
|
const sid = typeof obj.sessionID === "string" && obj.sessionID || typeof obj.sessionId === "string" && obj.sessionId || typeof obj.session_id === "string" && obj.session_id;
|
|
1736
|
-
if (sid
|
|
1873
|
+
if (sid && (!obj.type || obj.type === "step_start" || obj.type === "session")) {
|
|
1874
|
+
return { type: "session_id", data: sid };
|
|
1875
|
+
}
|
|
1737
1876
|
if (obj.type === "text") {
|
|
1738
1877
|
const text = obj.part?.text ?? obj.text;
|
|
1739
1878
|
if (text) return { type: "stdout", data: text };
|
|
1740
1879
|
}
|
|
1741
1880
|
if (obj.type === "tool_use") {
|
|
1742
1881
|
const part = obj.part;
|
|
1743
|
-
const id = part?.id ?? obj.id ?? `tool-${Date.now()}`;
|
|
1744
|
-
const name = part?.
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1882
|
+
const id = part?.callID ?? part?.id ?? obj.id ?? `tool-${Date.now()}`;
|
|
1883
|
+
const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
|
|
1884
|
+
const state = part?.state;
|
|
1885
|
+
const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
|
|
1886
|
+
const events = [{ type: "tool_use", id, name, input }];
|
|
1887
|
+
const output = state?.output;
|
|
1888
|
+
if (output != null || state?.status === "completed" || state?.status === "error") {
|
|
1889
|
+
const content = typeof output === "string" ? output : output != null ? JSON.stringify(output) : formatUnknownDetail(state?.error) || void 0;
|
|
1890
|
+
events.push({
|
|
1891
|
+
type: "tool_result",
|
|
1892
|
+
id,
|
|
1893
|
+
content,
|
|
1894
|
+
isError: state?.status === "error" || state?.status === "failed"
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
return events.length === 1 ? events[0] : events;
|
|
1747
1898
|
}
|
|
1748
1899
|
if (obj.type === "tool_result") {
|
|
1749
1900
|
const part = obj.part;
|
|
@@ -323,6 +323,16 @@ function resolveThreadDefaults(settings = loadAppSettings()) {
|
|
|
323
323
|
fast: getDefaultFast(settings)
|
|
324
324
|
};
|
|
325
325
|
}
|
|
326
|
+
function resolveNewThreadOptions(overrides = {}, settings = loadAppSettings()) {
|
|
327
|
+
const defaults = resolveThreadDefaults(settings);
|
|
328
|
+
const effort = overrides.effort === void 0 || overrides.effort === null ? defaults.effort : normalizeThinkingEffort(overrides.effort) ?? defaults.effort;
|
|
329
|
+
return {
|
|
330
|
+
agent: overrides.agent ?? defaults.agent,
|
|
331
|
+
model: overrides.model === void 0 ? defaults.model : overrides.model?.trim() || null,
|
|
332
|
+
effort,
|
|
333
|
+
fast: overrides.fast === void 0 || overrides.fast === null ? defaults.fast : Boolean(overrides.fast)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
326
336
|
function isLinearConnected(settings = loadAppSettings()) {
|
|
327
337
|
return Boolean(settings.integrations.linearApiKey?.trim());
|
|
328
338
|
}
|
|
@@ -342,7 +352,12 @@ function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
|
|
|
342
352
|
return Boolean(settings.brightsy.cloudConnectEnabled);
|
|
343
353
|
}
|
|
344
354
|
function brightsyCloudConnectAgent(settings = loadAppSettings()) {
|
|
345
|
-
|
|
355
|
+
const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
|
|
356
|
+
const preferred = getDefaultAgent(settings);
|
|
357
|
+
if (CLOUD_CONNECT_AGENTS.has(preferred)) {
|
|
358
|
+
return preferred;
|
|
359
|
+
}
|
|
360
|
+
return fallback;
|
|
346
361
|
}
|
|
347
362
|
function updateAdvancedSettings(patch) {
|
|
348
363
|
const current = loadAppSettings();
|
|
@@ -470,6 +485,7 @@ export {
|
|
|
470
485
|
getDefaultEffort,
|
|
471
486
|
getDefaultFast,
|
|
472
487
|
resolveThreadDefaults,
|
|
488
|
+
resolveNewThreadOptions,
|
|
473
489
|
isLinearConnected,
|
|
474
490
|
getIssueSource,
|
|
475
491
|
resolveEffectiveIssueSource,
|
|
@@ -321,6 +321,16 @@ function resolveThreadDefaults(settings = loadAppSettings()) {
|
|
|
321
321
|
fast: getDefaultFast(settings)
|
|
322
322
|
};
|
|
323
323
|
}
|
|
324
|
+
function resolveNewThreadOptions(overrides = {}, settings = loadAppSettings()) {
|
|
325
|
+
const defaults = resolveThreadDefaults(settings);
|
|
326
|
+
const effort = overrides.effort === void 0 || overrides.effort === null ? defaults.effort : normalizeThinkingEffort(overrides.effort) ?? defaults.effort;
|
|
327
|
+
return {
|
|
328
|
+
agent: overrides.agent ?? defaults.agent,
|
|
329
|
+
model: overrides.model === void 0 ? defaults.model : overrides.model?.trim() || null,
|
|
330
|
+
effort,
|
|
331
|
+
fast: overrides.fast === void 0 || overrides.fast === null ? defaults.fast : Boolean(overrides.fast)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
324
334
|
function isLinearConnected(settings = loadAppSettings()) {
|
|
325
335
|
return Boolean(settings.integrations.linearApiKey?.trim());
|
|
326
336
|
}
|
|
@@ -340,7 +350,12 @@ function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
|
|
|
340
350
|
return Boolean(settings.brightsy.cloudConnectEnabled);
|
|
341
351
|
}
|
|
342
352
|
function brightsyCloudConnectAgent(settings = loadAppSettings()) {
|
|
343
|
-
|
|
353
|
+
const fallback = settings.brightsy.cloudConnectAgent && CLOUD_CONNECT_AGENTS.has(settings.brightsy.cloudConnectAgent) ? settings.brightsy.cloudConnectAgent : "claude";
|
|
354
|
+
const preferred = getDefaultAgent(settings);
|
|
355
|
+
if (CLOUD_CONNECT_AGENTS.has(preferred)) {
|
|
356
|
+
return preferred;
|
|
357
|
+
}
|
|
358
|
+
return fallback;
|
|
344
359
|
}
|
|
345
360
|
function updateAdvancedSettings(patch) {
|
|
346
361
|
const current = loadAppSettings();
|
|
@@ -468,6 +483,7 @@ export {
|
|
|
468
483
|
getDefaultEffort,
|
|
469
484
|
getDefaultFast,
|
|
470
485
|
resolveThreadDefaults,
|
|
486
|
+
resolveNewThreadOptions,
|
|
471
487
|
isLinearConnected,
|
|
472
488
|
getIssueSource,
|
|
473
489
|
resolveEffectiveIssueSource,
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import {
|
|
4
4
|
resolveGithubRepoSlug
|
|
5
5
|
} from "./chunk-XOU6HNQJ.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveThreadDefaults
|
|
8
|
+
} from "./chunk-C4BCC5X5.js";
|
|
6
9
|
import {
|
|
7
10
|
globalAgentCwd,
|
|
8
11
|
sideboardReposDir
|
|
@@ -45,12 +48,12 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
45
48
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
46
49
|
"- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
|
|
47
50
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
48
|
-
"- list_models \u2014 only when you need a specific model (rare); otherwise
|
|
51
|
+
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
49
52
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
50
53
|
"Workspaces:",
|
|
51
54
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
52
55
|
"Worktree threads (chats):",
|
|
53
|
-
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId",
|
|
56
|
+
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort)",
|
|
54
57
|
"- 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.",
|
|
55
58
|
"- 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.",
|
|
56
59
|
"- 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",
|
|
@@ -68,6 +71,11 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
68
71
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
69
72
|
"Bash / Read / etc: allowed for (1) inspecting target worktrees / registered repo paths from MCP, and (2) greenfield setup under ~/sideboard/repos (git clone, gh repo create, git init+remote). Never git init/clone *inside* this synthetic home cwd \u2014 emptiness here is expected, not a bug."
|
|
70
73
|
].join("\n");
|
|
74
|
+
function accountDefaultsPlaybookLine() {
|
|
75
|
+
const d = resolveThreadDefaults();
|
|
76
|
+
const model = d.model?.trim() || "Auto";
|
|
77
|
+
return `- Account defaults for create_thread (omit agent/model to use these): agent=${d.agent}, model=${model}, effort=${d.effort}`;
|
|
78
|
+
}
|
|
71
79
|
function coordinatorTurnReminder(opts) {
|
|
72
80
|
const goal = opts.goal?.trim();
|
|
73
81
|
return [
|
|
@@ -77,6 +85,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
77
85
|
"- Registered workspaces / child threads are the fleet you manage via Sideboard MCP.",
|
|
78
86
|
`- Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
|
|
79
87
|
goal ? `- Goal / title: ${goal}` : null,
|
|
88
|
+
accountDefaultsPlaybookLine(),
|
|
80
89
|
`- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
|
|
81
90
|
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
82
91
|
"- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR.",
|
|
@@ -99,9 +108,12 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
99
108
|
"",
|
|
100
109
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
101
110
|
"",
|
|
111
|
+
accountDefaultsPlaybookLine(),
|
|
112
|
+
"",
|
|
102
113
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
103
114
|
"",
|
|
104
115
|
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered) and `parentThreadId` for children.",
|
|
116
|
+
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
105
117
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
106
118
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
107
119
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
@@ -127,8 +139,10 @@ function coordinatorSystemPrompt(opts) {
|
|
|
127
139
|
"You operate across ALL registered workspaces below.",
|
|
128
140
|
"You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
|
|
129
141
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
142
|
+
accountDefaultsPlaybookLine(),
|
|
130
143
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
131
144
|
"When creating threads, pass the correct repoPath for the target workspace and parentThreadId for children.",
|
|
145
|
+
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
132
146
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Never target upstream. Never open PRs from the orchestrator.",
|
|
133
147
|
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
134
148
|
`Goal: ${opts.goal}`,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-S2LL5HA4.js";
|
|
6
6
|
import {
|
|
7
7
|
ensureGlobalCoordinatorCwd
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DQJ5D42H.js";
|
|
9
9
|
import {
|
|
10
10
|
allocateTeamName,
|
|
11
11
|
takenSlugsFromThread,
|
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
updateThread,
|
|
18
18
|
writeThread
|
|
19
19
|
} from "./chunk-HLEX5AQ6.js";
|
|
20
|
+
import {
|
|
21
|
+
resolveNewThreadOptions,
|
|
22
|
+
resolveThreadDefaults
|
|
23
|
+
} from "./chunk-C4BCC5X5.js";
|
|
20
24
|
import {
|
|
21
25
|
globalAgentCwd
|
|
22
26
|
} from "./chunk-7MV3RXSC.js";
|
|
@@ -107,7 +111,13 @@ function createGlobalChat(opts) {
|
|
|
107
111
|
const explicit = opts.title?.trim();
|
|
108
112
|
const title = explicit && explicit !== CLOUD_ORCHESTRATOR_GOAL ? explicit : allocateTeamName(takenTeamSlugsForOrchestration()).name;
|
|
109
113
|
const sourceRef = opts.sourceRef?.trim() || (isCloud ? CLOUD_ORCHESTRATOR_GOAL : title);
|
|
110
|
-
const
|
|
114
|
+
const resolved = resolveNewThreadOptions({
|
|
115
|
+
agent: opts.agent,
|
|
116
|
+
model: opts.model,
|
|
117
|
+
effort: opts.effort,
|
|
118
|
+
fast: opts.fast
|
|
119
|
+
});
|
|
120
|
+
const agent = assertOrchestratorCapableAgent(resolved.agent);
|
|
111
121
|
const thread = createEmptyThread({
|
|
112
122
|
title,
|
|
113
123
|
// Stick nicknames the same way chat tabs do (avoid later sync overwrites).
|
|
@@ -119,9 +129,9 @@ function createGlobalChat(opts) {
|
|
|
119
129
|
repoPath: GLOBAL_WORKSPACE_ID,
|
|
120
130
|
agent,
|
|
121
131
|
autonomy: opts.autonomy ?? "default",
|
|
122
|
-
model:
|
|
123
|
-
effort:
|
|
124
|
-
fast:
|
|
132
|
+
model: resolved.model,
|
|
133
|
+
effort: resolved.effort,
|
|
134
|
+
fast: resolved.fast,
|
|
125
135
|
planMode: Boolean(opts.planMode),
|
|
126
136
|
attachments: opts.attachments ?? [],
|
|
127
137
|
parentThreadId: opts.parentThreadId ?? null,
|
|
@@ -154,20 +164,35 @@ function findCloudCoordinator() {
|
|
|
154
164
|
);
|
|
155
165
|
}
|
|
156
166
|
function ensureCloudCoordinator(agent) {
|
|
167
|
+
const defaults = resolveThreadDefaults();
|
|
168
|
+
const desired = assertOrchestratorCapableAgent(agent);
|
|
157
169
|
const existing = findCloudCoordinator();
|
|
158
170
|
if (existing) {
|
|
171
|
+
const patch = {};
|
|
159
172
|
if (existing.repoPath !== GLOBAL_WORKSPACE_ID) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
patch.repoPath = GLOBAL_WORKSPACE_ID;
|
|
174
|
+
patch.worktreePath = globalAgentCwd();
|
|
175
|
+
patch.branchName = "global";
|
|
176
|
+
}
|
|
177
|
+
const hasAgentTurns = existing.messages.some((m) => m.role === "agent");
|
|
178
|
+
const canRetarget = !existing.sessionId && !hasAgentTurns && existing.status !== "running" && existing.status !== "queued";
|
|
179
|
+
if (canRetarget) {
|
|
180
|
+
if (existing.agent !== desired) patch.agent = desired;
|
|
181
|
+
if (existing.model !== defaults.model) patch.model = defaults.model;
|
|
182
|
+
if (existing.effort !== defaults.effort) patch.effort = defaults.effort;
|
|
183
|
+
if (existing.fast !== defaults.fast) patch.fast = defaults.fast;
|
|
184
|
+
}
|
|
185
|
+
if (Object.keys(patch).length > 0) {
|
|
186
|
+
return updateThread(existing.id, patch);
|
|
165
187
|
}
|
|
166
188
|
return existing;
|
|
167
189
|
}
|
|
168
190
|
const created = createGlobalChat({
|
|
169
191
|
sourceRef: CLOUD_ORCHESTRATOR_GOAL,
|
|
170
|
-
agent
|
|
192
|
+
agent: desired,
|
|
193
|
+
model: defaults.model,
|
|
194
|
+
effort: defaults.effort,
|
|
195
|
+
fast: defaults.fast
|
|
171
196
|
});
|
|
172
197
|
const all = listThreads({ includeArchived: true }).filter(
|
|
173
198
|
(t) => t.status !== "archived" && isCloudCoordinatorThread(t)
|
|
@@ -3,7 +3,11 @@ import {
|
|
|
3
3
|
} from "./chunk-GI7E5733.js";
|
|
4
4
|
import {
|
|
5
5
|
ensureGlobalCoordinatorCwd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UA5NDAHL.js";
|
|
7
|
+
import {
|
|
8
|
+
resolveNewThreadOptions,
|
|
9
|
+
resolveThreadDefaults
|
|
10
|
+
} from "./chunk-DIOF73S2.js";
|
|
7
11
|
import {
|
|
8
12
|
allocateTeamName,
|
|
9
13
|
takenSlugsFromThread,
|
|
@@ -113,7 +117,13 @@ function createGlobalChat(opts) {
|
|
|
113
117
|
const explicit = opts.title?.trim();
|
|
114
118
|
const title = explicit && explicit !== CLOUD_ORCHESTRATOR_GOAL ? explicit : allocateTeamName(takenTeamSlugsForOrchestration()).name;
|
|
115
119
|
const sourceRef = opts.sourceRef?.trim() || (isCloud ? CLOUD_ORCHESTRATOR_GOAL : title);
|
|
116
|
-
const
|
|
120
|
+
const resolved = resolveNewThreadOptions({
|
|
121
|
+
agent: opts.agent,
|
|
122
|
+
model: opts.model,
|
|
123
|
+
effort: opts.effort,
|
|
124
|
+
fast: opts.fast
|
|
125
|
+
});
|
|
126
|
+
const agent = assertOrchestratorCapableAgent(resolved.agent);
|
|
117
127
|
const thread = createEmptyThread({
|
|
118
128
|
title,
|
|
119
129
|
// Stick nicknames the same way chat tabs do (avoid later sync overwrites).
|
|
@@ -125,9 +135,9 @@ function createGlobalChat(opts) {
|
|
|
125
135
|
repoPath: GLOBAL_WORKSPACE_ID,
|
|
126
136
|
agent,
|
|
127
137
|
autonomy: opts.autonomy ?? "default",
|
|
128
|
-
model:
|
|
129
|
-
effort:
|
|
130
|
-
fast:
|
|
138
|
+
model: resolved.model,
|
|
139
|
+
effort: resolved.effort,
|
|
140
|
+
fast: resolved.fast,
|
|
131
141
|
planMode: Boolean(opts.planMode),
|
|
132
142
|
attachments: opts.attachments ?? [],
|
|
133
143
|
parentThreadId: opts.parentThreadId ?? null,
|
|
@@ -160,20 +170,35 @@ function findCloudCoordinator() {
|
|
|
160
170
|
);
|
|
161
171
|
}
|
|
162
172
|
function ensureCloudCoordinator(agent) {
|
|
173
|
+
const defaults = resolveThreadDefaults();
|
|
174
|
+
const desired = assertOrchestratorCapableAgent(agent);
|
|
163
175
|
const existing = findCloudCoordinator();
|
|
164
176
|
if (existing) {
|
|
177
|
+
const patch = {};
|
|
165
178
|
if (existing.repoPath !== GLOBAL_WORKSPACE_ID) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
patch.repoPath = GLOBAL_WORKSPACE_ID;
|
|
180
|
+
patch.worktreePath = globalAgentCwd();
|
|
181
|
+
patch.branchName = "global";
|
|
182
|
+
}
|
|
183
|
+
const hasAgentTurns = existing.messages.some((m) => m.role === "agent");
|
|
184
|
+
const canRetarget = !existing.sessionId && !hasAgentTurns && existing.status !== "running" && existing.status !== "queued";
|
|
185
|
+
if (canRetarget) {
|
|
186
|
+
if (existing.agent !== desired) patch.agent = desired;
|
|
187
|
+
if (existing.model !== defaults.model) patch.model = defaults.model;
|
|
188
|
+
if (existing.effort !== defaults.effort) patch.effort = defaults.effort;
|
|
189
|
+
if (existing.fast !== defaults.fast) patch.fast = defaults.fast;
|
|
190
|
+
}
|
|
191
|
+
if (Object.keys(patch).length > 0) {
|
|
192
|
+
return updateThread(existing.id, patch);
|
|
171
193
|
}
|
|
172
194
|
return existing;
|
|
173
195
|
}
|
|
174
196
|
const created = createGlobalChat({
|
|
175
197
|
sourceRef: CLOUD_ORCHESTRATOR_GOAL,
|
|
176
|
-
agent
|
|
198
|
+
agent: desired,
|
|
199
|
+
model: defaults.model,
|
|
200
|
+
effort: defaults.effort,
|
|
201
|
+
fast: defaults.fast
|
|
177
202
|
});
|
|
178
203
|
const all = listThreads({ includeArchived: true }).filter(
|
|
179
204
|
(t) => t.status !== "archived" && isCloudCoordinatorThread(t)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveThreadDefaults
|
|
3
|
+
} from "./chunk-DIOF73S2.js";
|
|
1
4
|
import {
|
|
2
5
|
resolveGithubRepoSlug
|
|
3
6
|
} from "./chunk-DZFH2KLT.js";
|
|
@@ -43,12 +46,12 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
43
46
|
"- list_workspaces \u2014 registered repos (path + github slug when known)",
|
|
44
47
|
"- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear API or GitHub Issues)",
|
|
45
48
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
46
|
-
"- list_models \u2014 only when you need a specific model (rare); otherwise
|
|
49
|
+
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
47
50
|
"- list_threads / get_thread \u2014 fleet status (what is going on)",
|
|
48
51
|
"Workspaces:",
|
|
49
52
|
"- add_workspace / remove_workspace \u2014 register or unregister a git repo",
|
|
50
53
|
"Worktree threads (chats):",
|
|
51
|
-
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId",
|
|
54
|
+
"- create_thread \u2014 create a worktree + chat from branch | pr | ticket; pass repoPath + parentThreadId; omit agent and model to use Sideboard Account defaults (Settings \u2192 Default agent, model & effort)",
|
|
52
55
|
"- 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.",
|
|
53
56
|
"- 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.",
|
|
54
57
|
"- 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",
|
|
@@ -66,6 +69,11 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
66
69
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
67
70
|
"Bash / Read / etc: allowed for (1) inspecting target worktrees / registered repo paths from MCP, and (2) greenfield setup under ~/sideboard/repos (git clone, gh repo create, git init+remote). Never git init/clone *inside* this synthetic home cwd \u2014 emptiness here is expected, not a bug."
|
|
68
71
|
].join("\n");
|
|
72
|
+
function accountDefaultsPlaybookLine() {
|
|
73
|
+
const d = resolveThreadDefaults();
|
|
74
|
+
const model = d.model?.trim() || "Auto";
|
|
75
|
+
return `- Account defaults for create_thread (omit agent/model to use these): agent=${d.agent}, model=${model}, effort=${d.effort}`;
|
|
76
|
+
}
|
|
69
77
|
function coordinatorTurnReminder(opts) {
|
|
70
78
|
const goal = opts.goal?.trim();
|
|
71
79
|
return [
|
|
@@ -75,6 +83,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
75
83
|
"- Registered workspaces / child threads are the fleet you manage via Sideboard MCP.",
|
|
76
84
|
`- Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
|
|
77
85
|
goal ? `- Goal / title: ${goal}` : null,
|
|
86
|
+
accountDefaultsPlaybookLine(),
|
|
78
87
|
`- For "what's going on": call list_threads (and list_workspaces if needed). Summarize fleet status \u2014 do not ls/git-status this synthetic home.`,
|
|
79
88
|
"- Existing repo: create_thread on a repoPath \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
80
89
|
"- New repo: Bash (clone or gh repo create under ~/sideboard/repos/<name>) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR.",
|
|
@@ -97,9 +106,12 @@ function ensureGlobalCoordinatorCwd() {
|
|
|
97
106
|
"",
|
|
98
107
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
99
108
|
"",
|
|
109
|
+
accountDefaultsPlaybookLine(),
|
|
110
|
+
"",
|
|
100
111
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
101
112
|
"",
|
|
102
113
|
"When creating threads, pass `repoPath` from `list_workspaces` (or the path you just registered) and `parentThreadId` for children.",
|
|
114
|
+
"Omit `agent` / `model` on `create_thread` unless you have a reason to override Account defaults.",
|
|
103
115
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn.",
|
|
104
116
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
105
117
|
"Always ask worktree agents to open draft PRs (`send_to_thread` + `gh pr create --draft -R <origin>`); never open PRs from the orchestrator."
|
|
@@ -125,8 +137,10 @@ function coordinatorSystemPrompt(opts) {
|
|
|
125
137
|
"You operate across ALL registered workspaces below.",
|
|
126
138
|
"You have no project git home \u2014 this process cwd is synthetic and empty on purpose.",
|
|
127
139
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
140
|
+
accountDefaultsPlaybookLine(),
|
|
128
141
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
129
142
|
"When creating threads, pass the correct repoPath for the target workspace and parentThreadId for children.",
|
|
143
|
+
"Omit agent/model on create_thread unless you need to override Account defaults.",
|
|
130
144
|
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Never target upstream. Never open PRs from the orchestrator.",
|
|
131
145
|
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR via worktree agent.",
|
|
132
146
|
`Goal: ${opts.goal}`,
|