@wrongstack/core 0.109.1 → 0.141.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 (67) hide show
  1. package/dist/{agent-bridge-mOxbpFcg.d.ts → agent-bridge-r9y6gdn4.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DukQLUcS.d.ts → agent-subagent-runner-1GeQE_L0.d.ts} +7 -8
  3. package/dist/{brain-Dfv4Y82E.d.ts → brain-Cp_3GIS2.d.ts} +14 -3
  4. package/dist/{compactor-DXLxLcmU.d.ts → compactor-BueGt7LG.d.ts} +9 -1
  5. package/dist/{config-BSU-6vah.d.ts → config-BaVThgnT.d.ts} +104 -3
  6. package/dist/{context-CNRYfhUv.d.ts → context-C7G_MtLV.d.ts} +38 -1
  7. package/dist/coordination/index.d.ts +12 -13
  8. package/dist/coordination/index.js +229 -17
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -27
  11. package/dist/defaults/index.js +886 -721
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -16
  14. package/dist/execution/index.js +535 -322
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +6 -7
  17. package/dist/{goal-preamble-CI8lxeY1.d.ts → goal-preamble-iuIUTQwk.d.ts} +48 -18
  18. package/dist/{index-BWRN6wOb.d.ts → index-BZdezm3g.d.ts} +9 -10
  19. package/dist/{index-DIKEcfgC.d.ts → index-CPweVoFM.d.ts} +7 -6
  20. package/dist/index.d.ts +51 -52
  21. package/dist/index.js +752 -545
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -7
  24. package/dist/infrastructure/index.js +64 -19
  25. package/dist/infrastructure/index.js.map +1 -1
  26. package/dist/kernel/index.d.ts +10 -11
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/llm-selector-CP72f1lC.d.ts +58 -0
  29. package/dist/{mcp-servers-CXCsANdY.d.ts → mcp-servers-Bl5LTvQg.d.ts} +3 -3
  30. package/dist/{mode-ARA3HrkY.d.ts → mode-CZlO9iU1.d.ts} +1 -1
  31. package/dist/models/index.d.ts +157 -53
  32. package/dist/models/index.js +475 -20
  33. package/dist/models/index.js.map +1 -1
  34. package/dist/{models-registry-DU64QxQa.d.ts → models-registry-D90K9UnM.d.ts} +1 -1
  35. package/dist/{multi-agent-coordinator-51LvnXkD.d.ts → multi-agent-coordinator-QWEzJDlm.d.ts} +1 -1
  36. package/dist/{null-fleet-bus-D09hMzFQ.d.ts → null-fleet-bus-BUyfqh23.d.ts} +13 -10
  37. package/dist/observability/index.d.ts +2 -2
  38. package/dist/{parallel-eternal-engine-B2CbsKpc.d.ts → parallel-eternal-engine-Dj2SYzha.d.ts} +24 -11
  39. package/dist/{path-resolver-DDJiMAtX.d.ts → path-resolver-DRjQBkoO.d.ts} +3 -3
  40. package/dist/{permission-BDv7z0mk.d.ts → permission-B7nKnEvQ.d.ts} +1 -1
  41. package/dist/{permission-policy-dF74EpDp.d.ts → permission-policy-8-6zBmfA.d.ts} +2 -13
  42. package/dist/{pipeline-BqiA_UMr.d.ts → pipeline-BG7UgbDc.d.ts} +2 -2
  43. package/dist/{plan-templates-BdDxl9cI.d.ts → plan-templates-CkKNPU3I.d.ts} +6 -6
  44. package/dist/{provider-runner-BUunikwY.d.ts → provider-runner-BNpuIyOL.d.ts} +3 -3
  45. package/dist/{retry-policy-BcmuT_V0.d.ts → retry-policy-rutAfVeR.d.ts} +1 -1
  46. package/dist/sdd/index.d.ts +8 -9
  47. package/dist/sdd/index.js +208 -1
  48. package/dist/sdd/index.js.map +1 -1
  49. package/dist/{secret-vault-DrOhc2i5.d.ts → secret-vault-BTcC_T5v.d.ts} +3 -2
  50. package/dist/security/index.d.ts +5 -7
  51. package/dist/security/index.js +4 -35
  52. package/dist/security/index.js.map +1 -1
  53. package/dist/{selector-C7wcdqMA.d.ts → selector-4vDFZKt3.d.ts} +1 -1
  54. package/dist/{session-event-bridge-BpJ5trO9.d.ts → session-event-bridge-DWlvglC2.d.ts} +2 -2
  55. package/dist/{session-reader-DDz1Ek4V.d.ts → session-reader-BAtCxdaw.d.ts} +1 -1
  56. package/dist/storage/index.d.ts +14 -15
  57. package/dist/storage/index.js +140 -134
  58. package/dist/storage/index.js.map +1 -1
  59. package/dist/types/index.d.ts +19 -20
  60. package/dist/types/index.js +186 -102
  61. package/dist/types/index.js.map +1 -1
  62. package/dist/utils/index.d.ts +38 -15
  63. package/dist/utils/index.js +62 -33
  64. package/dist/utils/index.js.map +1 -1
  65. package/dist/{wstack-paths-_lqjzErq.d.ts → wstack-paths-DD50Omgn.d.ts} +3 -0
  66. package/package.json +2 -2
  67. package/dist/models-registry-B6_KfS65.d.ts +0 -95
