@dreb/coding-agent 2.45.3 → 2.46.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.
Files changed (71) hide show
  1. package/README.md +7 -3
  2. package/dist/core/agent-session.d.ts +10 -2
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +75 -15
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/context-buffer.d.ts +2 -0
  7. package/dist/core/context-buffer.d.ts.map +1 -1
  8. package/dist/core/context-buffer.js +8 -0
  9. package/dist/core/context-buffer.js.map +1 -1
  10. package/dist/core/dispatch-arbiter.d.ts +94 -0
  11. package/dist/core/dispatch-arbiter.d.ts.map +1 -0
  12. package/dist/core/dispatch-arbiter.js +316 -0
  13. package/dist/core/dispatch-arbiter.js.map +1 -0
  14. package/dist/core/extensions/types.d.ts +1 -1
  15. package/dist/core/extensions/types.d.ts.map +1 -1
  16. package/dist/core/extensions/types.js.map +1 -1
  17. package/dist/core/git-repo-state.d.ts +2 -0
  18. package/dist/core/git-repo-state.d.ts.map +1 -1
  19. package/dist/core/git-repo-state.js +19 -0
  20. package/dist/core/git-repo-state.js.map +1 -1
  21. package/dist/core/model-registry.d.ts +1 -0
  22. package/dist/core/model-registry.d.ts.map +1 -1
  23. package/dist/core/model-registry.js +49 -25
  24. package/dist/core/model-registry.js.map +1 -1
  25. package/dist/core/model-routing-guide.d.ts +15 -0
  26. package/dist/core/model-routing-guide.d.ts.map +1 -0
  27. package/dist/core/model-routing-guide.js +186 -0
  28. package/dist/core/model-routing-guide.js.map +1 -0
  29. package/dist/core/settings-manager.d.ts +18 -0
  30. package/dist/core/settings-manager.d.ts.map +1 -1
  31. package/dist/core/settings-manager.js +33 -0
  32. package/dist/core/settings-manager.js.map +1 -1
  33. package/dist/core/thinking.d.ts +14 -0
  34. package/dist/core/thinking.d.ts.map +1 -1
  35. package/dist/core/thinking.js +35 -2
  36. package/dist/core/thinking.js.map +1 -1
  37. package/dist/core/tools/index.d.ts +7 -1
  38. package/dist/core/tools/index.d.ts.map +1 -1
  39. package/dist/core/tools/index.js.map +1 -1
  40. package/dist/core/tools/subagent.d.ts +53 -6
  41. package/dist/core/tools/subagent.d.ts.map +1 -1
  42. package/dist/core/tools/subagent.js +343 -53
  43. package/dist/core/tools/subagent.js.map +1 -1
  44. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  45. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  46. package/dist/modes/interactive/components/settings-selector.js +99 -1
  47. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  49. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.js +75 -1
  51. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  52. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  53. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  54. package/dist/modes/rpc/rpc-mode.js +65 -0
  55. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  56. package/dist/modes/rpc/rpc-types.d.ts +8 -0
  57. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  58. package/dist/modes/rpc/rpc-types.js.map +1 -1
  59. package/docs/agent-models.md +33 -1
  60. package/docs/custom-provider.md +22 -8
  61. package/docs/dashboard.md +8 -2
  62. package/docs/extensions.md +1 -1
  63. package/docs/json.md +31 -5
  64. package/docs/models.md +39 -1
  65. package/docs/rpc.md +60 -9
  66. package/docs/session.md +7 -1
  67. package/docs/settings.md +33 -1
  68. package/docs/skills.md +38 -0
  69. package/examples/extensions/custom-provider-anthropic/index.ts +7 -3
  70. package/package.json +1 -1
  71. 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 extraSecretPatterns = this.settingsManager?.getSecretOutputPatterns();
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
@@ -1587,6 +1639,7 @@ export class AgentSession {
1587
1639
  this._disconnectFromAgent();
1588
1640
  await this.abort();
1589
1641
  this.agent.reset();
1642
+ this._dispatchArbiter.clearContext();
1590
1643
  this.sessionManager.newSession({ parentSession: options?.parentSession });
1591
1644
  this.agent.sessionId = this.sessionManager.getSessionId();
1592
1645
  this._steeringMessages = [];
@@ -2467,6 +2520,12 @@ export class AgentSession {
2467
2520
  parentSessionFile: () => this.sessionFile,
2468
2521
  modelRegistry: this._modelRegistry,
2469
2522
  getAgentModelsForAgent: (name) => this.settingsManager?.getAgentModelsForAgent(name),
2523
+ defaultThinkingLevel: () => this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
2524
+ arbitrate: (request, signal) => this._dispatchArbiter.arbitrate(request, signal),
2525
+ onArbitration: (event) => {
2526
+ this.sessionManager.appendCustomEntry("subagent_arbitration", event);
2527
+ this._emit(event);
2528
+ },
2470
2529
  onBackgroundStart: (agentId, agentType, taskSummary, sessionDir) => {
2471
2530
  this._emit({ type: "background_agent_start", agentId, agentType, taskSummary, sessionDir });
2472
2531
  },
@@ -2783,6 +2842,7 @@ export class AgentSession {
2783
2842
  this._steeringMessages = [];
2784
2843
  this._followUpMessages = [];
2785
2844
  this._pendingNextTurnMessages = [];
2845
+ this._dispatchArbiter.clearContext();
2786
2846
  // Set new session
2787
2847
  this.sessionManager.setSessionFile(sessionPath);
2788
2848
  this.agent.sessionId = this.sessionManager.getSessionId();