@dreb/coding-agent 2.45.4 → 2.47.0
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/README.md +10 -4
- package/dist/core/agent-session.d.ts +10 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +94 -23
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/context-buffer.d.ts +2 -0
- package/dist/core/context-buffer.d.ts.map +1 -1
- package/dist/core/context-buffer.js +8 -0
- package/dist/core/context-buffer.js.map +1 -1
- package/dist/core/dispatch-arbiter.d.ts +94 -0
- package/dist/core/dispatch-arbiter.d.ts.map +1 -0
- package/dist/core/dispatch-arbiter.js +316 -0
- package/dist/core/dispatch-arbiter.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +7 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +10 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +35 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/git-repo-state.d.ts +2 -0
- package/dist/core/git-repo-state.d.ts.map +1 -1
- package/dist/core/git-repo-state.js +19 -0
- package/dist/core/git-repo-state.js.map +1 -1
- package/dist/core/model-routing-guide.d.ts +15 -0
- package/dist/core/model-routing-guide.d.ts.map +1 -0
- package/dist/core/model-routing-guide.js +186 -0
- package/dist/core/model-routing-guide.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +38 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +18 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +33 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/thinking.d.ts +14 -0
- package/dist/core/thinking.d.ts.map +1 -1
- package/dist/core/thinking.js +35 -2
- package/dist/core/thinking.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +45 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +210 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/index.d.ts +18 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +53 -6
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +343 -53
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/modes/interactive/components/ask-user.d.ts +59 -0
- package/dist/modes/interactive/components/ask-user.d.ts.map +1 -0
- package/dist/modes/interactive/components/ask-user.js +240 -0
- package/dist/modes/interactive/components/ask-user.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +99 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +167 -6
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +18 -3
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +177 -59
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +32 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/agent-models.md +33 -1
- package/docs/dashboard.md +9 -3
- package/docs/extensions.md +33 -3
- package/docs/json.md +31 -5
- package/docs/rpc.md +105 -15
- package/docs/session.md +8 -2
- package/docs/settings.md +33 -1
- package/docs/skills.md +38 -0
- package/package.json +1 -1
- package/skills/model-routing-guide/SKILL.md +182 -0
|
@@ -14,17 +14,19 @@ import { parseSkillBlock } from "../../core/agent-session.js";
|
|
|
14
14
|
import { BuddyManager, checkOllama } from "../../core/buddy/buddy-manager.js";
|
|
15
15
|
import { Rarity, Stat } from "../../core/buddy/buddy-types.js";
|
|
16
16
|
import { BuddyController } from "../../core/buddy/index.js";
|
|
17
|
+
import { formatDispatchArbitrationRecord } from "../../core/dispatch-arbiter.js";
|
|
17
18
|
import { acquireDreamLock, buildDreamPrompt, cleanupDreamTmpDirs, parseDreamCommand, performDreamBackup, pruneOldBackups, resolveDreamContext, validateArchivePath, validateMemoryLinks, } from "../../core/dream.js";
|
|
18
19
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
19
20
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
20
21
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
21
22
|
import { findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
|
|
23
|
+
import { loadAndValidateModelRoutingGuide } from "../../core/model-routing-guide.js";
|
|
22
24
|
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
23
25
|
import { SessionManager } from "../../core/session-manager.js";
|
|
24
26
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
25
27
|
import { restoreStderr, takeOverStderr } from "../../core/stderr-guard.js";
|
|
26
28
|
import { TabTitleGenerator } from "../../core/tab-title.js";
|
|
27
|
-
import { resolveThinkingDisplay } from "../../core/thinking.js";
|
|
29
|
+
import { resolveThinkingDisplay, validateThinkingLevelForModel } from "../../core/thinking.js";
|
|
28
30
|
import { resolveToCwd } from "../../core/tools/path-utils.js";
|
|
29
31
|
import { abortBackgroundAgents, discoverAgentTypes, getRunningBackgroundAgents } from "../../core/tools/subagent.js";
|
|
30
32
|
import { copyToClipboard } from "../../utils/clipboard.js";
|
|
@@ -33,6 +35,7 @@ import { parseGitUrl } from "../../utils/git.js";
|
|
|
33
35
|
import { extractCopyableText, extractThinkingText, getMessagePreview, getMessageRoleLabel, toSingleLinePreview, } from "../../utils/message-text.js";
|
|
34
36
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
35
37
|
import { ArminComponent } from "./components/armin.js";
|
|
38
|
+
import { AskUserComponent } from "./components/ask-user.js";
|
|
36
39
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
37
40
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
38
41
|
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
@@ -134,6 +137,8 @@ export class InteractiveMode {
|
|
|
134
137
|
extensionSelector = undefined;
|
|
135
138
|
extensionInput = undefined;
|
|
136
139
|
extensionEditor = undefined;
|
|
140
|
+
extensionAsk = undefined;
|
|
141
|
+
extensionDialogQueue = Promise.resolve();
|
|
137
142
|
extensionTerminalInputUnsubscribers = new Set();
|
|
138
143
|
// Extension widgets (components rendered above/below the editor)
|
|
139
144
|
extensionWidgetsAbove = new Map();
|
|
@@ -1182,6 +1187,9 @@ export class InteractiveMode {
|
|
|
1182
1187
|
if (this.extensionEditor) {
|
|
1183
1188
|
this.hideExtensionEditor();
|
|
1184
1189
|
}
|
|
1190
|
+
if (this.extensionAsk) {
|
|
1191
|
+
this.hideExtensionAsk();
|
|
1192
|
+
}
|
|
1185
1193
|
this.ui.hideOverlay();
|
|
1186
1194
|
this.clearExtensionTerminalInputListeners();
|
|
1187
1195
|
this.setExtensionFooter(undefined);
|
|
@@ -1404,9 +1412,22 @@ export class InteractiveMode {
|
|
|
1404
1412
|
*/
|
|
1405
1413
|
createExtensionUIContext() {
|
|
1406
1414
|
return {
|
|
1407
|
-
select: (title, options, opts) => this.showExtensionSelector(title, options, opts),
|
|
1408
|
-
|
|
1409
|
-
|
|
1415
|
+
select: (title, options, opts) => this.enqueueExtensionDialog(() => this.showExtensionSelector(title, options, opts), {
|
|
1416
|
+
signal: opts?.signal,
|
|
1417
|
+
abortedValue: undefined,
|
|
1418
|
+
}),
|
|
1419
|
+
confirm: (title, message, opts) => this.enqueueExtensionDialog(() => this.showExtensionConfirm(title, message, opts), {
|
|
1420
|
+
signal: opts?.signal,
|
|
1421
|
+
abortedValue: false,
|
|
1422
|
+
}),
|
|
1423
|
+
input: (title, placeholder, opts) => this.enqueueExtensionDialog(() => this.showExtensionInput(title, placeholder, opts), {
|
|
1424
|
+
signal: opts?.signal,
|
|
1425
|
+
abortedValue: undefined,
|
|
1426
|
+
}),
|
|
1427
|
+
ask: (request, opts) => this.enqueueExtensionDialog(() => this.showExtensionAsk(request, opts), {
|
|
1428
|
+
signal: opts?.signal,
|
|
1429
|
+
abortedValue: undefined,
|
|
1430
|
+
}),
|
|
1410
1431
|
notify: (message, type) => this.showExtensionNotify(message, type),
|
|
1411
1432
|
onTerminalInput: (handler) => this.addExtensionTerminalInputListener(handler),
|
|
1412
1433
|
setStatus: (key, text) => this.setExtensionStatus(key, text),
|
|
@@ -1415,11 +1436,11 @@ export class InteractiveMode {
|
|
|
1415
1436
|
setFooter: (factory) => this.setExtensionFooter(factory),
|
|
1416
1437
|
setHeader: (factory) => this.setExtensionHeader(factory),
|
|
1417
1438
|
setTitle: (title) => this.ui.terminal.setTitle(title),
|
|
1418
|
-
custom: (factory, options) => this.showExtensionCustom(factory, options),
|
|
1439
|
+
custom: (factory, options) => this.enqueueExtensionDialog(() => this.showExtensionCustom(factory, options)),
|
|
1419
1440
|
pasteToEditor: (text) => this.editor.handleInput(`\x1b[200~${text}\x1b[201~`),
|
|
1420
1441
|
setEditorText: (text) => this.editor.setText(text),
|
|
1421
1442
|
getEditorText: () => this.editor.getExpandedText?.() ?? this.editor.getText(),
|
|
1422
|
-
editor: (title, prefill) => this.showExtensionEditor(title, prefill),
|
|
1443
|
+
editor: (title, prefill) => this.enqueueExtensionDialog(() => this.showExtensionEditor(title, prefill)),
|
|
1423
1444
|
setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
|
|
1424
1445
|
get theme() {
|
|
1425
1446
|
return theme;
|
|
@@ -1445,6 +1466,32 @@ export class InteractiveMode {
|
|
|
1445
1466
|
setToolsExpanded: (expanded) => this.setToolsExpanded(expanded),
|
|
1446
1467
|
};
|
|
1447
1468
|
}
|
|
1469
|
+
/** Serialize every blocking extension dialog that can take over keyboard focus. */
|
|
1470
|
+
enqueueExtensionDialog(open, opts) {
|
|
1471
|
+
if (!opts?.signal) {
|
|
1472
|
+
const result = this.extensionDialogQueue.then(open, open);
|
|
1473
|
+
this.extensionDialogQueue = result.then(() => undefined, () => undefined);
|
|
1474
|
+
return result;
|
|
1475
|
+
}
|
|
1476
|
+
const { signal, abortedValue } = opts;
|
|
1477
|
+
if (signal.aborted)
|
|
1478
|
+
return Promise.resolve(abortedValue);
|
|
1479
|
+
// Settle the caller as soon as a queued dialog aborts, even if an earlier
|
|
1480
|
+
// unbounded dialog still owns the UI. Its queue slot remains as a no-op so
|
|
1481
|
+
// later dialogs keep their FIFO order.
|
|
1482
|
+
let onQueuedAbort;
|
|
1483
|
+
const aborted = new Promise((resolve) => {
|
|
1484
|
+
onQueuedAbort = () => resolve(abortedValue);
|
|
1485
|
+
signal.addEventListener("abort", onQueuedAbort, { once: true });
|
|
1486
|
+
});
|
|
1487
|
+
const run = () => {
|
|
1488
|
+
signal.removeEventListener("abort", onQueuedAbort);
|
|
1489
|
+
return signal.aborted ? Promise.resolve(abortedValue) : open();
|
|
1490
|
+
};
|
|
1491
|
+
const queued = this.extensionDialogQueue.then(run, run);
|
|
1492
|
+
this.extensionDialogQueue = queued.then(() => undefined, () => undefined);
|
|
1493
|
+
return Promise.race([queued, aborted]).finally(() => signal.removeEventListener("abort", onQueuedAbort));
|
|
1494
|
+
}
|
|
1448
1495
|
/**
|
|
1449
1496
|
* Show a selector for extensions.
|
|
1450
1497
|
*/
|
|
@@ -1526,6 +1573,48 @@ export class InteractiveMode {
|
|
|
1526
1573
|
this.extensionInput = undefined;
|
|
1527
1574
|
this.restoreEditorComponent();
|
|
1528
1575
|
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Show a rich ask_user question for extensions/built-in tools.
|
|
1578
|
+
*/
|
|
1579
|
+
showExtensionAsk(request, opts) {
|
|
1580
|
+
return new Promise((resolve) => {
|
|
1581
|
+
if (opts?.signal?.aborted) {
|
|
1582
|
+
resolve(undefined);
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const onAbort = () => {
|
|
1586
|
+
this.hideExtensionAsk();
|
|
1587
|
+
resolve(undefined);
|
|
1588
|
+
};
|
|
1589
|
+
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1590
|
+
this.extensionAsk = new AskUserComponent(request, (result) => {
|
|
1591
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
1592
|
+
this.hideExtensionAsk();
|
|
1593
|
+
resolve(result);
|
|
1594
|
+
}, () => {
|
|
1595
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
1596
|
+
this.hideExtensionAsk();
|
|
1597
|
+
// The question-level escape action means stop the whole turn, not
|
|
1598
|
+
// continue without an answer. Mirror the default editor's Escape
|
|
1599
|
+
// behavior, including background-agent and queued-message cleanup.
|
|
1600
|
+
this.cancelBackgroundAgents();
|
|
1601
|
+
this.restoreQueuedMessagesToEditor({ abort: true });
|
|
1602
|
+
resolve(undefined);
|
|
1603
|
+
}, { tui: this.ui, timeout: opts?.timeout });
|
|
1604
|
+
this.editorContainer.clear();
|
|
1605
|
+
this.editorContainer.addChild(this.extensionAsk);
|
|
1606
|
+
this.ui.setFocus(this.extensionAsk);
|
|
1607
|
+
this.ui.requestRender();
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Hide the ask_user question.
|
|
1612
|
+
*/
|
|
1613
|
+
hideExtensionAsk() {
|
|
1614
|
+
this.extensionAsk?.dispose();
|
|
1615
|
+
this.extensionAsk = undefined;
|
|
1616
|
+
this.restoreEditorComponent();
|
|
1617
|
+
}
|
|
1529
1618
|
/**
|
|
1530
1619
|
* Show a multi-line editor for extensions (with Ctrl+G support).
|
|
1531
1620
|
*/
|
|
@@ -2295,6 +2384,15 @@ export class InteractiveMode {
|
|
|
2295
2384
|
this.showWarning(`Response truncated, retrying with larger token budget (${event.attempt}/${event.maxAttempts})…`);
|
|
2296
2385
|
break;
|
|
2297
2386
|
}
|
|
2387
|
+
case "subagent_arbitration": {
|
|
2388
|
+
const message = formatDispatchArbitrationRecord(event);
|
|
2389
|
+
if (event.status === "failure")
|
|
2390
|
+
this.showWarning(message);
|
|
2391
|
+
else
|
|
2392
|
+
this.showStatus(message);
|
|
2393
|
+
this.updateBackgroundAgentStatus();
|
|
2394
|
+
break;
|
|
2395
|
+
}
|
|
2298
2396
|
case "background_agent_start":
|
|
2299
2397
|
case "background_agent_end": {
|
|
2300
2398
|
this.updateBackgroundAgentStatus();
|
|
@@ -3098,6 +3196,67 @@ export class InteractiveMode {
|
|
|
3098
3196
|
this.ui.setFocus(focus);
|
|
3099
3197
|
this.ui.requestRender();
|
|
3100
3198
|
}
|
|
3199
|
+
async handleSubagentArbiterSettingsChange(settings) {
|
|
3200
|
+
if (this.settingsManager.hasGlobalSettingsLoadError()) {
|
|
3201
|
+
this.showError("Cannot write Dispatch Arbiter settings: the global settings file failed to load. Fix or remove the corrupt settings.json first.");
|
|
3202
|
+
return false;
|
|
3203
|
+
}
|
|
3204
|
+
if (settings.enabled) {
|
|
3205
|
+
if (!settings.model) {
|
|
3206
|
+
this.showError("Choose an exact Dispatch Arbiter model before enabling it.");
|
|
3207
|
+
return false;
|
|
3208
|
+
}
|
|
3209
|
+
const slash = settings.model.indexOf("/");
|
|
3210
|
+
const arbiterModel = slash > 0
|
|
3211
|
+
? this.session.modelRegistry.find(settings.model.slice(0, slash), settings.model.slice(slash + 1))
|
|
3212
|
+
: undefined;
|
|
3213
|
+
if (!arbiterModel) {
|
|
3214
|
+
this.showError(`Dispatch Arbiter model "${settings.model}" is not available.`);
|
|
3215
|
+
return false;
|
|
3216
|
+
}
|
|
3217
|
+
const thinking = settings.thinking ?? "off";
|
|
3218
|
+
const thinkingValidation = validateThinkingLevelForModel(arbiterModel, thinking);
|
|
3219
|
+
if (!thinkingValidation.ok) {
|
|
3220
|
+
this.showError(thinkingValidation.error);
|
|
3221
|
+
return false;
|
|
3222
|
+
}
|
|
3223
|
+
try {
|
|
3224
|
+
loadAndValidateModelRoutingGuide(settings.guidePath, process.cwd(), this.session.scopedModels.map(({ model }) => `${model.provider}/${model.id}`));
|
|
3225
|
+
}
|
|
3226
|
+
catch (error) {
|
|
3227
|
+
this.showError(`Dispatch Arbiter is not ready: ${error instanceof Error ? error.message : String(error)}`);
|
|
3228
|
+
return false;
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
this.settingsManager.drainErrors();
|
|
3232
|
+
try {
|
|
3233
|
+
this.settingsManager.setGlobalSubagentArbiterSettings(settings);
|
|
3234
|
+
await this.settingsManager.flush();
|
|
3235
|
+
}
|
|
3236
|
+
catch (error) {
|
|
3237
|
+
try {
|
|
3238
|
+
this.settingsManager.reload();
|
|
3239
|
+
}
|
|
3240
|
+
catch { }
|
|
3241
|
+
this.showError(`Failed to persist Dispatch Arbiter settings: ${error instanceof Error ? error.message : String(error)}`);
|
|
3242
|
+
return false;
|
|
3243
|
+
}
|
|
3244
|
+
const writeErrors = this.settingsManager.drainErrors();
|
|
3245
|
+
if (writeErrors.length > 0) {
|
|
3246
|
+
try {
|
|
3247
|
+
this.settingsManager.reload();
|
|
3248
|
+
}
|
|
3249
|
+
catch { }
|
|
3250
|
+
this.showError(`Failed to persist Dispatch Arbiter settings: ${writeErrors
|
|
3251
|
+
.map((entry) => `${entry.scope}: ${entry.error.message}`)
|
|
3252
|
+
.join("; ")}`);
|
|
3253
|
+
return false;
|
|
3254
|
+
}
|
|
3255
|
+
this.showStatus(settings.enabled
|
|
3256
|
+
? `Dispatch Arbiter enabled (${settings.model}, thinking ${settings.thinking ?? "off"}).`
|
|
3257
|
+
: "Dispatch Arbiter disabled.");
|
|
3258
|
+
return true;
|
|
3259
|
+
}
|
|
3101
3260
|
showSettingsSelector() {
|
|
3102
3261
|
this.showSelector((done) => {
|
|
3103
3262
|
// Discover agent types for agent models section
|
|
@@ -3137,6 +3296,7 @@ export class InteractiveMode {
|
|
|
3137
3296
|
agentModels: this.settingsManager.getAgentModels(),
|
|
3138
3297
|
agentNames: agentNames,
|
|
3139
3298
|
availableModelIds,
|
|
3299
|
+
subagentArbiter: this.settingsManager.getGlobalSubagentArbiterSettings() ?? {},
|
|
3140
3300
|
}, {
|
|
3141
3301
|
onAutoCompactChange: (enabled) => {
|
|
3142
3302
|
this.session.setAutoCompactionEnabled(enabled);
|
|
@@ -3251,6 +3411,7 @@ export class InteractiveMode {
|
|
|
3251
3411
|
this.editor.setAutocompleteMaxVisible(maxVisible);
|
|
3252
3412
|
}
|
|
3253
3413
|
},
|
|
3414
|
+
onSubagentArbiterChange: (settings) => this.handleSubagentArbiterSettingsChange(settings),
|
|
3254
3415
|
onAgentModelsChange: (agentName, models) => {
|
|
3255
3416
|
const shadowedByProject = this.settingsManager.hasProjectAgentModelOverride(agentName);
|
|
3256
3417
|
if (models.length > 0) {
|