@dreb/coding-agent 2.57.0 → 2.58.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 (45) hide show
  1. package/README.md +4 -2
  2. package/dist/core/agent-session.d.ts +10 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +28 -3
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/sdk.d.ts.map +1 -1
  7. package/dist/core/sdk.js +1 -0
  8. package/dist/core/sdk.js.map +1 -1
  9. package/dist/core/settings-manager.d.ts +5 -0
  10. package/dist/core/settings-manager.d.ts.map +1 -1
  11. package/dist/core/settings-manager.js +26 -0
  12. package/dist/core/settings-manager.js.map +1 -1
  13. package/dist/core/system-prompt.d.ts +2 -0
  14. package/dist/core/system-prompt.d.ts.map +1 -1
  15. package/dist/core/system-prompt.js +6 -1
  16. package/dist/core/system-prompt.js.map +1 -1
  17. package/dist/core/tools/index.d.ts +1 -1
  18. package/dist/core/tools/index.d.ts.map +1 -1
  19. package/dist/core/tools/index.js +1 -1
  20. package/dist/core/tools/index.js.map +1 -1
  21. package/dist/core/tools/subagent.d.ts +29 -0
  22. package/dist/core/tools/subagent.d.ts.map +1 -1
  23. package/dist/core/tools/subagent.js +48 -25
  24. package/dist/core/tools/subagent.js.map +1 -1
  25. package/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  26. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  27. package/dist/modes/interactive/components/settings-selector.js +11 -0
  28. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  29. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  30. package/dist/modes/interactive/interactive-mode.js +15 -0
  31. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  32. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  33. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  34. package/dist/modes/rpc/rpc-mode.js +12 -0
  35. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  36. package/dist/modes/rpc/rpc-types.d.ts +3 -0
  37. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  38. package/dist/modes/rpc/rpc-types.js.map +1 -1
  39. package/docs/custom-provider.md +17 -2
  40. package/docs/dashboard.md +1 -1
  41. package/docs/models.md +1 -1
  42. package/docs/rpc.md +11 -1
  43. package/docs/sdk.md +1 -1
  44. package/docs/settings.md +5 -1
  45. package/package.json +1 -1
@@ -11,6 +11,7 @@ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"
11
11
  import { attachJsonlLineReader, serializeJsonLine } from "../../modes/rpc/jsonl.js";
12
12
  import { log } from "../logger.js";
13
13
  import { resolveCliModel } from "../model-resolver.js";
14
+ import { DEFAULT_MAX_CONCURRENT_SUBAGENTS } from "../settings-manager.js";
14
15
  import { resolveEffectiveThinkingLevel, thinkingLevelToReasoning, validateThinkingLevelForModel } from "../thinking.js";
15
16
  import { getTextOutput, invalidArgText, str } from "./render-utils.js";
16
17
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
@@ -915,33 +916,43 @@ function formatSkippedModelFailureDetails(skippedModels) {
915
916
  return `Skipped models:\n${skippedModels.map((s) => `- ${s.model}: ${s.reason}`).join("\n")}`;
916
917
  }
917
918
  const MAX_PARALLEL_TASKS = 8;
918
- const MAX_CONCURRENCY = 4;
919
919
  const MAX_TASK_LENGTH = 32_768; // 32 KB — prevent E2BIG from oversized argv
920
+ /**
921
+ * Create a concurrency gate limited to `maxConcurrent` simultaneous holders. The count lives in
922
+ * this closure, so a single gate instance shared across tool rebuilds keeps counting in-flight
923
+ * children accurately, while distinct instances stay fully isolated from one another.
924
+ */
925
+ export function createSubagentConcurrencyGate(maxConcurrent) {
926
+ if (!Number.isSafeInteger(maxConcurrent) || maxConcurrent < 1) {
927
+ throw new Error("Subagent tool concurrency must be a positive whole number");
928
+ }
929
+ let running = 0;
930
+ const waiters = [];
931
+ return {
932
+ acquire() {
933
+ if (running < maxConcurrent) {
934
+ running++;
935
+ return Promise.resolve();
936
+ }
937
+ return new Promise((resolve) => {
938
+ waiters.push(() => {
939
+ running++;
940
+ resolve();
941
+ });
942
+ });
943
+ },
944
+ release() {
945
+ running--;
946
+ const next = waiters.shift();
947
+ if (next)
948
+ next();
949
+ },
950
+ };
951
+ }
920
952
  /** Resolve per-task thinking precedence for parallel and chain modes. */