package/dist/index.js CHANGED
@@ -1114,6 +1114,39 @@ function describeStatus(status, type) {
1114
1114
  function truncate(s, n) {
1115
1115
  return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
1116
1116
  }
1117
+ var StreamHangError = class extends ProviderError {
1118
+ /** Name of the provider that hung, e.g. "zai", "anthropic". */
1119
+ hungProviderId;
1120
+ /** Model that was being called when the hang occurred. */
1121
+ hungModel;
1122
+ /** How long (ms) we waited for the next chunk before declaring a hang. */
1123
+ hangTimeoutMs;
1124
+ /** How many bytes were received before the hang. */
1125
+ bytesReceived;
1126
+ /** Elapsed time (ms) from the start of the stream until the hang. */
1127
+ elapsedMs;
1128
+ constructor(opts) {
1129
+ super(
1130
+ `Stream hang: ${opts.providerId}/${opts.model} \u2014 no data for ${opts.hangTimeoutMs}ms after ${opts.bytesReceived} bytes (${opts.elapsedMs}ms elapsed)`,
1131
+ 599,
1132
+ true,
1133
+ // always retryable
1134
+ opts.providerId,
1135
+ {
1136
+ body: {
1137
+ message: `Stream stalled after ${opts.elapsedMs}ms, ${opts.bytesReceived} bytes received`
1138
+ },
1139
+ cause: opts.cause
1140
+ }
1141
+ );
1142
+ this.name = "StreamHangError";
1143
+ this.hungProviderId = opts.providerId;
1144
+ this.hungModel = opts.model;
1145
+ this.hangTimeoutMs = opts.hangTimeoutMs;
1146
+ this.bytesReceived = opts.bytesReceived;
1147
+ this.elapsedMs = opts.elapsedMs;
1148
+ }
1149
+ };
1117
1150
  function providerStatusToCode(status, type) {
1118
1151
  if (status === 0) return ERROR_CODES.PROVIDER_NETWORK_ERROR;
1119
1152
  if (type === "rate_limit_error" || status === 429) return ERROR_CODES.PROVIDER_RATE_LIMITED;
@@ -1728,16 +1761,17 @@ function expectDefined(value, label) {
1728
1761
 
1729
1762
  // src/utils/token-estimate.ts
1730
1763
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1731
- var _cal = {
1732
- ratio: 1,
1733
- // current calibration multiplier (actual / estimated)
1734
- count: 0,
1735
- // number of samples recorded
1736
- prevEst: 0,
1737
- // estimated tokens from the most recent estimateRequestTokens call
1738
- /** EWM α — higher = faster adaptation, more volatile */
1739
- alpha: 0.3
1740
- };
1764
+ var CAL_ALPHA = 0.3;
1765
+ var CALIBRATION_GLOBAL_KEY = "__global__";
1766
+ var _cals = /* @__PURE__ */ new Map();
1767
+ function calState(key) {
1768
+ let state = _cals.get(key);
1769
+ if (!state) {
1770
+ state = { ratio: 1, count: 0, prevEst: 0 };
1771
+ _cals.set(key, state);
1772
+ }
1773
+ return state;
1774
+ }
1741
1775
  var MIN_SAMPLES_FOR_CALIBRATION = 3;
1742
1776
  var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1743
1777
  var ESTIMATE_CACHE_MAX_SIZE = 1e4;
@@ -1770,10 +1804,25 @@ function estimateToolResultTokens(content) {
1770
1804
  function estimateTextTokens(text) {
1771
1805
  return RoughTokenEstimate(text);
1772
1806
  }
1807
+ function estimateMessageTokens(messages) {
1808
+ let total = 0;
1809
+ for (const m of messages) {
1810
+ if (typeof m.content === "string") {
1811
+ total += estimateTextTokens(m.content);
1812
+ } else {
1813
+ for (const b of m.content) {
1814
+ if (b.type === "text") total += estimateTextTokens(b.text);
1815
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
1816
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
1817
+ }
1818
+ }
1819
+ }
1820
+ return total;
1821
+ }
1773
1822
  function estimateToolDefTokens(tool) {
1774
1823
  return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
1775
1824
  }
1776
- function estimateRequestTokens(messages, systemPrompt, tools) {
1825
+ function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
1777
1826
  let messagesTokens = 0;
1778
1827
  if (typeof messages === "string") {
1779
1828
  messagesTokens = RoughTokenEstimate(messages);
@@ -1812,7 +1861,7 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
1812
1861
  toolsTokens += estimateToolDefTokens(t2);
1813
1862
  }
1814
1863
  const total = messagesTokens + systemTokens + toolsTokens;
1815
- _cal.prevEst = total;
1864
+ calState(calibrationKey).prevEst = total;
1816
1865
  return {
1817
1866
  messages: messagesTokens,
1818
1867
  systemPrompt: systemTokens,
@@ -1820,30 +1869,33 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
1820
1869
  total
1821
1870
  };
1822
1871
  }
1823
- function recordActualUsage(actualInputTokens, estimatedInputTokens) {
1872
+ function recordActualUsage(actualInputTokens, estimatedInputTokens, calibrationKey = CALIBRATION_GLOBAL_KEY) {
1824
1873
  if (actualInputTokens <= 0) return;
1825
- const est = estimatedInputTokens ?? _cal.prevEst;
1874
+ const cal = calState(calibrationKey);
1875
+ const est = estimatedInputTokens ?? cal.prevEst;
1826
1876
  if (est <= 0) return;
1827
1877
  const sampleRatio = actualInputTokens / est;
1828
- if (_cal.count === 0) {
1829
- _cal.ratio = sampleRatio;
1878
+ if (cal.count === 0) {
1879
+ cal.ratio = sampleRatio;
1830
1880
  } else {
1831
- _cal.ratio = _cal.alpha * sampleRatio + (1 - _cal.alpha) * _cal.ratio;
1881
+ cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;
1832
1882
  }
1833
- _cal.ratio = Math.min(1.5, Math.max(0.5, _cal.ratio));
1834
- _cal.count++;
1883
+ cal.ratio = Math.min(1.5, Math.max(0.5, cal.ratio));
1884
+ cal.count++;
1835
1885
  }
1836
- function getCalibrationState() {
1886
+ function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
1887
+ const cal = calState(calibrationKey);
1837
1888
  return {
1838
- ratio: _cal.ratio,
1839
- count: _cal.count,
1840
- calibrated: _cal.count >= MIN_SAMPLES_FOR_CALIBRATION
1889
+ ratio: cal.ratio,
1890
+ count: cal.count,
1891
+ calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
1841
1892
  };
1842
1893
  }
1843
- function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
1844
- const result = estimateRequestTokens(messages, systemPrompt, tools);
1845
- if (_cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
1846
- const safeRatio = Math.min(1.5, Math.max(0.5, _cal.ratio));
1894
+ function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
1895
+ const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
1896
+ const cal = calState(calibrationKey);
1897
+ if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
1898
+ const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
1847
1899
  return {
1848
1900
  messages: Math.round(result.messages * safeRatio),
1849
1901
  systemPrompt: Math.round(result.systemPrompt * safeRatio),
@@ -1853,10 +1905,12 @@ function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
1853
1905
  }
1854
1906
  return result;
1855
1907
  }
1856
- function resetCalibration() {
1857
- _cal.ratio = 1;
1858
- _cal.count = 0;
1859
- _cal.prevEst = 0;
1908
+ function resetCalibration(calibrationKey) {
1909
+ if (calibrationKey === void 0) {
1910
+ _cals.clear();
1911
+ return;
1912
+ }
1913
+ _cals.delete(calibrationKey);
1860
1914
  }
1861
1915
 
1862
1916
  // src/utils/message-invariants.ts
@@ -1951,34 +2005,144 @@ function isEmptyMessage(msg) {
1951
2005
  return msg.content.length === 0;
1952
2006
  }
1953
2007
 
2008
+ // src/execution/compaction-core.ts
2009
+ var estimateMessages = estimateMessageTokens;
2010
+ function hasTextContent(m) {
2011
+ if (typeof m.content === "string") return m.content.trim().length > 0;
2012
+ return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
2013
+ }
2014
+ function findPreserveStart(messages, preserveK) {
2015
+ let pairCount = 0;
2016
+ let preserveStart = messages.length;
2017
+ for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
2018
+ const m = messages[i];
2019
+ if (!m) continue;
2020
+ if (m.role === "user" || m.role === "assistant") {
2021
+ pairCount++;
2022
+ preserveStart = i;
2023
+ }
2024
+ }
2025
+ for (let i = preserveStart; i < messages.length; i++) {
2026
+ const m = messages[i];
2027
+ if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
2028
+ const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
2029
+ if (hasToolUse2 && i + 1 < messages.length) {
2030
+ const next = messages[i + 1];
2031
+ if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
2032
+ preserveStart = i + 1;
2033
+ }
2034
+ }
2035
+ }
2036
+ return preserveStart;
2037
+ }
2038
+ function eliseOldToolResults(messages, opts) {
2039
+ const preserveStart = findPreserveStart(messages, opts.preserveK);
2040
+ let saved = 0;
2041
+ let changed = false;
2042
+ const next = new Array(messages.length);
2043
+ for (let i = 0; i < messages.length; i++) {
2044
+ const msg = messages[i];
2045
+ if (i >= preserveStart || !msg || !Array.isArray(msg.content)) {
2046
+ next[i] = msg;
2047
+ continue;
2048
+ }
2049
+ const original = msg.content;
2050
+ const newContent = original.map((b) => {
2051
+ if (b.type !== "tool_result") return b;
2052
+ const tokens = estimateToolResultTokens(b.content);
2053
+ if (tokens < opts.eliseThreshold) return b;
2054
+ saved += tokens;
2055
+ const elided = {
2056
+ type: "tool_result",
2057
+ tool_use_id: b.tool_use_id,
2058
+ content: `[elided: ~${tokens} tokens]`,
2059
+ is_error: b.is_error
2060
+ };
2061
+ return elided;
2062
+ });
2063
+ if (newContent.every((b, idx) => b === original[idx])) {
2064
+ next[i] = msg;
2065
+ } else {
2066
+ next[i] = { ...msg, content: newContent };
2067
+ changed = true;
2068
+ }
2069
+ }
2070
+ return { messages: changed ? next : messages, saved, changed };
2071
+ }
2072
+ function buildLosslessDigest(messages) {
2073
+ const lines = [];
2074
+ for (const m of messages) {
2075
+ let text;
2076
+ let omitted = 0;
2077
+ if (typeof m.content === "string") {
2078
+ text = m.content;
2079
+ } else {
2080
+ const parts = [];
2081
+ for (const b of m.content) {
2082
+ if (isTextBlock(b)) parts.push(b.text);
2083
+ else if (b.type === "tool_use" || b.type === "tool_result") omitted++;
2084
+ }
2085
+ text = parts.join(" ");
2086
+ }
2087
+ if (text.trim().length === 0 && omitted === 0) continue;
2088
+ const marker = omitted > 0 ? ` [${omitted} tool call(s) omitted \u2014 see session log]` : "";
2089
+ lines.push(`[${m.role}]: ${text}${marker}`);
2090
+ }
2091
+ return lines.join("\n");
2092
+ }
2093
+ function findSafeBoundary(messages, from, to) {
2094
+ for (let i = to; i >= from; i--) {
2095
+ const m = messages[i];
2096
+ if (!m) continue;
2097
+ if (m.role === "user" && hasTextContent(m)) {
2098
+ return findExchangeStart(messages, i);
2099
+ }
2100
+ }
2101
+ return -1;
2102
+ }
2103
+ function findExchangeStart(messages, userIndex) {
2104
+ for (let i = userIndex - 1; i >= 0; i--) {
2105
+ const m = messages[i];
2106
+ if (!m) continue;
2107
+ if (m.role === "assistant") {
2108
+ const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
2109
+ if (!hasToolUse2) return i + 1;
2110
+ } else if (m.role === "user") {
2111
+ return i;
2112
+ }
2113
+ }
2114
+ return 0;
2115
+ }
2116
+
1954
2117
  // src/execution/compactor.ts
1955
2118
  var HybridCompactor = class {
1956
2119
  preserveK;
1957
2120
  eliseThreshold;
1958
- estimator;
1959
2121
  constructor(opts = {}) {
1960
2122
  this.preserveK = opts.preserveK ?? 5;
1961
2123
  this.eliseThreshold = opts.eliseThreshold ?? 2e3;
1962
- this.estimator = opts.estimator ?? estimateTextTokens;
1963
2124
  }
1964
2125
  async compact(ctx, opts = {}) {
1965
- const beforeTokens = this.estimateMessages(ctx.messages);
2126
+ const beforeTokens = estimateMessages(ctx.messages);
1966
2127
  const beforeFull = this.estimateFullRequest(ctx);
1967
2128
  const reductions = [];
1968
2129
  const policy = readContextWindowPolicy(ctx);
1969
2130
  const preserveK = policy?.preserveK ?? this.preserveK;
1970
2131
  const eliseThreshold = policy?.eliseThreshold ?? this.eliseThreshold;
1971
- const phase1Saved = this.eliseOldToolResults(ctx, preserveK, eliseThreshold);
1972
- if (phase1Saved > 0) reductions.push({ phase: "elision", saved: phase1Saved });
2132
+ const elide = eliseOldToolResults(ctx.messages, { preserveK, eliseThreshold });
2133
+ if (elide.changed) ctx.state.replaceMessages(elide.messages);
2134
+ if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
2135
+ let collapsedDigest;
1973
2136
  if (opts.aggressive) {
1974
- const phase2Saved = this.collapseAncientTurns(ctx, preserveK);
1975
- if (phase2Saved > 0) reductions.push({ phase: "summary", saved: phase2Saved });
2137
+ const phase2 = this.collapseAncientTurns(ctx, preserveK);
2138
+ if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
2139
+ collapsedDigest = phase2.digest;
1976
2140
  }
1977
2141
  const repaired = repairToolUseAdjacency(ctx.messages);
1978
2142
  if (repaired.report.changed) {
1979
2143
  ctx.state.replaceMessages(repaired.messages);
1980
2144
  }
1981
- const afterTokens = this.estimateMessages(ctx.messages);
2145
+ const afterTokens = estimateMessages(ctx.messages);
1982
2146
  const afterFull = this.estimateFullRequest(ctx);
1983
2147
  return {
1984
2148
  before: beforeTokens,
@@ -1986,6 +2150,7 @@ var HybridCompactor = class {
1986
2150
  fullRequestTokensBefore: beforeFull,
1987
2151
  fullRequestTokensAfter: afterFull,
1988
2152
  reductions,
2153
+ collapsedDigest,
1989
2154
  repaired: repaired.report.changed ? {
1990
2155
  removedToolUses: repaired.report.removedToolUses,
1991
2156
  removedToolResults: repaired.report.removedToolResults,
@@ -1998,72 +2163,23 @@ var HybridCompactor = class {
1998
2163
  * This is the accurate figure for context-window pressure monitoring.
1999
2164
  */
2000
2165
  estimateFullRequest(ctx) {
2001
- const breakdown = estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []);
2002
- return breakdown.total;
2003
- }
2004
- eliseOldToolResults(ctx, preserveK = this.preserveK, eliseThreshold = this.eliseThreshold) {
2005
- const messages = ctx.messages;
2006
- let pairCount = 0;
2007
- let preserveStart = messages.length;
2008
- for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
2009
- const m = messages[i];
2010
- if (!m) continue;
2011
- if (m.role === "user" || m.role === "assistant") {
2012
- pairCount++;
2013
- preserveStart = i;
2014
- }
2015
- }
2016
- for (let i = preserveStart; i < messages.length; i++) {
2017
- const m = messages[i];
2018
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
2019
- const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
2020
- if (hasToolUse2 && i + 1 < messages.length) {
2021
- const next = messages[i + 1];
2022
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
2023
- preserveStart = i + 1;
2024
- }
2025
- }
2026
- }
2027
- let saved = 0;
2028
- let changed = false;
2029
- const nextMessages = new Array(messages.length);
2030
- for (let i = 0; i < messages.length; i++) {
2031
- const msg = messages[i];
2032
- if (i >= preserveStart) {
2033
- nextMessages[i] = msg;
2034
- continue;
2035
- }
2036
- if (!msg || !Array.isArray(msg.content)) {
2037
- nextMessages[i] = msg;
2038
- continue;
2039
- }
2040
- const newContent = msg.content.map((b) => {
2041
- if (b.type !== "tool_result") return b;
2042
- const tokens = estimateToolResultTokens(b.content);
2043
- if (tokens < eliseThreshold) return b;
2044
- saved += tokens;
2045
- const elided = {
2046
- type: "tool_result",
2047
- tool_use_id: b.tool_use_id,
2048
- content: `[elided: ~${tokens} tokens]`,
2049
- is_error: b.is_error
2050
- };
2051
- return elided;
2052
- });
2053
- if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
2054
- nextMessages[i] = msg;
2055
- } else {
2056
- nextMessages[i] = { ...msg, content: newContent };
2057
- changed = true;
2058
- }
2059
- }
2060
- if (changed) ctx.state.replaceMessages(nextMessages);
2061
- return saved;
2166
+ return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
2062
2167
  }
2168
+ /**
2169
+ * Lossless rule-based collapse of ancient turns into a single digest message.
2170
+ *
2171
+ * Preserves ALL textual content of the collapsed range — user instructions,
2172
+ * assistant decisions/conclusions, and any prior digests (chained forward so
2173
+ * the digest stays lossless across repeated compactions). Only `tool_use` /
2174
+ * `tool_result` protocol blocks are dropped and replaced with a count marker;
2175
+ * their full payload already lives in the session log. No sub-LLM call.
2176
+ *
2177
+ * Returns the token savings and the digest text (for audit logging).
2178
+ */
2063
2179
  collapseAncientTurns(ctx, preserveK = this.preserveK) {
2064
2180
  const messages = ctx.messages;
2065
2181
  const cutTarget = Math.max(0, messages.length - preserveK * 2);
2066
- if (cutTarget <= 0) return 0;
2182
+ if (cutTarget <= 0) return { saved: 0 };
2067
2183
  let boundary = -1;
2068
2184
  for (let i = cutTarget; i < messages.length; i++) {
2069
2185
  const m = messages[i];
@@ -2073,34 +2189,20 @@ var HybridCompactor = class {
2073
2189
  break;
2074
2190
  }
2075
2191
  }
2076
- if (boundary <= 0) return 0;
2192
+ if (boundary <= 0) return { saved: 0 };
2077
2193
  const removed = messages.slice(0, boundary);
2078
- const removedTokens = this.estimateMessages(removed);
2079
- const summary = [
2080
- {
2081
- role: "user",
2082
- content: `[previous_session_summary: ${removed.length} earlier turns compacted. Todo state preserved in context.]`
2083
- },
2084
- { role: "assistant", content: "Continuing from compacted context." }
2085
- ];
2194
+ const removedTokens = estimateMessages(removed);
2195
+ const digest = buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)`;
2196
+ const summaryMsg = {
2197
+ role: "system",
2198
+ content: `[prior_turns_digest: ${digest}]`
2199
+ };
2086
2200
  const tail = ctx.messages.slice(boundary);
2087
- ctx.state.replaceMessages([...summary, ...tail]);
2088
- return Math.max(0, removedTokens - this.estimateMessages(summary));
2089
- }
2090
- estimateMessages(messages) {
2091
- let total = 0;
2092
- for (const m of messages) {
2093
- if (typeof m.content === "string") {
2094
- total += this.estimator(m.content);
2095
- } else {
2096
- for (const b of m.content) {
2097
- if (b.type === "text") total += this.estimator(b.text);
2098
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
2099
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
2100
- }
2101
- }
2102
- }
2103
- return total;
2201
+ ctx.state.replaceMessages([summaryMsg, ...tail]);
2202
+ return {
2203
+ saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
2204
+ digest
2205
+ };
2104
2206
  }
2105
2207
  };
2106
2208
  function readContextWindowPolicy(ctx) {
@@ -2112,10 +2214,6 @@ function readContextWindowPolicy(ctx) {
2112
2214
  }
2113
2215
  return candidate;
2114
2216
  }
2115
- function hasTextContent(m) {
2116
- if (typeof m.content === "string") return m.content.trim().length > 0;
2117
- return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
2118
- }
2119
2217
  var PROJECT_MARKERS = [
2120
2218
  ".git",
2121
2219
  "package.json",
@@ -2322,6 +2420,7 @@ var DefaultRetryPolicy = class {
2322
2420
  if (err instanceof ProviderError) {
2323
2421
  if (err.status === 429) return 5;
2324
2422
  if (err.status === 529) return 3;
2423
+ if (err.status === 599) return 5;
2325
2424
  if (err.status >= 500) return 3;
2326
2425
  return 0;
2327
2426
  }
@@ -3362,6 +3461,8 @@ function topologicalSort(graph) {
3362
3461
  var ToolCapabilities = {
3363
3462
  /** Can execute arbitrary commands in the user's shell (the `bash` tool). */
3364
3463
  SHELL_ARBITRARY: "shell.arbitrary",
3464
+ /** Can execute a restricted set of commands (the `exec` tool). */
3465
+ SHELL_RESTRICTED: "shell.restricted",
3365
3466
  /** Can write / modify / delete files inside the project. */
3366
3467
  FS_WRITE: "fs.write",
3367
3468
  /** Can write files outside the current project root (very high risk). */
@@ -3377,6 +3478,7 @@ var ToolCapabilities = {
3377
3478
  };
3378
3479
  var DANGEROUS_FOR_SUBAGENTS = [
3379
3480
  ToolCapabilities.SHELL_ARBITRARY,
3481
+ ToolCapabilities.SHELL_RESTRICTED,
3380
3482
  ToolCapabilities.FS_WRITE,
3381
3483
  ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
3382
3484
  ToolCapabilities.MCP_PROXY,
@@ -4451,7 +4553,7 @@ function computeTaskItemProgress(tasks) {
4451
4553
  let failed = 0;
4452
4554
  let review = 0;
4453
4555
  let estimatedHours = 0;
4454
- let actualHours = 0;
4556
+ const actualHours = 0;
4455
4557
  for (const t2 of tasks) {
4456
4558
  switch (t2.status) {
4457
4559
  case "completed":
@@ -4815,6 +4917,7 @@ function resolveWstackPaths(opts) {
4815
4917
  projectTrust: path6.join(projectDir, "trust.json"),
4816
4918
  projectMeta: path6.join(projectDir, "meta.json"),
4817
4919
  projectLocalConfig: path6.join(projectDir, "config.local.json"),
4920
+ inProjectConfig: path6.join(opts.projectRoot, ".wrongstack", "config.json"),
4818
4921
  inProjectAgentsFile: path6.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
4819
4922
  inProjectSkills: path6.join(opts.projectRoot, ".wrongstack", "skills"),
4820
4923
  inProjectWorktrees: path6.join(opts.projectRoot, ".wrongstack", "worktrees"),
@@ -4923,6 +5026,13 @@ function buildChildEnv(optsOrSessionId) {
4923
5026
  function sleep(ms) {
4924
5027
  return new Promise((resolve12) => setTimeout(resolve12, ms));
4925
5028
  }
5029
+
5030
+ // src/utils/assert-never.ts
5031
+ function assertNever(x, message) {
5032
+ throw new Error(
5033
+ message ?? `Unhandled case: ${JSON.stringify(x)}`
5034
+ );
5035
+ }
4926
5036
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
4927
5037
  var IS_WINDOWS = process.platform === "win32";
4928
5038
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -4985,7 +5095,7 @@ async function expandGlob(pattern) {
4985
5095
  const abs = isAbsolute(pattern);
4986
5096
  const base = abs ? baseDir(pattern) : baseDir(pattern);
4987
5097
  const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
4988
- async function walk4(dir, pat) {
5098
+ async function walk3(dir, pat) {
4989
5099
  let entries;
4990
5100
  try {
4991
5101
  entries = await fsp3.readdir(dir);
@@ -5006,12 +5116,12 @@ async function expandGlob(pattern) {
5006
5116
  const before = pat.slice(0, firstGlob);
5007
5117
  const rest = pat.slice(firstGlob);
5008
5118
  if (before.endsWith("**")) {
5009
- await walk4(dir, rest);
5119
+ await walk3(dir, rest);
5010
5120
  for (const e of entries) {
5011
5121
  const full = `${dir}${SEP}${e}`;
5012
5122
  try {
5013
5123
  const stat11 = await fsp3.stat(full);
5014
- if (stat11.isDirectory()) await walk4(full, rest);
5124
+ if (stat11.isDirectory()) await walk3(full, rest);
5015
5125
  } catch {
5016
5126
  }
5017
5127
  }
@@ -5029,13 +5139,13 @@ async function expandGlob(pattern) {
5029
5139
  const full = `${dir}${SEP}${seg}`;
5030
5140
  try {
5031
5141
  const stat11 = await fsp3.stat(full);
5032
- if (stat11.isDirectory()) await walk4(full, rest);
5142
+ if (stat11.isDirectory()) await walk3(full, rest);
5033
5143
  } catch {
5034
5144
  }
5035
5145
  }
5036
5146
  }
5037
5147
  }
5038
- await walk4(base === "." ? "." : base, relPat);
5148
+ await walk3(base === "." ? "." : base, relPat);
5039
5149
  return [...results];
5040
5150
  }
5041
5151
 
@@ -5206,8 +5316,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
5206
5316
  onClose: (s) => this.appendToIndex(s)
5207
5317
  });
5208
5318
  } catch (err) {
5209
- await handle.close().catch(() => {
5210
- });
5319
+ await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
5211
5320
  throw err;
5212
5321
  }
5213
5322
  }
@@ -5238,8 +5347,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
5238
5347
  );
5239
5348
  return { writer, data };
5240
5349
  } catch (err) {
5241
- await handle.close().catch(() => {
5242
- });
5350
+ await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
5243
5351
  throw err;
5244
5352
  }
5245
5353
  }
@@ -5428,15 +5536,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
5428
5536
  * sidecars, and the session directory (fleet.json, shared/, subagents/).
5429
5537
  */
5430
5538
  async deleteSession(id) {
5431
- await fsp3.unlink(this.sessionPath(id, ".jsonl")).catch(() => void 0);
5432
- await fsp3.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
5539
+ await fsp3.unlink(this.sessionPath(id, ".jsonl")).catch((err) => console.warn(`[session-store] delete .jsonl failed: ${err}`));
5540
+ await fsp3.unlink(this.sessionPath(id, ".summary.json")).catch((err) => console.warn(`[session-store] delete .summary.json failed: ${err}`));
5433
5541
  const shardDir = path6.dirname(path6.join(this.dir, id));
5434
5542
  const base = path6.basename(id);
5435
5543
  for (const ext of [".plan.json", ".todos.json"]) {
5436
- await fsp3.unlink(path6.join(shardDir, `${base}${ext}`)).catch(() => void 0);
5544
+ await fsp3.unlink(path6.join(shardDir, `${base}${ext}`)).catch((err) => console.warn(`[session-store] delete ${ext} failed: ${err}`));
5437
5545
  }
5438
5546
  const sessDir = path6.join(shardDir, base);
5439
- await fsp3.rm(sessDir, { recursive: true, force: true }).catch(() => void 0);
5547
+ await fsp3.rm(sessDir, { recursive: true, force: true }).catch((err) => console.warn(`[session-store] delete session dir failed: ${err}`));
5440
5548
  await this.writeTombstone(id);
5441
5549
  }
5442
5550
  async delete(id) {
@@ -6252,39 +6360,41 @@ ${line}`;
6252
6360
  }
6253
6361
  async forget(scope, query, filePath) {
6254
6362
  const file = this.resolveFile(filePath, scope);
6255
- let existing;
6256
- try {
6257
- existing = await fsp3.readFile(file, "utf8");
6258
- } catch {
6259
- return 0;
6260
- }
6261
- const needle = query.toLowerCase();
6262
- const idMatcher = /mem_\d+_\w+/;
6263
- let removed = 0;
6264
- const lines = existing.split("\n").filter((line) => {
6265
- const trimmed = line.trim();
6266
- if (!trimmed.startsWith("- ")) return true;
6267
- if (idMatcher.test(query)) {
6268
- const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
6269
- if (entryIdMatch && entryIdMatch[0] === query) {
6363
+ return withFileLock(file, async () => {
6364
+ let existing;
6365
+ try {
6366
+ existing = await fsp3.readFile(file, "utf8");
6367
+ } catch {
6368
+ return 0;
6369
+ }
6370
+ const needle = query.toLowerCase();
6371
+ const idMatcher = /mem_\d+_\w+/;
6372
+ let removed = 0;
6373
+ const lines = existing.split("\n").filter((line) => {
6374
+ const trimmed = line.trim();
6375
+ if (!trimmed.startsWith("- ")) return true;
6376
+ if (idMatcher.test(query)) {
6377
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
6378
+ if (entryIdMatch && entryIdMatch[0] === query) {
6379
+ removed++;
6380
+ return false;
6381
+ }
6382
+ }
6383
+ if (trimmed.toLowerCase().includes(needle)) {
6270
6384
  removed++;
6271
6385
  return false;
6272
6386
  }
6387
+ return true;
6388
+ });
6389
+ if (removed > 0) {
6390
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
6391
+ await atomicWrite(file, "");
6392
+ } else {
6393
+ await atomicWrite(file, lines.join("\n"));
6394
+ }
6273
6395
  }
6274
- if (trimmed.toLowerCase().includes(needle)) {
6275
- removed++;
6276
- return false;
6277
- }
6278
- return true;
6396
+ return removed;
6279
6397
  });
6280
- if (removed > 0) {
6281
- if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
6282
- await atomicWrite(file, "");
6283
- } else {
6284
- await atomicWrite(file, lines.join("\n"));
6285
- }
6286
- }
6287
- return removed;
6288
6398
  }
6289
6399
  async readAll(scope, filePath) {
6290
6400
  const file = this.resolveFile(filePath, scope);
@@ -6724,48 +6834,6 @@ function deepFreeze(obj) {
6724
6834
  }
6725
6835
  return Object.freeze(obj);
6726
6836
  }
6727
-
6728
- // src/security/config-secrets.ts
6729
- function decryptConfigSecrets2(cfg, vault) {
6730
- return walk3(cfg, vault, (v, key) => {
6731
- try {
6732
- return vault.decrypt(v);
6733
- } catch (err) {
6734
- console.warn(
6735
- `[secret-vault] Failed to decrypt "${key}":`,
6736
- err instanceof Error ? err.message : err
6737
- );
6738
- return "";
6739
- }
6740
- });
6741
- }
6742
- function walk3(node, vault, transform) {
6743
- if (node === null || node === void 0) return node;
6744
- if (typeof node !== "object") return node;
6745
- if (Array.isArray(node)) {
6746
- return node.map((item) => walk3(item, vault, transform));
6747
- }
6748
- const out = /* @__PURE__ */ Object.create(null);
6749
- for (const [k, v] of Object.entries(node)) {
6750
- if (typeof v === "string" && isSecretField2(k)) {
6751
- out[k] = transform(v, k);
6752
- } else if (typeof v === "object" && v !== null) {
6753
- out[k] = walk3(v, vault, transform);
6754
- } else {
6755
- out[k] = v;
6756
- }
6757
- }
6758
- return out;
6759
- }
6760
- var SECRET_KEY_PATTERN2 = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key)/i;
6761
- var NON_SECRET_OVERRIDES2 = /* @__PURE__ */ new Set(["publickey", "public_key"]);
6762
- function isSecretField2(name) {
6763
- const lc = name.toLowerCase();
6764
- if (NON_SECRET_OVERRIDES2.has(lc)) return false;
6765
- return SECRET_KEY_PATTERN2.test(lc);
6766
- }
6767
-
6768
- // src/storage/config-loader.ts
6769
6837
  init_atomic_write();
6770
6838
  var BEHAVIOR_DEFAULTS = {
6771
6839
  version: 1,
@@ -6904,12 +6972,14 @@ var DefaultConfigLoader = class {
6904
6972
  }
6905
6973
  async load(opts = {}) {
6906
6974
  let cfg = { ...BEHAVIOR_DEFAULTS };
6907
- const [global, local] = await Promise.all([
6975
+ const [global, local, inProject] = await Promise.all([
6908
6976
  this.readJson(this.paths.globalConfig),
6909
- this.readJson(this.paths.projectLocalConfig)
6977
+ this.readJson(this.paths.projectLocalConfig),
6978
+ this.readJson(this.paths.inProjectConfig)
6910
6979
  ]);
6911
6980
  cfg = deepMerge2(cfg, global);
6912
6981
  cfg = deepMerge2(cfg, local);
6982
+ cfg = deepMerge2(cfg, inProject);
6913
6983
  for (const [key, fn] of Object.entries(ENV_MAP)) {
6914
6984
  const v = process.env[key];
6915
6985
  if (v) fn(cfg, v);
@@ -6933,7 +7003,7 @@ var DefaultConfigLoader = class {
6933
7003
  cfg = deepMerge2(cfg, opts.cliFlags);
6934
7004
  }
6935
7005
  if (this.vault) {
6936
- cfg = decryptConfigSecrets2(cfg, this.vault);
7006
+ cfg = decryptConfigSecrets(cfg, this.vault);
6937
7007
  }
6938
7008
  if (cfg.providers) {
6939
7009
  for (const pcfg of Object.values(cfg.providers)) {
@@ -6983,7 +7053,7 @@ var DefaultConfigLoader = class {
6983
7053
  const parsed = safeParse(raw);
6984
7054
  if (!parsed.ok || !parsed.value) return null;
6985
7055
  if (this.vault) {
6986
- const decrypted = decryptConfigSecrets2({ sync: parsed.value }, this.vault);
7056
+ const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
6987
7057
  return decrypted.sync ?? null;
6988
7058
  }
6989
7059
  return parsed.value;
@@ -8266,38 +8336,15 @@ var DefaultPermissionPolicy = class {
8266
8336
  }
8267
8337
  };
8268
8338
  var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
8269
- /**
8270
- * Legacy name-based denylist.
8271
- * @deprecated Prefer declaring `capabilities` on the Tool and using capability-based checks.
8272
- */
8273
- static LEGACY_NAME_DENY = /* @__PURE__ */ new Set([
8274
- "bash",
8275
- "write",
8276
- "edit",
8277
- "replace",
8278
- "scaffold",
8279
- "patch",
8280
- "install",
8281
- "exec"
8282
- ]);
8283
- // Note: hasDangerousCapabilityForSubagents is now the shared helper from capabilities.ts
8284
- // The old private method was removed in favor of the centralized utility.
8285
- /**
8286
- * Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
8287
- * unknown capability — they may wrap a shell or filesystem. They are
8288
- * fail-closed here: not auto-approved for subagents by default, so the
8289
- * leader must allow them explicitly per-spawn.
8290
- */
8291
8339
  static isMcpTool(name) {
8292
8340
  return name.startsWith("mcp__");
8293
8341
  }
8294
8342
  async evaluate(tool) {
8295
8343
  const hasDangerousCap = hasDangerousCapabilityForSubagents(tool);
8296
- const legacyNameBlock = _AutoApprovePermissionPolicy.LEGACY_NAME_DENY.has(tool.name);
8297
8344
  const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
8298
- const blocked = tool.permission === "deny" || hasDangerousCap || legacyNameBlock || isMcp;
8345
+ const blocked = tool.permission === "deny" || hasDangerousCap || isMcp;
8299
8346
  if (blocked) {
8300
- const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` : legacyNameBlock || isMcp ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny";
8347
+ const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` : isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny";
8301
8348
  return {
8302
8349
  permission: "deny",
8303
8350
  source: "subagent_guard",
@@ -8599,53 +8646,63 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
8599
8646
  const next = await iter.next();
8600
8647
  if (next.done) break;
8601
8648
  const ev = next.value;
8602
- switch (ev.type) {
8603
- case "message_start":
8604
- handleMessageStart(state, ev.model);
8605
- break;
8606
- case "content_block_start":
8607
- handleContentBlockStart(state, ev);
8608
- break;
8609
- case "content_block_stop":
8610
- handleContentBlockStop(state, ev);
8611
- break;
8612
- case "text_delta":
8613
- handleTextDelta(state, ev.text);
8614
- events.emit("provider.text_delta", { ctx, text: ev.text });
8615
- break;
8616
- case "tool_use_start": {
8617
- const idVal = ev.id;
8618
- const nameVal = ev.name;
8619
- handleToolUseStart(state, { id: idVal, name: nameVal });
8620
- const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
8621
- events.emit("provider.tool_use_start", emittedPayload);
8622
- break;
8623
- }
8624
- case "tool_use_input_delta":
8625
- handleToolUseInputDelta(state, ev);
8626
- break;
8627
- case "tool_use_stop": {
8628
- const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
8629
- handleToolUseStop(state, ev);
8630
- events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
8631
- break;
8649
+ try {
8650
+ switch (ev.type) {
8651
+ case "message_start":
8652
+ handleMessageStart(state, ev.model);
8653
+ break;
8654
+ case "content_block_start":
8655
+ handleContentBlockStart(state, ev);
8656
+ break;
8657
+ case "content_block_stop":
8658
+ handleContentBlockStop(state, ev);
8659
+ break;
8660
+ case "text_delta":
8661
+ handleTextDelta(state, ev.text);
8662
+ events.emit("provider.text_delta", { ctx, text: ev.text });
8663
+ break;
8664
+ case "tool_use_start": {
8665
+ const idVal = ev.id;
8666
+ const nameVal = ev.name;
8667
+ handleToolUseStart(state, { id: idVal, name: nameVal });
8668
+ const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
8669
+ events.emit("provider.tool_use_start", emittedPayload);
8670
+ break;
8671
+ }
8672
+ case "tool_use_input_delta":
8673
+ handleToolUseInputDelta(state, ev);
8674
+ break;
8675
+ case "tool_use_stop": {
8676
+ const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
8677
+ handleToolUseStop(state, ev);
8678
+ events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
8679
+ break;
8680
+ }
8681
+ case "thinking_start":
8682
+ handleThinkingStart(state, ev);
8683
+ break;
8684
+ case "thinking_delta":
8685
+ handleThinkingDelta(state, ev.text);
8686
+ events.emit("provider.thinking_delta", { ctx, text: ev.text });
8687
+ break;
8688
+ case "thinking_signature":
8689
+ handleThinkingSignature(state, ev.signature);
8690
+ break;
8691
+ case "thinking_stop":
8692
+ handleThinkingStop(state);
8693
+ break;
8694
+ case "message_stop":
8695
+ handleMessageStop(state, ev);
8696
+ break;
8697
+ default:
8698
+ break;
8632
8699
  }
8633
- case "thinking_start":
8634
- handleThinkingStart(state, ev);
8635
- break;
8636
- case "thinking_delta":
8637
- handleThinkingDelta(state, ev.text);
8638
- events.emit("provider.thinking_delta", { ctx, text: ev.text });
8639
- break;
8640
- case "thinking_signature":
8641
- handleThinkingSignature(state, ev.signature);
8642
- break;
8643
- case "thinking_stop":
8644
- handleThinkingStop(state);
8645
- break;
8646
- case "message_stop":
8647
- handleMessageStop(state, ev);
8648
- break;
8700
+ } catch (handlerErr) {
8701
+ events.emit("provider.stream_error", {
8702
+ ctx,
8703
+ eventType: ev.type,
8704
+ msg: handlerErr instanceof Error ? handlerErr.message : String(handlerErr)
8705
+ });
8649
8706
  }
8650
8707
  }
8651
8708
  } catch (err) {
@@ -8658,8 +8715,10 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
8658
8715
  try {
8659
8716
  let drainTimer = null;
8660
8717
  try {
8718
+ const drainPromise = Promise.resolve(iter.return?.()).catch(() => {
8719
+ });
8661
8720
  await Promise.race([
8662
- Promise.resolve(iter.return?.()),
8721
+ drainPromise,
8663
8722
  new Promise((resolve12) => {
8664
8723
  drainTimer = setTimeout(resolve12, STREAM_DRAIN_TIMEOUT_MS);
8665
8724
  })
@@ -8778,23 +8837,25 @@ var IntelligentCompactor = class {
8778
8837
  this.summarizerModel = opts.summarizerModel;
8779
8838
  }
8780
8839
  async compact(ctx, opts = {}) {
8781
- const beforeTokens = this.estimateTokens(ctx.messages);
8840
+ const beforeTokens = estimateMessages(ctx.messages);
8782
8841
  const beforeFull = this.estimateFullRequest(ctx);
8783
8842
  const reductions = [];
8784
8843
  const load = beforeFull / this.maxContext;
8785
8844
  const aggressive = load >= this.hardThreshold ? true : opts.aggressive ?? load >= this.softThreshold;
8786
- const saved1 = this.eliseOldToolResults(ctx);
8845
+ const saved1 = this.elide(ctx);
8787
8846
  if (saved1 > 0) reductions.push({ phase: "elision", saved: saved1 });
8847
+ let collapsedDigest;
8788
8848
  if (aggressive) {
8789
- const saved2 = await this.summarizeAncientTurns(ctx);
8790
- if (saved2 > 0) reductions.push({ phase: "summary", saved: saved2 });
8849
+ const phase2 = await this.summarizeAncientTurns(ctx);
8850
+ if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
8851
+ collapsedDigest = phase2.digest;
8791
8852
  } else if (load >= this.warnThreshold) {
8792
- const saved2 = this.lightweightCompact(ctx);
8853
+ const saved2 = this.elide(ctx);
8793
8854
  if (saved2 > 0) reductions.push({ phase: "elision", saved: saved2 });
8794
8855
  }
8795
8856
  const repaired = repairToolUseAdjacency(ctx.messages);
8796
8857
  if (repaired.report.changed) ctx.state.replaceMessages(repaired.messages);
8797
- const afterTokens = this.estimateTokens(ctx.messages);
8858
+ const afterTokens = estimateMessages(ctx.messages);
8798
8859
  const afterFull = this.estimateFullRequest(ctx);
8799
8860
  return {
8800
8861
  before: beforeTokens,
@@ -8802,6 +8863,7 @@ var IntelligentCompactor = class {
8802
8863
  fullRequestTokensBefore: beforeFull,
8803
8864
  fullRequestTokensAfter: afterFull,
8804
8865
  reductions,
8866
+ collapsedDigest,
8805
8867
  repaired: repaired.report.changed ? {
8806
8868
  removedToolUses: repaired.report.removedToolUses,
8807
8869
  removedToolResults: repaired.report.removedToolResults,
@@ -8814,75 +8876,39 @@ var IntelligentCompactor = class {
8814
8876
  * This is the accurate figure for context-window pressure monitoring.
8815
8877
  */
8816
8878
  estimateFullRequest(ctx) {
8817
- const breakdown = estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []);
8818
- return breakdown.total;
8879
+ return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
8880
+ }
8881
+ /** Run shared tool-result elision and commit through ConversationState. */
8882
+ elide(ctx) {
8883
+ const result = eliseOldToolResults(ctx.messages, {
8884
+ preserveK: this.preserveK,
8885
+ eliseThreshold: this.eliseThreshold
8886
+ });
8887
+ if (result.changed) ctx.state.replaceMessages(result.messages);
8888
+ return result.saved;
8819
8889
  }
8820
8890
  async summarizeAncientTurns(ctx) {
8821
8891
  const messages = ctx.messages;
8822
8892
  const cutoff = Math.max(0, messages.length - this.preserveK * 2);
8823
- if (cutoff <= 2) return 0;
8824
- const boundary = this.findSafeBoundary(messages, 0, cutoff);
8825
- if (boundary <= 1) return 0;
8893
+ if (cutoff <= 2) return { saved: 0 };
8894
+ const boundary = findSafeBoundary(messages, 0, cutoff);
8895
+ if (boundary <= 1) return { saved: 0 };
8826
8896
  const toSummarize = messages.slice(0, boundary);
8827
- const removedTokens = this.estimateTokens(toSummarize);
8897
+ const removedTokens = estimateMessages(toSummarize);
8828
8898
  let summaryText;
8829
8899
  try {
8830
8900
  summaryText = await this.callSummarizer(toSummarize, ctx);
8831
8901
  } catch {
8832
- const preservedMessages = [];
8833
- for (const m of toSummarize) {
8834
- if (m.role === "system") continue;
8835
- if (typeof m.content === "string") {
8836
- preservedMessages.push(m);
8837
- } else if (Array.isArray(m.content)) {
8838
- const textParts = m.content.filter(isTextBlock);
8839
- const toolParts = m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result");
8840
- const next = {
8841
- role: m.role,
8842
- content: [
8843
- ...textParts,
8844
- ...toolParts.length > 0 ? [{ type: "text", text: `[${toolParts.length} tool call(s) omitted]` }] : []
8845
- ]
8846
- };
8847
- preservedMessages.push(next);
8848
- }
8849
- }
8850
- summaryText = preservedMessages.map((m) => `[${m.role}]: ${typeof m.content === "string" ? m.content : m.content.filter(isTextBlock).map((b) => b.text).join(" ")}`).join("\n");
8851
- if (!summaryText) summaryText = `${toSummarize.length} earlier turns (semantic content preserved)`;
8902
+ summaryText = buildLosslessDigest(toSummarize) || `${toSummarize.length} earlier turns (semantic content preserved)`;
8852
8903
  }
8853
8904
  const summaryMsg = {
8854
8905
  role: "system",
8855
8906
  content: `[prior_turns_summary: ${summaryText}]`
8856
8907
  };
8857
- const summaryTokens = this.estimateTokens([summaryMsg]);
8908
+ const summaryTokens = estimateMessages([summaryMsg]);
8858
8909
  const tail = ctx.messages.slice(boundary);
8859
8910
  ctx.state.replaceMessages([summaryMsg, ...tail]);
8860
- return Math.max(0, removedTokens - summaryTokens);
8861
- }
8862
- findSafeBoundary(messages, from, to) {
8863
- for (let i = to; i >= from; i--) {
8864
- const m = messages[i];
8865
- if (!m) continue;
8866
- if (m.role === "user" && this.hasTextContent(m)) {
8867
- return this.findExchangeStart(messages, i);
8868
- }
8869
- }
8870
- return -1;
8871
- }
8872
- findExchangeStart(messages, userIndex) {
8873
- for (let i = userIndex - 1; i >= 0; i--) {
8874
- const m = messages[i];
8875
- if (!m) continue;
8876
- if (m.role === "assistant") {
8877
- const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
8878
- if (!hasToolUse2) {
8879
- return i + 1;
8880
- }
8881
- } else if (m.role !== "user") ; else {
8882
- return i;
8883
- }
8884
- }
8885
- return 0;
8911
+ return { saved: Math.max(0, removedTokens - summaryTokens), digest: summaryText };
8886
8912
  }
8887
8913
  async callSummarizer(messages, ctx) {
8888
8914
  const prompt = [
@@ -8917,86 +8943,6 @@ var IntelligentCompactor = class {
8917
8943
  }
8918
8944
  return [{ type: "text", text: lines.join("\n") }];
8919
8945
  }
8920
- lightweightCompact(ctx) {
8921
- return this.eliseOldToolResults(ctx);
8922
- }
8923
- eliseOldToolResults(ctx) {
8924
- const messages = ctx.messages;
8925
- let pairCount = 0;
8926
- let preserveStart = messages.length;
8927
- for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
8928
- const m = messages[i];
8929
- if (!m) continue;
8930
- if (m.role === "user" || m.role === "assistant") {
8931
- pairCount++;
8932
- preserveStart = i;
8933
- }
8934
- }
8935
- for (let i = preserveStart; i < messages.length; i++) {
8936
- const m = messages[i];
8937
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
8938
- const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
8939
- if (hasToolUse2 && i + 1 < messages.length) {
8940
- const next = messages[i + 1];
8941
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
8942
- preserveStart = i + 1;
8943
- }
8944
- }
8945
- }
8946
- let saved = 0;
8947
- let changed = false;
8948
- const nextMessages = new Array(messages.length);
8949
- for (let i = 0; i < messages.length; i++) {
8950
- const msg = messages[i];
8951
- if (i >= preserveStart) {
8952
- nextMessages[i] = msg;
8953
- continue;
8954
- }
8955
- if (!msg || !Array.isArray(msg.content)) {
8956
- nextMessages[i] = msg;
8957
- continue;
8958
- }
8959
- const newContent = msg.content.map((b) => {
8960
- if (b.type !== "tool_result") return b;
8961
- const tokens = estimateToolResultTokens(b.content);
8962
- if (tokens < this.eliseThreshold) return b;
8963
- saved += tokens;
8964
- return {
8965
- type: "tool_result",
8966
- tool_use_id: b.tool_use_id,
8967
- content: `[elided: ~${tokens} tokens]`,
8968
- is_error: b.is_error
8969
- };
8970
- });
8971
- if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
8972
- nextMessages[i] = msg;
8973
- } else {
8974
- nextMessages[i] = { ...msg, content: newContent };
8975
- changed = true;
8976
- }
8977
- }
8978
- if (changed) ctx.state.replaceMessages(nextMessages);
8979
- return saved;
8980
- }
8981
- hasTextContent(m) {
8982
- if (typeof m.content === "string") return m.content.trim().length > 0;
8983
- return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
8984
- }
8985
- estimateTokens(messages) {
8986
- let total = 0;
8987
- for (const m of messages) {
8988
- if (typeof m.content === "string") {
8989
- total += estimateTextTokens(m.content);
8990
- } else {
8991
- for (const b of m.content) {
8992
- if (b.type === "text") total += estimateTextTokens(b.text);
8993
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
8994
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
8995
- }
8996
- }
8997
- }
8998
- return total;
8999
- }
9000
8946
  };
9001
8947
 
9002
8948
  // src/models/llm-selector.ts
@@ -9022,20 +8968,6 @@ Rules:
9022
8968
  - If unsure, keep rather than collapse (errors are more costly than waste)
9023
8969
 
9024
8970
  Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
9025
- function estimateTokens(messages) {
9026
- let total = 0;
9027
- for (const m of messages) {
9028
- if (typeof m.content === "string") {
9029
- total += Math.ceil(m.content.length / 4);
9030
- } else if (Array.isArray(m.content)) {
9031
- for (const b of m.content) {
9032
- if (b.type === "text") total += Math.ceil(b.text.length / 4);
9033
- else total += Math.ceil(JSON.stringify(b).length / 4);
9034
- }
9035
- }
9036
- }
9037
- return total;
9038
- }
9039
8971
  function formatMessages(messages, maxChars = 8e3) {
9040
8972
  const lines = [];
9041
8973
  let used = 0;
@@ -9074,7 +9006,7 @@ var LLMSelector = class {
9074
9006
  async select(messages, maxToKeep) {
9075
9007
  const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
9076
9008
  const historyText = formatMessages(messages);
9077
- const totalTokens = estimateTokens(messages);
9009
+ const totalTokens = estimateMessageTokens(messages);
9078
9010
  const systemText = `${this.systemPrompt}
9079
9011
 
9080
9012
  Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
@@ -9106,10 +9038,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
9106
9038
  let startIdx = 0;
9107
9039
  for (let i = messages.length - 1; i >= 0; i--) {
9108
9040
  const m = expectDefined(messages[i]);
9109
- const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
9110
- (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
9111
- 0
9112
- );
9041
+ const cost = estimateMessageTokens([m]);
9113
9042
  if (tokenCount + cost <= budget) {
9114
9043
  tokenCount += cost;
9115
9044
  } else {
@@ -9342,52 +9271,12 @@ Summarize the following message range:`;
9342
9271
  return Math.floor(this.maxContext * 0.75);
9343
9272
  }
9344
9273
  eliseOldToolResults(ctx) {
9345
- const messages = ctx.messages;
9346
- let pairCount = 0;
9347
- let preserveStart = messages.length;
9348
- for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
9349
- const m = messages[i];
9350
- if (!m) continue;
9351
- if (m.role === "user" || m.role === "assistant") {
9352
- pairCount++;
9353
- preserveStart = i;
9354
- }
9355
- }
9356
- let saved = 0;
9357
- let changed = false;
9358
- const nextMessages = new Array(messages.length);
9359
- for (let i = 0; i < messages.length; i++) {
9360
- const msg = messages[i];
9361
- if (i >= preserveStart) {
9362
- nextMessages[i] = msg;
9363
- continue;
9364
- }
9365
- if (!msg || !Array.isArray(msg.content)) {
9366
- nextMessages[i] = msg;
9367
- continue;
9368
- }
9369
- const newContent = msg.content.map((b) => {
9370
- if (b.type !== "tool_result") return b;
9371
- const text = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
9372
- const tokens = this.roughTokenEstimate(text);
9373
- if (tokens < this.eliseThreshold) return b;
9374
- saved += tokens;
9375
- return {
9376
- type: "tool_result",
9377
- tool_use_id: b.tool_use_id,
9378
- content: `[elided: ~${tokens} tokens]`,
9379
- is_error: b.is_error
9380
- };
9381
- });
9382
- if (newContent.every((b, idx) => b === msg.content[idx])) {
9383
- nextMessages[i] = msg;
9384
- } else {
9385
- nextMessages[i] = { ...msg, content: newContent };
9386
- changed = true;
9387
- }
9388
- }
9389
- if (changed) ctx.state.replaceMessages(nextMessages);
9390
- return saved;
9274
+ const result = eliseOldToolResults(ctx.messages, {
9275
+ preserveK: this.preserveK,
9276
+ eliseThreshold: this.eliseThreshold
9277
+ });
9278
+ if (result.changed) ctx.state.replaceMessages(result.messages);
9279
+ return result.saved;
9391
9280
  }
9392
9281
  hasTextContent(m) {
9393
9282
  if (typeof m.content === "string") return m.content.trim().length > 0;
@@ -9417,8 +9306,80 @@ Summarize the following message range:`;
9417
9306
  }
9418
9307
  };
9419
9308
 
9309
+ // src/execution/strategy-compactor.ts
9310
+ function createStrategyCompactor(opts = {}) {
9311
+ const requested = opts.strategy ?? (opts.llmSelector ? "selective" : "hybrid");
9312
+ const strategy = requested;
9313
+ if (strategy === "intelligent" || strategy === "selective") {
9314
+ return new ProviderBackedCompactor(strategy, opts);
9315
+ }
9316
+ return new HybridCompactor({
9317
+ preserveK: opts.preserveK,
9318
+ eliseThreshold: opts.eliseThreshold
9319
+ });
9320
+ }
9321
+ var ProviderBackedCompactor = class {
9322
+ constructor(strategy, opts) {
9323
+ this.strategy = strategy;
9324
+ this.opts = opts;
9325
+ }
9326
+ strategy;
9327
+ opts;
9328
+ async compact(ctx, compactOpts = {}) {
9329
+ return this.resolveInner(ctx).compact(ctx, compactOpts);
9330
+ }
9331
+ /**
9332
+ * Construct the concrete compactor for this run. Rebuilt per call (cheap, no
9333
+ * I/O) so a model switch — which changes `ctx.provider.capabilities.maxContext`
9334
+ * — is always reflected. Reads the active ContextWindowPolicy from `ctx.meta`
9335
+ * so the LLM compactors honor the same thresholds/preserveK as the policy.
9336
+ */
9337
+ resolveInner(ctx) {
9338
+ const provider = ctx.provider;
9339
+ if (!provider) {
9340
+ return new HybridCompactor({
9341
+ preserveK: this.opts.preserveK,
9342
+ eliseThreshold: this.opts.eliseThreshold
9343
+ });
9344
+ }
9345
+ const policy = readPolicy(ctx);
9346
+ const maxContext = provider.capabilities?.maxContext || void 0;
9347
+ const thresholds = policy?.thresholds;
9348
+ const common = {
9349
+ provider,
9350
+ maxContext,
9351
+ preserveK: this.opts.preserveK ?? policy?.preserveK,
9352
+ eliseThreshold: this.opts.eliseThreshold ?? policy?.eliseThreshold,
9353
+ ...thresholds ? { warnThreshold: thresholds.warn, softThreshold: thresholds.soft, hardThreshold: thresholds.hard } : {}
9354
+ };
9355
+ if (this.strategy === "selective") {
9356
+ return new SelectiveCompactor({
9357
+ ...common,
9358
+ selectorModel: this.opts.summarizerModel,
9359
+ summarizerModel: this.opts.summarizerModel
9360
+ });
9361
+ }
9362
+ return new IntelligentCompactor({
9363
+ ...common,
9364
+ summarizerModel: this.opts.summarizerModel
9365
+ });
9366
+ }
9367
+ };
9368
+ function readPolicy(ctx) {
9369
+ const policy = ctx.meta?.["contextWindowPolicy"];
9370
+ if (!policy || typeof policy !== "object") return null;
9371
+ const candidate = policy;
9372
+ if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
9373
+ return candidate;
9374
+ }
9375
+
9420
9376
  // src/execution/auto-compaction-middleware.ts
9421
9377
  var LEVEL_RANK2 = { warn: 0, soft: 1, hard: 2 };
9378
+ var MAX_DIGEST_LOG_CHARS = 4e3;
9379
+ function truncateDigest(digest) {
9380
+ if (digest.length <= MAX_DIGEST_LOG_CHARS) return digest;
9381
+ return `${digest.slice(0, MAX_DIGEST_LOG_CHARS)}\u2026 [+${digest.length - MAX_DIGEST_LOG_CHARS} chars; full turns in session log]`;
9382
+ }
9422
9383
  var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9423
9384
  name = "AutoCompaction";
9424
9385
  compactor;
@@ -9479,7 +9440,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9479
9440
  }
9480
9441
  handler() {
9481
9442
  return async (ctx, next) => {
9482
- const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
9443
+ const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(
9444
+ ctx.messages,
9445
+ ctx.systemPrompt,
9446
+ ctx.tools ?? [],
9447
+ `${ctx.provider?.id ?? "unknown"}/${ctx.model}`
9448
+ ).total;
9483
9449
  const load = tokens / this._maxContext;
9484
9450
  const policy = this.policyProvider?.(ctx);
9485
9451
  const thresholds = policy?.thresholds ?? {
@@ -9541,7 +9507,11 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9541
9507
  after: report.after,
9542
9508
  level: pressure.level,
9543
9509
  aggressive,
9544
- reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved }))
9510
+ reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
9511
+ // Record what was collapsed so the audit trail shows the preserved
9512
+ // content, not just token counts. Bounded to keep the log line small;
9513
+ // the full original turns are already in the session JSONL.
9514
+ ...report.collapsedDigest ? { digest: truncateDigest(report.collapsedDigest) } : {}
9545
9515
  });
9546
9516
  ctx.clearFileTracking();
9547
9517
  } catch (err) {
@@ -9626,6 +9596,12 @@ var DoneConditionChecker = class {
9626
9596
  return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
9627
9597
  }
9628
9598
  break;
9599
+ case "all_tasks_done":
9600
+ break;
9601
+ case "custom":
9602
+ break;
9603
+ default:
9604
+ return assertNever(this.condition.type);
9629
9605
  }
9630
9606
  return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
9631
9607
  }
@@ -9748,16 +9724,20 @@ async function loadGoal(filePath) {
9748
9724
  let raw;
9749
9725
  try {
9750
9726
  raw = await fsp3.readFile(filePath, "utf8");
9751
- } catch {
9752
- return null;
9727
+ } catch (err) {
9728
+ const code = err.code;
9729
+ if (code === "ENOENT") return null;
9730
+ throw err;
9753
9731
  }
9754
9732
  try {
9755
9733
  const parsed = JSON.parse(raw);
9756
9734
  if (parsed?.version !== 1 || typeof parsed.goal !== "string" || !Array.isArray(parsed.journal)) {
9735
+ console.warn(`[goal-store] Corrupt goal.json at ${filePath} \u2014 invalid schema. Consider deleting it and re-creating.`);
9757
9736
  return null;
9758
9737
  }
9759
9738
  return parsed;
9760
9739
  } catch {
9740
+ console.warn(`[goal-store] Corrupt goal.json at ${filePath} \u2014 JSON parse failed. Consider deleting it and re-creating.`);
9761
9741
  return null;
9762
9742
  }
9763
9743
  }
@@ -9884,9 +9864,9 @@ function parseProgressFromText(text) {
9884
9864
  const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
9885
9865
  const m = text.match(re);
9886
9866
  if (!m) return null;
9887
- const progress = Math.min(100, Math.max(0, Number.parseInt(m[1], 10)));
9867
+ const progress = Math.min(100, Math.max(0, Number.parseInt(m[1] ?? "0", 10)));
9888
9868
  const note = m[2]?.trim() || void 0;
9889
- return { progress, note };
9869
+ return note === void 0 ? { progress } : { progress, note };
9890
9870
  }
9891
9871
  function recordProgress(goal, progress, note) {
9892
9872
  const clamped = Math.min(100, Math.max(0, progress));
@@ -9906,7 +9886,7 @@ function computeTrend(history) {
9906
9886
  const recent = history.slice(-5);
9907
9887
  const deltas = [];
9908
9888
  for (let i = 1; i < recent.length; i++) {
9909
- deltas.push(recent[i].progress - recent[i - 1].progress);
9889
+ deltas.push((recent[i]?.progress ?? 0) - (recent[i - 1]?.progress ?? 0));
9910
9890
  }
9911
9891
  if (deltas.length < 2) return void 0;
9912
9892
  const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
@@ -10480,6 +10460,7 @@ ${recentJournal}` : "No prior iterations.",
10480
10460
  note: note.slice(0, 240)
10481
10461
  });
10482
10462
  await saveGoal(this.goalPath, withEntry);
10463
+ this.opts.onEternalStop?.();
10483
10464
  }
10484
10465
  /**
10485
10466
  * Manually clear the goal — equivalent to `/goal clear` typed by the user.
@@ -10493,8 +10474,8 @@ ${recentJournal}` : "No prior iterations.",
10493
10474
  await saveGoal(this.goalPath, abandoned);
10494
10475
  }
10495
10476
  try {
10496
- const { unlink: unlink14 } = await import('fs/promises');
10497
- await unlink14(this.goalPath);
10477
+ const { unlink: unlink15 } = await import('fs/promises');
10478
+ await unlink15(this.goalPath);
10498
10479
  } catch {
10499
10480
  }
10500
10481
  this.opts.onEternalStop?.();
@@ -11495,6 +11476,53 @@ Working rules:
11495
11476
  "flaws"
11496
11477
  ]
11497
11478
  }
11479
+ },
11480
+ {
11481
+ config: {
11482
+ id: "refactor-planner",
11483
+ name: "Refactor Planner",
11484
+ role: "refactor-planner",
11485
+ tools: [...PLAN_TOOLS, "diff"],
11486
+ prompt: `You are the Refactor Planner agent. Your job is to analyze code
11487
+ structure and produce a concrete, phased refactoring plan with risk
11488
+ assessment, dependency ordering, and rollback strategy.
11489
+
11490
+ Scope:
11491
+ - Map module-level dependencies (import graph)
11492
+ - Identify coupling hotspots (high fan-in/out modules)
11493
+ - Assess refactoring risk by complexity and test coverage
11494
+ - Generate phased plans with checkpoint milestones
11495
+ - Produce diff-friendly task lists (one task = one concern)
11496
+
11497
+ Input format you accept:
11498
+ { "task": "plan | assess | roadmap", "target": "src/core", "constraint": "no-breaking-changes | minimal-downtime | full-rewrite", "focus": "architecture | performance | maintainability" }
11499
+
11500
+ Output: Markdown refactor plan with phases (Low Risk / Medium Risk / High Risk),
11501
+ dependency graph, rollback strategy, and exit criteria.
11502
+
11503
+ Working rules:
11504
+ - Always include rollback strategy \u2014 every refactor can fail
11505
+ - Merge tasks that take <1h into a single phase
11506
+ - Respect team constraints (reviewer availability, parallelization)
11507
+ - Never plan without analyzing the actual code first`
11508
+ },
11509
+ budget: HEAVY_BUDGET,
11510
+ capability: {
11511
+ phase: "planning",
11512
+ summary: "Refactoring planner: analyzes code structure, maps dependencies, produces risk-scored phased plans with rollback strategy.",
11513
+ keywords: [
11514
+ "refactor",
11515
+ "refactoring",
11516
+ "restructure",
11517
+ "debt",
11518
+ "technical debt",
11519
+ "clean up",
11520
+ "modularize",
11521
+ "decouple",
11522
+ "dependency graph",
11523
+ "code structure"
11524
+ ]
11525
+ }
11498
11526
  }
11499
11527
  ];
11500
11528
 
@@ -12034,6 +12062,157 @@ Working rules:
12034
12062
  "robustness"
12035
12063
  ]
12036
12064
  }
12065
+ },
12066
+ {
12067
+ config: {
12068
+ id: "security-scanner",
12069
+ name: "Security Scanner",
12070
+ role: "security-scanner",
12071
+ tools: [...TOOLS.inspect],
12072
+ prompt: `You are the Security Scanner agent. Your job is to scan code,
12073
+ configs, and dependencies for security issues from hardcoded secrets to
12074
+ supply chain risks.
12075
+
12076
+ Scope:
12077
+ - Detect hardcoded secrets: API keys, tokens, passwords, private keys
12078
+ - Find injection vectors: eval, innerHTML, SQL concat, shell injection
12079
+ - Identify insecure patterns: weak crypto, hardcoded IVs, disabled TLS
12080
+ - Scan dependencies for known CVEs (via npm/pnpm audit)
12081
+ - Flag supply chain risks: postinstall hooks, unverified scripts
12082
+
12083
+ Input format you accept:
12084
+ { "task": "scan | audit | secrets | dependencies", "paths": ["src", "config"], "depth": "quick | normal | deep" }
12085
+
12086
+ Output: Markdown security report with severity-ranked findings, injection
12087
+ vectors, dependency issues, and a remediation checklist.
12088
+
12089
+ Working rules:
12090
+ - Never scan node_modules \u2014 use npm audit instead
12091
+ - Always provide remediation steps, not just findings
12092
+ - Verify regex-based secrets before flagging (false positive risk)
12093
+ - When in doubt, flag as medium rather than ignoring potential issues`
12094
+ },
12095
+ budget: HEAVY_BUDGET,
12096
+ capability: {
12097
+ phase: "verify",
12098
+ summary: "Security scanner: detects hardcoded secrets, injection vectors, insecure patterns, and supply-chain risks with remediation.",
12099
+ keywords: [
12100
+ "security",
12101
+ "scan",
12102
+ "vulnerability",
12103
+ "secret",
12104
+ "api key",
12105
+ "hardcoded",
12106
+ "injection",
12107
+ "cve",
12108
+ "audit dependencies",
12109
+ "supply chain",
12110
+ "xss",
12111
+ "sqli",
12112
+ "shell injection",
12113
+ "sensitive data",
12114
+ "credential"
12115
+ ]
12116
+ }
12117
+ },
12118
+ {
12119
+ config: {
12120
+ id: "bug-hunter",
12121
+ name: "Bug Hunter",
12122
+ role: "bug-hunter",
12123
+ tools: [...TOOLS.inspect],
12124
+ prompt: `You are the Bug Hunter agent. Your job is to systematically scan
12125
+ source code for bugs, anti-patterns, and code smells using pattern matching
12126
+ and heuristics. Output a prioritized hit list with file:line references.
12127
+
12128
+ Scope:
12129
+ - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
12130
+ - Identify anti-patterns (callback hell, God objects, circular deps)
12131
+ - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
12132
+ - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
12133
+ - Rank findings: critical > high > medium > low
12134
+
12135
+ Input format you accept:
12136
+ { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
12137
+
12138
+ Output: Markdown bug hunt report with critically/high/medium/low sections.
12139
+ Each entry: **[TYPE]** \`file:line\` \u2014 description + suggested fix
12140
+
12141
+ Working rules:
12142
+ - Never scan node_modules \u2014 it's noise
12143
+ - Always include file:line for every finding
12144
+ - If >30% of findings are false positives, note the confidence level
12145
+ - Ask director for clarification if paths are ambiguous`
12146
+ },
12147
+ budget: HEAVY_BUDGET,
12148
+ capability: {
12149
+ phase: "verify",
12150
+ summary: "Bug hunter: scans source code for bugs, anti-patterns, and code smells, producing a file:line-ranked hit list with fixes.",
12151
+ keywords: [
12152
+ "bug",
12153
+ "hunt",
12154
+ "scan",
12155
+ "code smell",
12156
+ "anti-pattern",
12157
+ "race condition",
12158
+ "memory leak",
12159
+ "null deref",
12160
+ "type safety",
12161
+ "unhandled error",
12162
+ "find bugs",
12163
+ "audit code",
12164
+ "code quality"
12165
+ ]
12166
+ }
12167
+ },
12168
+ {
12169
+ config: {
12170
+ id: "audit-log",
12171
+ name: "Audit Log",
12172
+ role: "audit-log",
12173
+ tools: [...TOOLS.inspect],
12174
+ prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
12175
+ session logs and produce actionable markdown reports.
12176
+
12177
+ Scope:
12178
+ - Parse session logs (iteration counts, tool calls, errors, usage)
12179
+ - Detect repeated failure patterns across multiple runs
12180
+ - Identify tool usage anomalies (over-use, failures, unexpected chains)
12181
+ - Track token consumption trends
12182
+ - Generate structured audit reports with severity ratings
12183
+
12184
+ Input format you accept:
12185
+ { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
12186
+
12187
+ Output: Markdown audit report with Summary, Top Errors, Tool Usage table,
12188
+ Anomalies, and Cost Trend sections.
12189
+
12190
+ Working rules:
12191
+ - Never fabricate numbers \u2014 read the actual logs first
12192
+ - Always include file:line references for errors
12193
+ - If sessionPath is missing, ask the director to provide it
12194
+ - Report confidence level: high (>90% accuracy), medium, low`
12195
+ },
12196
+ budget: MEDIUM_BUDGET,
12197
+ capability: {
12198
+ phase: "verify",
12199
+ summary: "Audit log analyzer: parses session JSONL, detects failure patterns, tool anomalies, and cost trends with structured reports.",
12200
+ keywords: [
12201
+ "audit",
12202
+ "log",
12203
+ "logs",
12204
+ "session",
12205
+ "trace",
12206
+ "analyze logs",
12207
+ "error patterns",
12208
+ "cost analysis",
12209
+ "tool usage",
12210
+ "token usage",
12211
+ "post-mortem",
12212
+ "trend",
12213
+ "anomaly"
12214
+ ]
12215
+ }
12037
12216
  }
12038
12217
  ];
12039
12218
 
@@ -16811,7 +16990,10 @@ var Director = class _Director {
16811
16990
  workCompleteFlag = false;
16812
16991
  /** Pending /btw notes stashed by the leader agent (see setLeaderBtwNote). */
16813
16992
  _leaderBtwNotes = [];
16814
- /** Active collab sessions tracked by sessionId (see spawnCollab). */
16993
+ /** Active collab sessions tracked by sessionId (see spawnCollab).
16994
+ * The tuple holds the session and its Director-registered listener unsubs.
16995
+ * Calling the unsubs on cancel/premature-cleanup prevents listener accumulation
16996
+ * on CollabSession (EventEmitter) across many spawnCollab() calls. */
16815
16997
  _activeCollabSessions = /* @__PURE__ */ new Map();
16816
16998
  /** Prevents large `ask_subagent` answers from bloating the leader's context window. */
16817
16999
  largeAnswerStore;
@@ -17141,11 +17323,13 @@ var Director = class _Director {
17141
17323
  * The CollabDebugReport will reflect 'cancelled' disposition when awaited.
17142
17324
  */
17143
17325
  cancelCollabSession(sessionId, reason = "Director cancelled") {
17144
- const session = this._activeCollabSessions.get(sessionId);
17145
- if (!session || session.isCancelled()) return;
17146
- session.cancel(reason);
17147
- for (const [_role, subagentId] of session.getSubagentIds()) {
17148
- this.coordinator.stop(subagentId).catch(() => {
17326
+ const entry = this._activeCollabSessions.get(sessionId);
17327
+ if (!entry || entry.session.isCancelled()) return;
17328
+ for (const unsub of entry.unsubs) unsub();
17329
+ entry.session.cancel(reason);
17330
+ for (const [_role, subagentId] of entry.session.getSubagentIds()) {
17331
+ this.coordinator.stop(subagentId).catch((err) => {
17332
+ console.debug(`[director] stop subagent ${subagentId} failed (may have already completed): ${err}`);
17149
17333
  });
17150
17334
  }
17151
17335
  }
@@ -17793,9 +17977,15 @@ var Director = class _Director {
17793
17977
  return options.onBudgetWarning?.(alert) ?? "ignore";
17794
17978
  }
17795
17979
  });
17796
- this._activeCollabSessions.set(session.sessionId, session);
17797
- session.on("session.done", () => this._activeCollabSessions.delete(session.sessionId));
17798
- session.on("session.error", () => this._activeCollabSessions.delete(session.sessionId));
17980
+ const doneHandler = () => this._activeCollabSessions.delete(session.sessionId);
17981
+ const errorHandler = () => this._activeCollabSessions.delete(session.sessionId);
17982
+ session.on("session.done", doneHandler);
17983
+ session.on("session.error", errorHandler);
17984
+ const unsubs = [
17985
+ () => session.off("session.done", doneHandler),
17986
+ () => session.off("session.error", errorHandler)
17987
+ ];
17988
+ this._activeCollabSessions.set(session.sessionId, { session, unsubs });
17799
17989
  return session.start();
17800
17990
  }
17801
17991
  /**
@@ -20563,6 +20753,8 @@ var SpecVersioning = class {
20563
20753
  switch (type) {
20564
20754
  case "functional":
20565
20755
  return "feature";
20756
+ case "non-functional":
20757
+ return "feature";
20566
20758
  case "security":
20567
20759
  return "feature";
20568
20760
  case "performance":
@@ -20570,7 +20762,7 @@ var SpecVersioning = class {
20570
20762
  case "ux":
20571
20763
  return "feature";
20572
20764
  default:
20573
- return "feature";
20765
+ return assertNever(type);
20574
20766
  }
20575
20767
  }
20576
20768
  };
@@ -21721,27 +21913,13 @@ function startOtlpTraceExporter(opts) {
21721
21913
  // src/infrastructure/context-manager.ts
21722
21914
  var CONTEXT_MANAGER_TOOL_NAME = "context_manager";
21723
21915
  function roughEstimate(messages) {
21724
- let total = 0;
21725
- for (const m of messages) {
21726
- if (typeof m.content === "string") {
21727
- total += Math.ceil(m.content.length / 4);
21728
- } else if (Array.isArray(m.content)) {
21729
- for (const b of m.content) {
21730
- if (b.type === "text") total += Math.ceil(b.text.length / 4);
21731
- else if (b.type === "tool_use" || b.type === "tool_result") {
21732
- total += Math.ceil(JSON.stringify(b).length / 4);
21733
- }
21734
- }
21735
- }
21736
- }
21737
- return total;
21916
+ return estimateMessageTokens(messages);
21738
21917
  }
21739
21918
  function createContextManagerTool(opts = {}) {
21740
21919
  const minCompactThreshold = opts.minCompactThreshold ?? 0;
21741
21920
  const noopRetryDeltaTokens = opts.noopRetryDeltaTokens ?? 2e3;
21742
- const maxContext = opts.maxContext ?? 128e3;
21921
+ const configuredMaxContext = opts.maxContext;
21743
21922
  const compactThresholdFraction = opts.compactThresholdFraction ?? 0.5;
21744
- const effectiveThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(maxContext * compactThresholdFraction);
21745
21923
  let lastNoopTokens = 0;
21746
21924
  return {
21747
21925
  name: CONTEXT_MANAGER_TOOL_NAME,
@@ -21835,6 +22013,8 @@ function createContextManagerTool(opts = {}) {
21835
22013
  }
21836
22014
  const fullEstimate = input.systemPrompt != null && Array.isArray(input.tools) ? estimateRequestTokens(messages, input.systemPrompt, input.tools) : { total: beforeTokens};
21837
22015
  const currentTokens = fullEstimate.total;
22016
+ const runtimeMaxContext = configuredMaxContext ?? ctx.provider?.capabilities?.maxContext ?? 128e3;
22017
+ const runtimeThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(runtimeMaxContext * compactThresholdFraction);
21838
22018
  if (lastNoopTokens > 0) {
21839
22019
  const delta = currentTokens - lastNoopTokens;
21840
22020
  if (delta < noopRetryDeltaTokens) {
@@ -21847,13 +22027,13 @@ function createContextManagerTool(opts = {}) {
21847
22027
  };
21848
22028
  }
21849
22029
  }
21850
- if (currentTokens < effectiveThreshold) {
22030
+ if (runtimeThreshold > 0 && currentTokens < runtimeThreshold) {
21851
22031
  return {
21852
22032
  action: "compact",
21853
22033
  beforeTokens,
21854
22034
  afterTokens: beforeTokens,
21855
22035
  messageCount: messages.length,
21856
- notes: `Context tokens (${currentTokens}) below compact threshold (${effectiveThreshold}). Skipping.`
22036
+ notes: `Context tokens (${currentTokens}) below compact threshold (${runtimeThreshold}, based on provider maxContext ${runtimeMaxContext}). Skipping.`
21857
22037
  };
21858
22038
  }
21859
22039
  const report = await opts.compactor.compact(ctx);
@@ -22916,7 +23096,7 @@ var SessionMemoryConsolidator = class {
22916
23096
  for (const op of parsed.operations) {
22917
23097
  switch (op.action) {
22918
23098
  case "add": {
22919
- if (op.text && op.text.trim()) {
23099
+ if (op.text?.trim()) {
22920
23100
  await this.memoryStore.remember(op.text.trim(), void 0, {
22921
23101
  type: op.type,
22922
23102
  tags: op.tags,
@@ -27212,13 +27392,27 @@ function requestLimitExtension(opts) {
27212
27392
  function toError(err) {
27213
27393
  return err instanceof Error ? err : new Error(String(err));
27214
27394
  }
27395
+ function signalAbortReason(signal) {
27396
+ const r = signal.reason;
27397
+ if (r instanceof Error) return r.message || r.name;
27398
+ if (typeof r === "string" && r.length > 0) return r;
27399
+ return "aborted";
27400
+ }
27215
27401
  function createAgentLoopHandler(a, handlers) {
27216
27402
  async function compactContextIfNeeded() {
27217
27403
  await a.pipelines.contextWindow.run(a.ctx);
27218
27404
  }
27405
+ const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
27219
27406
  function emitContextPct() {
27220
- const maxContext = a.ctx.provider.capabilities.maxContext ?? 2e5;
27221
- const { total } = estimateRequestTokens(a.ctx.messages, a.ctx.systemPrompt, a.ctx.tools ?? []);
27407
+ const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
27408
+ const providerMax = a.ctx.provider.capabilities.maxContext;
27409
+ const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : 2e5;
27410
+ const { total } = estimateRequestTokensCalibrated(
27411
+ a.ctx.messages,
27412
+ a.ctx.systemPrompt,
27413
+ a.ctx.tools ?? [],
27414
+ calibrationKey()
27415
+ );
27222
27416
  a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
27223
27417
  }
27224
27418
  function injectPendingBtwNotes() {
@@ -27297,7 +27491,7 @@ function createAgentLoopHandler(a, handlers) {
27297
27491
  for (let i = 0; ; i++) {
27298
27492
  iterations = i + 1;
27299
27493
  if (controller.signal.aborted) {
27300
- return { status: "aborted", iterations };
27494
+ return { status: "aborted", iterations, abortReason: signalAbortReason(controller.signal) };
27301
27495
  }
27302
27496
  await a.ctx.session.writeInFlightMarker(`iteration ${i} / max ${a.maxIterations}`).catch((err) => {
27303
27497
  (a.logger.debug ?? a.logger.warn)?.(
@@ -27334,13 +27528,14 @@ function createAgentLoopHandler(a, handlers) {
27334
27528
  let res;
27335
27529
  try {
27336
27530
  res = await customRunner(a.ctx, req);
27337
- const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? []).total;
27338
- recordActualUsage(res.usage.input, calibratedEstimate);
27531
+ const key = calibrationKey(req.model);
27532
+ const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? [], key).total;
27533
+ recordActualUsage(res.usage.input, calibratedEstimate, key);
27339
27534
  recoveryRetries = 0;
27340
27535
  } catch (err) {
27341
27536
  if (controller.signal.aborted) {
27342
27537
  a.events.emit("error", { err: toError(err), phase: "provider" });
27343
- return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
27538
+ return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED"), abortReason: signalAbortReason(controller.signal) };
27344
27539
  }
27345
27540
  const extDecision = await a.extensions.runOnError(a.ctx, err, "provider", i);
27346
27541
  if (extDecision) {
@@ -27388,7 +27583,7 @@ function createAgentLoopHandler(a, handlers) {
27388
27583
  }
27389
27584
  const responseResult = await handlers.response.processResponse(res, req);
27390
27585
  if (responseResult.aborted) {
27391
- return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries };
27586
+ return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries, abortReason: signalAbortReason(controller.signal) };
27392
27587
  }
27393
27588
  if (responseResult.done) {
27394
27589
  return { status: "done", iterations, finalText: responseResult.finalText, delegateSummaries };
@@ -27408,7 +27603,14 @@ function createAgentLoopHandler(a, handlers) {
27408
27603
  }
27409
27604
  return { status: "done", iterations, finalText, delegateSummaries };
27410
27605
  }
27411
- await handlers.tools.executeTools(toolUses);
27606
+ try {
27607
+ await handlers.tools.executeTools(toolUses);
27608
+ } catch (toolErr) {
27609
+ if (controller.signal.aborted) {
27610
+ return { status: "aborted", iterations, finalText, delegateSummaries, abortReason: signalAbortReason(controller.signal) };
27611
+ }
27612
+ throw toolErr;
27613
+ }
27412
27614
  if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
27413
27615
  emitContextPct();
27414
27616
  a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
@@ -27575,7 +27777,8 @@ var Agent = class {
27575
27777
  const result = {
27576
27778
  status: signal.aborted ? "aborted" : "failed",
27577
27779
  iterations: 0,
27578
- error: wse
27780
+ error: wse,
27781
+ abortReason: signal.aborted ? signalAbortReason(signal) : void 0
27579
27782
  };
27580
27783
  await this.extensions.runAfterRun(this.ctx, result);
27581
27784
  return result;
@@ -29900,8 +30103,7 @@ var PhaseOrchestrator = class {
29900
30103
  /** Wait for all pending phase merges, dependency-ordered and globally serialized. */
29901
30104
  async drainMerges() {
29902
30105
  await Promise.allSettled([...this.phaseMergePromise.values()]);
29903
- await this.mergeQueue.catch(() => {
29904
- });
30106
+ await this.mergeQueue.catch((err) => console.warn(`[phase-orchestrator] mergeQueue failed: ${err}`));
29905
30107
  }
29906
30108
  /** Pause: active phases continue, but no new phase starts. */
29907
30109
  pause() {
@@ -31637,6 +31839,11 @@ var DefaultBrainArbiter = class {
31637
31839
  options: request.options,
31638
31840
  rationale: "Decision requires human authority or lacks a safe automatic option."
31639
31841
  };
31842
+ default:
31843
+ return {
31844
+ type: "deny",
31845
+ reason: `Brain could not safely decide (unknown fallback: ${String(request.fallback)}): ${request.question}`
31846
+ };
31640
31847
  }
31641
31848
  }
31642
31849
  };
@@ -32943,6 +33150,6 @@ ${formatPlan(updated)}`
32943
33150
  };
32944
33151
  }
32945
33152
 
32946
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
33153
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertNever, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateMessageTokens, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, zaiVisionServer };
32947
33154
  //# sourceMappingURL=index.js.map
32948
33155
  //# sourceMappingURL=index.js.map