@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
|
@@ -22,11 +22,13 @@ import { sleep } from "../utils/sleep.js";
|
|
|
22
22
|
import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
|
|
23
23
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
24
24
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
25
|
+
import { DispatchArbiter } from "./dispatch-arbiter.js";
|
|
25
26
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
26
27
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
27
28
|
import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
28
29
|
import { checkScriptContent, extractScriptPaths, isForbiddenCommand } from "./forbidden-commands.js";
|
|
29
|
-
import { getGitRepoState } from "./git-repo-state.js";
|
|
30
|
+
import { getGitRepoState, getGitStatusMetadata } from "./git-repo-state.js";
|
|
31
|
+
import { findGitRoot } from "./git-root.js";
|
|
30
32
|
import { log } from "./logger.js";
|
|
31
33
|
import { computeNestedContextBlock } from "./nested-context.js";
|
|
32
34
|
import { PerformanceTracker } from "./performance-tracker.js";
|
|
@@ -153,6 +155,8 @@ export class AgentSession {
|
|
|
153
155
|
_ownsPerformanceTracker;
|
|
154
156
|
// Git repo state captured once at session start
|
|
155
157
|
_gitRepoState;
|
|
158
|
+
// Fully headless pre-spawn router. It owns only bounded parent activity context.
|
|
159
|
+
_dispatchArbiter;
|
|
156
160
|
constructor(config) {
|
|
157
161
|
this.agent = config.agent;
|
|
158
162
|
this.sessionManager = config.sessionManager;
|
|
@@ -181,6 +185,29 @@ export class AgentSession {
|
|
|
181
185
|
this._uiType = config.uiType;
|
|
182
186
|
// Capture git repo state once at session start (before building runtime/system prompt)
|
|
183
187
|
this._gitRepoState = getGitRepoState(this._cwd) ?? undefined;
|
|
188
|
+
this._dispatchArbiter = new DispatchArbiter({
|
|
189
|
+
getSettings: () => this.settingsManager.getGlobalSubagentArbiterSettings(),
|
|
190
|
+
getCandidateModels: () => this._scopedModels,
|
|
191
|
+
getModelRegistry: () => this._modelRegistry,
|
|
192
|
+
getMessages: () => this.agent.state.messages,
|
|
193
|
+
getParentModel: () => this.model,
|
|
194
|
+
getSessionTitle: () => this.sessionName,
|
|
195
|
+
getRepoMetadata: (cwd) => {
|
|
196
|
+
const gitRoot = findGitRoot(cwd);
|
|
197
|
+
const isSessionCwd = resolve(cwd) === resolve(this._cwd);
|
|
198
|
+
const currentStatus = gitRoot
|
|
199
|
+
? (getGitStatusMetadata(cwd) ?? (isSessionCwd ? this._gitRepoState : undefined))
|
|
200
|
+
: undefined;
|
|
201
|
+
return {
|
|
202
|
+
repo: gitRoot ? basename(gitRoot) : undefined,
|
|
203
|
+
cwd,
|
|
204
|
+
branch: currentStatus?.branch,
|
|
205
|
+
dirtyCount: currentStatus?.dirtyCount,
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
getExtraSecretPatterns: () => this._compileExtraSecretPatterns(),
|
|
209
|
+
complete: config.dispatchArbiterComplete,
|
|
210
|
+
});
|
|
184
211
|
// Always subscribe to agent events for internal handling
|
|
185
212
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
186
213
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
@@ -199,6 +226,21 @@ export class AgentSession {
|
|
|
199
226
|
getPerformanceTracker() {
|
|
200
227
|
return this.performanceTracker;
|
|
201
228
|
}
|
|
229
|
+
_compileExtraSecretPatterns() {
|
|
230
|
+
return this.settingsManager.getSecretOutputPatterns()?.flatMap((pattern) => {
|
|
231
|
+
if (!pattern.pattern || typeof pattern.pattern !== "string" || pattern.pattern.trim() === "") {
|
|
232
|
+
console.warn(`[secret-scrubber] Skipping empty or invalid pattern in secretOutputPatterns: "${pattern.name}"`);
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
return [{ name: pattern.name, pattern: new RegExp(pattern.pattern, "g") }];
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
console.warn(`[secret-scrubber] Skipping invalid regex in secretOutputPatterns "${pattern.name}": ${pattern.pattern} — ${error instanceof Error ? error.message : String(error)}`);
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
202
244
|
/**
|
|
203
245
|
* Install tool hooks once on the Agent instance.
|
|
204
246
|
*
|
|
@@ -285,20 +327,7 @@ export class AgentSession {
|
|
|
285
327
|
this.agent.setAfterToolCall(async ({ toolCall, args, result, isError }) => {
|
|
286
328
|
// Scrub secrets from tool output — runs before extensions, cannot be bypassed
|
|
287
329
|
let scrubbedContent = result.content;
|
|
288
|
-
const
|
|
289
|
-
const compiledExtras = extraSecretPatterns?.flatMap((p) => {
|
|
290
|
-
if (!p.pattern || typeof p.pattern !== "string" || p.pattern.trim() === "") {
|
|
291
|
-
console.warn(`[secret-scrubber] Skipping empty or invalid pattern in secretOutputPatterns: "${p.name}"`);
|
|
292
|
-
return [];
|
|
293
|
-
}
|
|
294
|
-
try {
|
|
295
|
-
return [{ name: p.name, pattern: new RegExp(p.pattern, "g") }];
|
|
296
|
-
}
|
|
297
|
-
catch (err) {
|
|
298
|
-
console.warn(`[secret-scrubber] Skipping invalid regex in secretOutputPatterns "${p.name}": ${p.pattern} — ${err instanceof Error ? err.message : String(err)}`);
|
|
299
|
-
return [];
|
|
300
|
-
}
|
|
301
|
-
});
|
|
330
|
+
const compiledExtras = this._compileExtraSecretPatterns();
|
|
302
331
|
let totalRedactions = 0;
|
|
303
332
|
scrubbedContent = scrubbedContent.map((item) => {
|
|
304
333
|
if (item.type === "text" && item.text) {
|
|
@@ -513,6 +542,15 @@ export class AgentSession {
|
|
|
513
542
|
*/
|
|
514
543
|
_handleBackgroundComplete(agentId, result, cancelled) {
|
|
515
544
|
const parts = [];
|
|
545
|
+
if (result.model || result.thinking) {
|
|
546
|
+
const metadata = [
|
|
547
|
+
result.model ? `model: ${result.model}` : undefined,
|
|
548
|
+
result.thinking ? `thinking: ${result.thinking}` : undefined,
|
|
549
|
+
]
|
|
550
|
+
.filter((value) => value !== undefined)
|
|
551
|
+
.join(", ");
|
|
552
|
+
parts.push(`Execution metadata: ${metadata}`);
|
|
553
|
+
}
|
|
516
554
|
if (cancelled) {
|
|
517
555
|
parts.push("This agent was cancelled by the user.");
|
|
518
556
|
}
|
|
@@ -585,6 +623,9 @@ export class AgentSession {
|
|
|
585
623
|
agentId,
|
|
586
624
|
agentType: result.agent,
|
|
587
625
|
success: result.exitCode === 0,
|
|
626
|
+
model: result.model,
|
|
627
|
+
thinking: result.thinking,
|
|
628
|
+
steps: result.steps,
|
|
588
629
|
sessionFile: result.sessionFile,
|
|
589
630
|
});
|
|
590
631
|
}
|
|
@@ -738,6 +779,17 @@ export class AgentSession {
|
|
|
738
779
|
await this._emitExtensionEvent(event);
|
|
739
780
|
// Notify all listeners
|
|
740
781
|
this._emit(event);
|
|
782
|
+
// Keep a small, mode-independent rolling context for the headless arbiter.
|
|
783
|
+
if (event.type === "message_end") {
|
|
784
|
+
this._dispatchArbiter.onMessageEnd(event.message);
|
|
785
|
+
}
|
|
786
|
+
else if (event.type === "tool_execution_end") {
|
|
787
|
+
this._dispatchArbiter.onToolEnd({
|
|
788
|
+
toolName: event.toolName,
|
|
789
|
+
isError: event.isError,
|
|
790
|
+
result: event.result,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
741
793
|
// Handle session persistence
|
|
742
794
|
if (event.type === "message_end") {
|
|
743
795
|
// Check if this is a custom message from extensions
|
|
@@ -831,7 +883,12 @@ export class AgentSession {
|
|
|
831
883
|
}
|
|
832
884
|
/** Emit extension events based on agent events */
|
|
833
885
|
async _emitExtensionEvent(event) {
|
|
834
|
-
|
|
886
|
+
// The runner is created unconditionally (so built-in tools like ask_user
|
|
887
|
+
// always have a UI context). When no extensions are loaded there are no
|
|
888
|
+
// handlers to invoke — return synchronously to avoid inserting an extra
|
|
889
|
+
// await tick per event, which would otherwise delay the final agent_end
|
|
890
|
+
// emission past when prompt() resolves.
|
|
891
|
+
if (!this._extensionRunner || !this._extensionRunner.hasExtensions)
|
|
835
892
|
return;
|
|
836
893
|
if (event.type === "agent_start") {
|
|
837
894
|
this._turnIndex = 0;
|
|
@@ -1587,6 +1644,7 @@ export class AgentSession {
|
|
|
1587
1644
|
this._disconnectFromAgent();
|
|
1588
1645
|
await this.abort();
|
|
1589
1646
|
this.agent.reset();
|
|
1647
|
+
this._dispatchArbiter.clearContext();
|
|
1590
1648
|
this.sessionManager.newSession({ parentSession: options?.parentSession });
|
|
1591
1649
|
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
1592
1650
|
this._steeringMessages = [];
|
|
@@ -2390,9 +2448,15 @@ export class AgentSession {
|
|
|
2390
2448
|
const wrappedExtensionTools = this._extensionRunner
|
|
2391
2449
|
? wrapRegisteredTools(allCustomTools, this._extensionRunner)
|
|
2392
2450
|
: [];
|
|
2451
|
+
// Give base tools a per-execution extension context so built-ins like
|
|
2452
|
+
// ask_user can reach ctx.ui. createContext() snapshots hasUI at call time,
|
|
2453
|
+
// so print/RPC-without-host modes degrade to the no-op UI (hasUI === false).
|
|
2454
|
+
const baseToolCtxFactory = this._extensionRunner
|
|
2455
|
+
? () => this._extensionRunner.createContext()
|
|
2456
|
+
: undefined;
|
|
2393
2457
|
const toolRegistry = new Map(Array.from(this._baseToolDefinitions.values()).map((definition) => [
|
|
2394
2458
|
definition.name,
|
|
2395
|
-
wrapToolDefinition(definition),
|
|
2459
|
+
wrapToolDefinition(definition, baseToolCtxFactory),
|
|
2396
2460
|
]));
|
|
2397
2461
|
for (const tool of wrappedExtensionTools) {
|
|
2398
2462
|
toolRegistry.set(tool.name, tool);
|
|
@@ -2467,6 +2531,12 @@ export class AgentSession {
|
|
|
2467
2531
|
parentSessionFile: () => this.sessionFile,
|
|
2468
2532
|
modelRegistry: this._modelRegistry,
|
|
2469
2533
|
getAgentModelsForAgent: (name) => this.settingsManager?.getAgentModelsForAgent(name),
|
|
2534
|
+
defaultThinkingLevel: () => this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
2535
|
+
arbitrate: (request, signal) => this._dispatchArbiter.arbitrate(request, signal),
|
|
2536
|
+
onArbitration: (event) => {
|
|
2537
|
+
this.sessionManager.appendCustomEntry("subagent_arbitration", event);
|
|
2538
|
+
this._emit(event);
|
|
2539
|
+
},
|
|
2470
2540
|
onBackgroundStart: (agentId, agentType, taskSummary, sessionDir) => {
|
|
2471
2541
|
this._emit({ type: "background_agent_start", agentId, agentType, taskSummary, sessionDir });
|
|
2472
2542
|
},
|
|
@@ -2485,12 +2555,11 @@ export class AgentSession {
|
|
|
2485
2555
|
extensionsResult.runtime.flagValues.set(name, value);
|
|
2486
2556
|
}
|
|
2487
2557
|
}
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
: undefined;
|
|
2558
|
+
// The runner also owns the cross-surface UI context used by built-in
|
|
2559
|
+
// tools such as ask_user. Create it even when no third-party extensions
|
|
2560
|
+
// are loaded; otherwise ordinary TUI/Dashboard sessions give base tools
|
|
2561
|
+
// no ctx.ui and ask_user can never open its dialog.
|
|
2562
|
+
this._extensionRunner = new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, this._modelRegistry);
|
|
2494
2563
|
if (this._extensionRunnerRef) {
|
|
2495
2564
|
this._extensionRunnerRef.current = this._extensionRunner;
|
|
2496
2565
|
}
|
|
@@ -2513,6 +2582,7 @@ export class AgentSession {
|
|
|
2513
2582
|
"subagent",
|
|
2514
2583
|
"wait",
|
|
2515
2584
|
"search",
|
|
2585
|
+
"ask_user",
|
|
2516
2586
|
"skill",
|
|
2517
2587
|
"tasks_update",
|
|
2518
2588
|
"suggest_next",
|
|
@@ -2783,6 +2853,7 @@ export class AgentSession {
|
|
|
2783
2853
|
this._steeringMessages = [];
|
|
2784
2854
|
this._followUpMessages = [];
|
|
2785
2855
|
this._pendingNextTurnMessages = [];
|
|
2856
|
+
this._dispatchArbiter.clearContext();
|
|
2786
2857
|
// Set new session
|
|
2787
2858
|
this.sessionManager.setSessionFile(sessionPath);
|
|
2788
2859
|
this.agent.sessionId = this.sessionManager.getSessionId();
|