921
953
  export function resolveSubagentThinkingOverride(taskThinking, topLevelThinking) {
922
954
  return taskThinking ?? topLevelThinking;
923
955
  }
924
- // Semaphore for background task concurrency — shared across all background launches
925
- let bgRunning = 0;
926
- const bgWaiters = [];
927
- async function bgAcquire() {
928
- if (bgRunning < MAX_CONCURRENCY) {
929
- bgRunning++;
930
- return;
931
- }
932
- return new Promise((resolve) => {
933
- bgWaiters.push(() => {
934
- bgRunning++;
935
- resolve();
936
- });
937
- });
938
- }
939
- function bgRelease() {
940
- bgRunning--;
941
- const next = bgWaiters.shift();
942
- if (next)
943
- next();
944
- }
945
956
  /**
946
957
  * Resolve a per-task cwd.
947
958
  * Accepts absolute paths as-is. Resolves relative paths against the parent cwd,
@@ -1743,6 +1754,18 @@ export function createSubagentToolDefinition(cwd, options) {
1743
1754
  const arbitrate = options?.arbitrate;
1744
1755
  const onArbitration = options?.onArbitration;
1745
1756
  const getDefaultThinkingLevel = options?.defaultThinkingLevel;
1757
+ const maxConcurrentSubagents = options?.maxConcurrentSubagents ?? DEFAULT_MAX_CONCURRENT_SUBAGENTS;
1758
+ if (!Number.isSafeInteger(maxConcurrentSubagents) || maxConcurrentSubagents < 1) {
1759
+ throw new Error("Subagent tool concurrency must be a positive whole number");
1760
+ }
1761
+ // The concurrency gate is owned by the caller (e.g. AgentSession) so that it survives
1762
+ // runtime rebuilds/reloads: the tool definition is recreated on every `/reload`, but the
1763
+ // gate must keep counting in-flight children launched before the reload. When no gate is
1764
+ // supplied (external SDK callers, tests) the tool owns a fresh per-instance gate, which
1765
+ // still keeps separately embedded sessions from coupling through module-global state.
1766
+ const concurrencyGate = options?.concurrencyGate ?? createSubagentConcurrencyGate(maxConcurrentSubagents);
1767
+ const acquireBackgroundSlot = () => concurrencyGate.acquire();
1768
+ const releaseBackgroundSlot = () => concurrencyGate.release();
1746
1769
  // Discover agents at definition time to build the prompt guidelines.
1747
1770
  // This is cheap (reads .md files) and the same call happens on every execute().
1748
1771
  const knownAgents = discoverAgentTypes(cwd);
@@ -1758,7 +1781,7 @@ export function createSubagentToolDefinition(cwd, options) {
1758
1781
  label: "subagent",
1759
1782
  description: "Run focused, independent work in a child agent when the task matches that agent's defined role " +
1760
1783
  "(Explore for concrete evidence gathering, Sandbox for isolated /tmp-only analysis). " +
1761
- "Supports `task` for a single task, `tasks` for parallel execution in one call (up to 8, max 4 concurrent), " +
1784
+ `Supports \`task\` for a single task, \`tasks\` for parallel execution in one call (up to 8, max ${maxConcurrentSubagents} concurrent), ` +
1762
1785
  "and `chain` for a sequential pipeline with {previous} substitution. " +
1763
1786
  "All subagents run in background — returns immediately, notifies on completion.",
1764
1787
  promptSnippet: "Run role-matched work in independent child agents",
@@ -1878,7 +1901,7 @@ export function createSubagentToolDefinition(cwd, options) {
1878
1901
  }
1879
1902
  };
1880
1903
  const run = async () => {
1881
- await bgAcquire();
1904
+ await acquireBackgroundSlot();
1882
1905
  try {
1883
1906
  const result = await runFn(bgSignal, onChildEvent, onArbitrationRecord, onControlAvailable);
1884
1907
  const entry = backgroundAgentRegistry.get(agentId);
@@ -1905,7 +1928,7 @@ export function createSubagentToolDefinition(cwd, options) {
1905
1928
  }
1906
1929
  finally {
1907
1930
  backgroundControlClients.delete(agentId);
1908
- bgRelease();
1931
+ releaseBackgroundSlot();
1909
1932
  }
1910
1933
  };
1911
1934
  run().catch((err) => {