@wrongstack/core 0.119.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 (64) hide show
  1. package/dist/{agent-bridge-PRAbEX-9.d.ts → agent-bridge-r9y6gdn4.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DXWImnNV.d.ts → agent-subagent-runner-1GeQE_L0.d.ts} +7 -7
  3. package/dist/{brain-NZZqL8sn.d.ts → brain-Cp_3GIS2.d.ts} +14 -3
  4. package/dist/{compactor-C3stu664.d.ts → compactor-BueGt7LG.d.ts} +9 -1
  5. package/dist/{config-DZV9VK9n.d.ts → config-BaVThgnT.d.ts} +1 -1
  6. package/dist/{context-hvXINa6a.d.ts → context-C7G_MtLV.d.ts} +7 -0
  7. package/dist/coordination/index.d.ts +12 -12
  8. package/dist/coordination/index.js +31 -17
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +24 -24
  11. package/dist/defaults/index.js +638 -681
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +14 -14
  14. package/dist/execution/index.js +326 -317
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +6 -6
  17. package/dist/{goal-preamble-COpt55W8.d.ts → goal-preamble-iuIUTQwk.d.ts} +47 -17
  18. package/dist/{index-Cj3le7TI.d.ts → index-BZdezm3g.d.ts} +8 -8
  19. package/dist/{index-4VVHuTZF.d.ts → index-CPweVoFM.d.ts} +5 -5
  20. package/dist/index.d.ts +47 -47
  21. package/dist/index.js +455 -503
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -6
  24. package/dist/infrastructure/index.js +64 -19
  25. package/dist/infrastructure/index.js.map +1 -1
  26. package/dist/kernel/index.d.ts +9 -9
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/{llm-selector-Bmx4PsVM.d.ts → llm-selector-CP72f1lC.d.ts} +2 -2
  29. package/dist/{mcp-servers-CofNHOqn.d.ts → mcp-servers-Bl5LTvQg.d.ts} +3 -3
  30. package/dist/models/index.d.ts +5 -5
  31. package/dist/models/index.js +53 -20
  32. package/dist/models/index.js.map +1 -1
  33. package/dist/{models-registry-BzlsjKcy.d.ts → models-registry-D90K9UnM.d.ts} +1 -1
  34. package/dist/{multi-agent-coordinator-CmDmUgq2.d.ts → multi-agent-coordinator-QWEzJDlm.d.ts} +1 -1
  35. package/dist/{null-fleet-bus-CyH2YqP4.d.ts → null-fleet-bus-BUyfqh23.d.ts} +13 -10
  36. package/dist/observability/index.d.ts +2 -2
  37. package/dist/{parallel-eternal-engine-uxVm4y4V.d.ts → parallel-eternal-engine-Dj2SYzha.d.ts} +24 -11
  38. package/dist/{path-resolver-gfoGxpdd.d.ts → path-resolver-DRjQBkoO.d.ts} +3 -3
  39. package/dist/{permission-DhLrgA30.d.ts → permission-B7nKnEvQ.d.ts} +1 -1
  40. package/dist/{permission-policy-DGOvne7i.d.ts → permission-policy-8-6zBmfA.d.ts} +2 -13
  41. package/dist/{pipeline-CqF7uju2.d.ts → pipeline-BG7UgbDc.d.ts} +2 -2
  42. package/dist/{plan-templates-DoKhzenv.d.ts → plan-templates-CkKNPU3I.d.ts} +5 -5
  43. package/dist/{provider-runner-BPlI5sPZ.d.ts → provider-runner-BNpuIyOL.d.ts} +3 -3
  44. package/dist/{retry-policy-B4Q-FZgE.d.ts → retry-policy-rutAfVeR.d.ts} +1 -1
  45. package/dist/sdd/index.d.ts +8 -8
  46. package/dist/sdd/index.js +10 -1
  47. package/dist/sdd/index.js.map +1 -1
  48. package/dist/{secret-vault-B7GnL6o9.d.ts → secret-vault-BTcC_T5v.d.ts} +3 -2
  49. package/dist/security/index.d.ts +5 -7
  50. package/dist/security/index.js +4 -35
  51. package/dist/security/index.js.map +1 -1
  52. package/dist/{selector-BakeMnWR.d.ts → selector-4vDFZKt3.d.ts} +1 -1
  53. package/dist/{session-event-bridge-DE5Oekwx.d.ts → session-event-bridge-DWlvglC2.d.ts} +2 -2
  54. package/dist/{session-reader-DFC81-4p.d.ts → session-reader-BAtCxdaw.d.ts} +1 -1
  55. package/dist/storage/index.d.ts +13 -13
  56. package/dist/storage/index.js +98 -101
  57. package/dist/storage/index.js.map +1 -1
  58. package/dist/types/index.d.ts +18 -18
  59. package/dist/types/index.js +152 -102
  60. package/dist/types/index.js.map +1 -1
  61. package/dist/utils/index.d.ts +37 -13
  62. package/dist/utils/index.js +60 -32
  63. package/dist/utils/index.js.map +1 -1
  64. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1761,16 +1761,17 @@ function expectDefined(value, label) {
1761
1761
 
1762
1762
  // src/utils/token-estimate.ts
1763
1763
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
1764
- var _cal = {
1765
- ratio: 1,
1766
- // current calibration multiplier (actual / estimated)
1767
- count: 0,
1768
- // number of samples recorded
1769
- prevEst: 0,
1770
- // estimated tokens from the most recent estimateRequestTokens call
1771
- /** EWM α — higher = faster adaptation, more volatile */
1772
- alpha: 0.3
1773
- };
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
+ }
1774
1775
  var MIN_SAMPLES_FOR_CALIBRATION = 3;
1775
1776
  var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1776
1777
  var ESTIMATE_CACHE_MAX_SIZE = 1e4;
@@ -1803,10 +1804,25 @@ function estimateToolResultTokens(content) {
1803
1804
  function estimateTextTokens(text) {
1804
1805
  return RoughTokenEstimate(text);
1805
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
+ }
1806
1822
  function estimateToolDefTokens(tool) {
1807
1823
  return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
1808
1824
  }
1809
- function estimateRequestTokens(messages, systemPrompt, tools) {
1825
+ function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
1810
1826
  let messagesTokens = 0;
1811
1827
  if (typeof messages === "string") {
1812
1828
  messagesTokens = RoughTokenEstimate(messages);
@@ -1845,7 +1861,7 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
1845
1861
  toolsTokens += estimateToolDefTokens(t2);
1846
1862
  }
1847
1863
  const total = messagesTokens + systemTokens + toolsTokens;
1848
- _cal.prevEst = total;
1864
+ calState(calibrationKey).prevEst = total;
1849
1865
  return {
1850
1866
  messages: messagesTokens,
1851
1867
  systemPrompt: systemTokens,
@@ -1853,30 +1869,33 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
1853
1869
  total
1854
1870
  };
1855
1871
  }
1856
- function recordActualUsage(actualInputTokens, estimatedInputTokens) {
1872
+ function recordActualUsage(actualInputTokens, estimatedInputTokens, calibrationKey = CALIBRATION_GLOBAL_KEY) {
1857
1873
  if (actualInputTokens <= 0) return;
1858
- const est = estimatedInputTokens ?? _cal.prevEst;
1874
+ const cal = calState(calibrationKey);
1875
+ const est = estimatedInputTokens ?? cal.prevEst;
1859
1876
  if (est <= 0) return;
1860
1877
  const sampleRatio = actualInputTokens / est;
1861
- if (_cal.count === 0) {
1862
- _cal.ratio = sampleRatio;
1878
+ if (cal.count === 0) {
1879
+ cal.ratio = sampleRatio;
1863
1880
  } else {
1864
- _cal.ratio = _cal.alpha * sampleRatio + (1 - _cal.alpha) * _cal.ratio;
1881
+ cal.ratio = CAL_ALPHA * sampleRatio + (1 - CAL_ALPHA) * cal.ratio;
1865
1882
  }
1866
- _cal.ratio = Math.min(1.5, Math.max(0.5, _cal.ratio));
1867
- _cal.count++;
1883
+ cal.ratio = Math.min(1.5, Math.max(0.5, cal.ratio));
1884
+ cal.count++;
1868
1885
  }
1869
- function getCalibrationState() {
1886
+ function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
1887
+ const cal = calState(calibrationKey);
1870
1888
  return {
1871
- ratio: _cal.ratio,
1872
- count: _cal.count,
1873
- calibrated: _cal.count >= MIN_SAMPLES_FOR_CALIBRATION
1889
+ ratio: cal.ratio,
1890
+ count: cal.count,
1891
+ calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
1874
1892
  };
1875
1893
  }
1876
- function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
1877
- const result = estimateRequestTokens(messages, systemPrompt, tools);
1878
- if (_cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
1879
- 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));
1880
1899
  return {
1881
1900
  messages: Math.round(result.messages * safeRatio),
1882
1901
  systemPrompt: Math.round(result.systemPrompt * safeRatio),
@@ -1886,10 +1905,12 @@ function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
1886
1905
  }
1887
1906
  return result;
1888
1907
  }
1889
- function resetCalibration() {
1890
- _cal.ratio = 1;
1891
- _cal.count = 0;
1892
- _cal.prevEst = 0;
1908
+ function resetCalibration(calibrationKey) {
1909
+ if (calibrationKey === void 0) {
1910
+ _cals.clear();
1911
+ return;
1912
+ }
1913
+ _cals.delete(calibrationKey);
1893
1914
  }
1894
1915
 
1895
1916
  // src/utils/message-invariants.ts
@@ -1984,34 +2005,144 @@ function isEmptyMessage(msg) {
1984
2005
  return msg.content.length === 0;
1985
2006
  }
1986
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
+
1987
2117
  // src/execution/compactor.ts
1988
2118
  var HybridCompactor = class {
1989
2119
  preserveK;
1990
2120
  eliseThreshold;
1991
- estimator;
1992
2121
  constructor(opts = {}) {
1993
2122
  this.preserveK = opts.preserveK ?? 5;
1994
2123
  this.eliseThreshold = opts.eliseThreshold ?? 2e3;
1995
- this.estimator = opts.estimator ?? estimateTextTokens;
1996
2124
  }
1997
2125
  async compact(ctx, opts = {}) {
1998
- const beforeTokens = this.estimateMessages(ctx.messages);
2126
+ const beforeTokens = estimateMessages(ctx.messages);
1999
2127
  const beforeFull = this.estimateFullRequest(ctx);
2000
2128
  const reductions = [];
2001
2129
  const policy = readContextWindowPolicy(ctx);
2002
2130
  const preserveK = policy?.preserveK ?? this.preserveK;
2003
2131
  const eliseThreshold = policy?.eliseThreshold ?? this.eliseThreshold;
2004
- const phase1Saved = this.eliseOldToolResults(ctx, preserveK, eliseThreshold);
2005
- 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;
2006
2136
  if (opts.aggressive) {
2007
- const phase2Saved = this.collapseAncientTurns(ctx, preserveK);
2008
- 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;
2009
2140
  }
2010
2141
  const repaired = repairToolUseAdjacency(ctx.messages);
2011
2142
  if (repaired.report.changed) {
2012
2143
  ctx.state.replaceMessages(repaired.messages);
2013
2144
  }
2014
- const afterTokens = this.estimateMessages(ctx.messages);
2145
+ const afterTokens = estimateMessages(ctx.messages);
2015
2146
  const afterFull = this.estimateFullRequest(ctx);
2016
2147
  return {
2017
2148
  before: beforeTokens,
@@ -2019,6 +2150,7 @@ var HybridCompactor = class {
2019
2150
  fullRequestTokensBefore: beforeFull,
2020
2151
  fullRequestTokensAfter: afterFull,
2021
2152
  reductions,
2153
+ collapsedDigest,
2022
2154
  repaired: repaired.report.changed ? {
2023
2155
  removedToolUses: repaired.report.removedToolUses,
2024
2156
  removedToolResults: repaired.report.removedToolResults,
@@ -2031,72 +2163,23 @@ var HybridCompactor = class {
2031
2163
  * This is the accurate figure for context-window pressure monitoring.
2032
2164
  */
2033
2165
  estimateFullRequest(ctx) {
2034
- const breakdown = estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []);
2035
- return breakdown.total;
2036
- }
2037
- eliseOldToolResults(ctx, preserveK = this.preserveK, eliseThreshold = this.eliseThreshold) {
2038
- const messages = ctx.messages;
2039
- let pairCount = 0;
2040
- let preserveStart = messages.length;
2041
- for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
2042
- const m = messages[i];
2043
- if (!m) continue;
2044
- if (m.role === "user" || m.role === "assistant") {
2045
- pairCount++;
2046
- preserveStart = i;
2047
- }
2048
- }
2049
- for (let i = preserveStart; i < messages.length; i++) {
2050
- const m = messages[i];
2051
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
2052
- const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
2053
- if (hasToolUse2 && i + 1 < messages.length) {
2054
- const next = messages[i + 1];
2055
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
2056
- preserveStart = i + 1;
2057
- }
2058
- }
2059
- }
2060
- let saved = 0;
2061
- let changed = false;
2062
- const nextMessages = new Array(messages.length);
2063
- for (let i = 0; i < messages.length; i++) {
2064
- const msg = messages[i];
2065
- if (i >= preserveStart) {
2066
- nextMessages[i] = msg;
2067
- continue;
2068
- }
2069
- if (!msg || !Array.isArray(msg.content)) {
2070
- nextMessages[i] = msg;
2071
- continue;
2072
- }
2073
- const newContent = msg.content.map((b) => {
2074
- if (b.type !== "tool_result") return b;
2075
- const tokens = estimateToolResultTokens(b.content);
2076
- if (tokens < eliseThreshold) return b;
2077
- saved += tokens;
2078
- const elided = {
2079
- type: "tool_result",
2080
- tool_use_id: b.tool_use_id,
2081
- content: `[elided: ~${tokens} tokens]`,
2082
- is_error: b.is_error
2083
- };
2084
- return elided;
2085
- });
2086
- if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
2087
- nextMessages[i] = msg;
2088
- } else {
2089
- nextMessages[i] = { ...msg, content: newContent };
2090
- changed = true;
2091
- }
2092
- }
2093
- if (changed) ctx.state.replaceMessages(nextMessages);
2094
- return saved;
2166
+ return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
2095
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
+ */
2096
2179
  collapseAncientTurns(ctx, preserveK = this.preserveK) {
2097
2180
  const messages = ctx.messages;
2098
2181
  const cutTarget = Math.max(0, messages.length - preserveK * 2);
2099
- if (cutTarget <= 0) return 0;
2182
+ if (cutTarget <= 0) return { saved: 0 };
2100
2183
  let boundary = -1;
2101
2184
  for (let i = cutTarget; i < messages.length; i++) {
2102
2185
  const m = messages[i];
@@ -2106,34 +2189,20 @@ var HybridCompactor = class {
2106
2189
  break;
2107
2190
  }
2108
2191
  }
2109
- if (boundary <= 0) return 0;
2192
+ if (boundary <= 0) return { saved: 0 };
2110
2193
  const removed = messages.slice(0, boundary);
2111
- const removedTokens = this.estimateMessages(removed);
2112
- const summary = [
2113
- {
2114
- role: "user",
2115
- content: `[previous_session_summary: ${removed.length} earlier turns compacted. Todo state preserved in context.]`
2116
- },
2117
- { role: "assistant", content: "Continuing from compacted context." }
2118
- ];
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
+ };
2119
2200
  const tail = ctx.messages.slice(boundary);
2120
- ctx.state.replaceMessages([...summary, ...tail]);
2121
- return Math.max(0, removedTokens - this.estimateMessages(summary));
2122
- }
2123
- estimateMessages(messages) {
2124
- let total = 0;
2125
- for (const m of messages) {
2126
- if (typeof m.content === "string") {
2127
- total += this.estimator(m.content);
2128
- } else {
2129
- for (const b of m.content) {
2130
- if (b.type === "text") total += this.estimator(b.text);
2131
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
2132
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
2133
- }
2134
- }
2135
- }
2136
- return total;
2201
+ ctx.state.replaceMessages([summaryMsg, ...tail]);
2202
+ return {
2203
+ saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
2204
+ digest
2205
+ };
2137
2206
  }
2138
2207
  };
2139
2208
  function readContextWindowPolicy(ctx) {
@@ -2145,10 +2214,6 @@ function readContextWindowPolicy(ctx) {
2145
2214
  }
2146
2215
  return candidate;
2147
2216
  }
2148
- function hasTextContent(m) {
2149
- if (typeof m.content === "string") return m.content.trim().length > 0;
2150
- return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
2151
- }
2152
2217
  var PROJECT_MARKERS = [
2153
2218
  ".git",
2154
2219
  "package.json",
@@ -3396,6 +3461,8 @@ function topologicalSort(graph) {
3396
3461
  var ToolCapabilities = {
3397
3462
  /** Can execute arbitrary commands in the user's shell (the `bash` tool). */
3398
3463
  SHELL_ARBITRARY: "shell.arbitrary",
3464
+ /** Can execute a restricted set of commands (the `exec` tool). */
3465
+ SHELL_RESTRICTED: "shell.restricted",
3399
3466
  /** Can write / modify / delete files inside the project. */
3400
3467
  FS_WRITE: "fs.write",
3401
3468
  /** Can write files outside the current project root (very high risk). */
@@ -3411,6 +3478,7 @@ var ToolCapabilities = {
3411
3478
  };
3412
3479
  var DANGEROUS_FOR_SUBAGENTS = [
3413
3480
  ToolCapabilities.SHELL_ARBITRARY,
3481
+ ToolCapabilities.SHELL_RESTRICTED,
3414
3482
  ToolCapabilities.FS_WRITE,
3415
3483
  ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
3416
3484
  ToolCapabilities.MCP_PROXY,
@@ -4958,6 +5026,13 @@ function buildChildEnv(optsOrSessionId) {
4958
5026
  function sleep(ms) {
4959
5027
  return new Promise((resolve12) => setTimeout(resolve12, ms));
4960
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
+ }
4961
5036
  var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
4962
5037
  var IS_WINDOWS = process.platform === "win32";
4963
5038
  var SEP = IS_WINDOWS ? "\\" : "/";
@@ -5020,7 +5095,7 @@ async function expandGlob(pattern) {
5020
5095
  const abs = isAbsolute(pattern);
5021
5096
  const base = abs ? baseDir(pattern) : baseDir(pattern);
5022
5097
  const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
5023
- async function walk4(dir, pat) {
5098
+ async function walk3(dir, pat) {
5024
5099
  let entries;
5025
5100
  try {
5026
5101
  entries = await fsp3.readdir(dir);
@@ -5041,12 +5116,12 @@ async function expandGlob(pattern) {
5041
5116
  const before = pat.slice(0, firstGlob);
5042
5117
  const rest = pat.slice(firstGlob);
5043
5118
  if (before.endsWith("**")) {
5044
- await walk4(dir, rest);
5119
+ await walk3(dir, rest);
5045
5120
  for (const e of entries) {
5046
5121
  const full = `${dir}${SEP}${e}`;
5047
5122
  try {
5048
5123
  const stat11 = await fsp3.stat(full);
5049
- if (stat11.isDirectory()) await walk4(full, rest);
5124
+ if (stat11.isDirectory()) await walk3(full, rest);
5050
5125
  } catch {
5051
5126
  }
5052
5127
  }
@@ -5064,13 +5139,13 @@ async function expandGlob(pattern) {
5064
5139
  const full = `${dir}${SEP}${seg}`;
5065
5140
  try {
5066
5141
  const stat11 = await fsp3.stat(full);
5067
- if (stat11.isDirectory()) await walk4(full, rest);
5142
+ if (stat11.isDirectory()) await walk3(full, rest);
5068
5143
  } catch {
5069
5144
  }
5070
5145
  }
5071
5146
  }
5072
5147
  }
5073
- await walk4(base === "." ? "." : base, relPat);
5148
+ await walk3(base === "." ? "." : base, relPat);
5074
5149
  return [...results];
5075
5150
  }
5076
5151
 
@@ -5241,8 +5316,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
5241
5316
  onClose: (s) => this.appendToIndex(s)
5242
5317
  });
5243
5318
  } catch (err) {
5244
- await handle.close().catch(() => {
5245
- });
5319
+ await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
5246
5320
  throw err;
5247
5321
  }
5248
5322
  }
@@ -5273,8 +5347,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
5273
5347
  );
5274
5348
  return { writer, data };
5275
5349
  } catch (err) {
5276
- await handle.close().catch(() => {
5277
- });
5350
+ await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
5278
5351
  throw err;
5279
5352
  }
5280
5353
  }
@@ -5463,15 +5536,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
5463
5536
  * sidecars, and the session directory (fleet.json, shared/, subagents/).
5464
5537
  */
5465
5538
  async deleteSession(id) {
5466
- await fsp3.unlink(this.sessionPath(id, ".jsonl")).catch(() => void 0);
5467
- 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}`));
5468
5541
  const shardDir = path6.dirname(path6.join(this.dir, id));
5469
5542
  const base = path6.basename(id);
5470
5543
  for (const ext of [".plan.json", ".todos.json"]) {
5471
- 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}`));
5472
5545
  }
5473
5546
  const sessDir = path6.join(shardDir, base);
5474
- 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}`));
5475
5548
  await this.writeTombstone(id);
5476
5549
  }
5477
5550
  async delete(id) {
@@ -6761,48 +6834,6 @@ function deepFreeze(obj) {
6761
6834
  }
6762
6835
  return Object.freeze(obj);
6763
6836
  }
6764
-
6765
- // src/security/config-secrets.ts
6766
- function decryptConfigSecrets2(cfg, vault) {
6767
- return walk3(cfg, vault, (v, key) => {
6768
- try {
6769
- return vault.decrypt(v);
6770
- } catch (err) {
6771
- console.warn(
6772
- `[secret-vault] Failed to decrypt "${key}":`,
6773
- err instanceof Error ? err.message : err
6774
- );
6775
- return "";
6776
- }
6777
- });
6778
- }
6779
- function walk3(node, vault, transform) {
6780
- if (node === null || node === void 0) return node;
6781
- if (typeof node !== "object") return node;
6782
- if (Array.isArray(node)) {
6783
- return node.map((item) => walk3(item, vault, transform));
6784
- }
6785
- const out = /* @__PURE__ */ Object.create(null);
6786
- for (const [k, v] of Object.entries(node)) {
6787
- if (typeof v === "string" && isSecretField2(k)) {
6788
- out[k] = transform(v, k);
6789
- } else if (typeof v === "object" && v !== null) {
6790
- out[k] = walk3(v, vault, transform);
6791
- } else {
6792
- out[k] = v;
6793
- }
6794
- }
6795
- return out;
6796
- }
6797
- 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;
6798
- var NON_SECRET_OVERRIDES2 = /* @__PURE__ */ new Set(["publickey", "public_key"]);
6799
- function isSecretField2(name) {
6800
- const lc = name.toLowerCase();
6801
- if (NON_SECRET_OVERRIDES2.has(lc)) return false;
6802
- return SECRET_KEY_PATTERN2.test(lc);
6803
- }
6804
-
6805
- // src/storage/config-loader.ts
6806
6837
  init_atomic_write();
6807
6838
  var BEHAVIOR_DEFAULTS = {
6808
6839
  version: 1,
@@ -6972,7 +7003,7 @@ var DefaultConfigLoader = class {
6972
7003
  cfg = deepMerge2(cfg, opts.cliFlags);
6973
7004
  }
6974
7005
  if (this.vault) {
6975
- cfg = decryptConfigSecrets2(cfg, this.vault);
7006
+ cfg = decryptConfigSecrets(cfg, this.vault);
6976
7007
  }
6977
7008
  if (cfg.providers) {
6978
7009
  for (const pcfg of Object.values(cfg.providers)) {
@@ -7022,7 +7053,7 @@ var DefaultConfigLoader = class {
7022
7053
  const parsed = safeParse(raw);
7023
7054
  if (!parsed.ok || !parsed.value) return null;
7024
7055
  if (this.vault) {
7025
- const decrypted = decryptConfigSecrets2({ sync: parsed.value }, this.vault);
7056
+ const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
7026
7057
  return decrypted.sync ?? null;
7027
7058
  }
7028
7059
  return parsed.value;
@@ -8305,38 +8336,15 @@ var DefaultPermissionPolicy = class {
8305
8336
  }
8306
8337
  };
8307
8338
  var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
8308
- /**
8309
- * Legacy name-based denylist.
8310
- * @deprecated Prefer declaring `capabilities` on the Tool and using capability-based checks.
8311
- */
8312
- static LEGACY_NAME_DENY = /* @__PURE__ */ new Set([
8313
- "bash",
8314
- "write",
8315
- "edit",
8316
- "replace",
8317
- "scaffold",
8318
- "patch",
8319
- "install",
8320
- "exec"
8321
- ]);
8322
- // Note: hasDangerousCapabilityForSubagents is now the shared helper from capabilities.ts
8323
- // The old private method was removed in favor of the centralized utility.
8324
- /**
8325
- * Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
8326
- * unknown capability — they may wrap a shell or filesystem. They are
8327
- * fail-closed here: not auto-approved for subagents by default, so the
8328
- * leader must allow them explicitly per-spawn.
8329
- */
8330
8339
  static isMcpTool(name) {
8331
8340
  return name.startsWith("mcp__");
8332
8341
  }
8333
8342
  async evaluate(tool) {
8334
8343
  const hasDangerousCap = hasDangerousCapabilityForSubagents(tool);
8335
- const legacyNameBlock = _AutoApprovePermissionPolicy.LEGACY_NAME_DENY.has(tool.name);
8336
8344
  const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
8337
- const blocked = tool.permission === "deny" || hasDangerousCap || legacyNameBlock || isMcp;
8345
+ const blocked = tool.permission === "deny" || hasDangerousCap || isMcp;
8338
8346
  if (blocked) {
8339
- 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";
8340
8348
  return {
8341
8349
  permission: "deny",
8342
8350
  source: "subagent_guard",
@@ -8638,53 +8646,63 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
8638
8646
  const next = await iter.next();
8639
8647
  if (next.done) break;
8640
8648
  const ev = next.value;
8641
- switch (ev.type) {
8642
- case "message_start":
8643
- handleMessageStart(state, ev.model);
8644
- break;
8645
- case "content_block_start":
8646
- handleContentBlockStart(state, ev);
8647
- break;
8648
- case "content_block_stop":
8649
- handleContentBlockStop(state, ev);
8650
- break;
8651
- case "text_delta":
8652
- handleTextDelta(state, ev.text);
8653
- events.emit("provider.text_delta", { ctx, text: ev.text });
8654
- break;
8655
- case "tool_use_start": {
8656
- const idVal = ev.id;
8657
- const nameVal = ev.name;
8658
- handleToolUseStart(state, { id: idVal, name: nameVal });
8659
- const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
8660
- events.emit("provider.tool_use_start", emittedPayload);
8661
- break;
8662
- }
8663
- case "tool_use_input_delta":
8664
- handleToolUseInputDelta(state, ev);
8665
- break;
8666
- case "tool_use_stop": {
8667
- const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
8668
- handleToolUseStop(state, ev);
8669
- events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
8670
- 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;
8671
8699
  }
8672
- case "thinking_start":
8673
- handleThinkingStart(state, ev);
8674
- break;
8675
- case "thinking_delta":
8676
- handleThinkingDelta(state, ev.text);
8677
- events.emit("provider.thinking_delta", { ctx, text: ev.text });
8678
- break;
8679
- case "thinking_signature":
8680
- handleThinkingSignature(state, ev.signature);
8681
- break;
8682
- case "thinking_stop":
8683
- handleThinkingStop(state);
8684
- break;
8685
- case "message_stop":
8686
- handleMessageStop(state, ev);
8687
- 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
+ });
8688
8706
  }
8689
8707
  }
8690
8708
  } catch (err) {
@@ -8697,8 +8715,10 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
8697
8715
  try {
8698
8716
  let drainTimer = null;
8699
8717
  try {
8718
+ const drainPromise = Promise.resolve(iter.return?.()).catch(() => {
8719
+ });
8700
8720
  await Promise.race([
8701
- Promise.resolve(iter.return?.()),
8721
+ drainPromise,
8702
8722
  new Promise((resolve12) => {
8703
8723
  drainTimer = setTimeout(resolve12, STREAM_DRAIN_TIMEOUT_MS);
8704
8724
  })
@@ -8817,23 +8837,25 @@ var IntelligentCompactor = class {
8817
8837
  this.summarizerModel = opts.summarizerModel;
8818
8838
  }
8819
8839
  async compact(ctx, opts = {}) {
8820
- const beforeTokens = this.estimateTokens(ctx.messages);
8840
+ const beforeTokens = estimateMessages(ctx.messages);
8821
8841
  const beforeFull = this.estimateFullRequest(ctx);
8822
8842
  const reductions = [];
8823
8843
  const load = beforeFull / this.maxContext;
8824
8844
  const aggressive = load >= this.hardThreshold ? true : opts.aggressive ?? load >= this.softThreshold;
8825
- const saved1 = this.eliseOldToolResults(ctx);
8845
+ const saved1 = this.elide(ctx);
8826
8846
  if (saved1 > 0) reductions.push({ phase: "elision", saved: saved1 });
8847
+ let collapsedDigest;
8827
8848
  if (aggressive) {
8828
- const saved2 = await this.summarizeAncientTurns(ctx);
8829
- 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;
8830
8852
  } else if (load >= this.warnThreshold) {
8831
- const saved2 = this.lightweightCompact(ctx);
8853
+ const saved2 = this.elide(ctx);
8832
8854
  if (saved2 > 0) reductions.push({ phase: "elision", saved: saved2 });
8833
8855
  }
8834
8856
  const repaired = repairToolUseAdjacency(ctx.messages);
8835
8857
  if (repaired.report.changed) ctx.state.replaceMessages(repaired.messages);
8836
- const afterTokens = this.estimateTokens(ctx.messages);
8858
+ const afterTokens = estimateMessages(ctx.messages);
8837
8859
  const afterFull = this.estimateFullRequest(ctx);
8838
8860
  return {
8839
8861
  before: beforeTokens,
@@ -8841,6 +8863,7 @@ var IntelligentCompactor = class {
8841
8863
  fullRequestTokensBefore: beforeFull,
8842
8864
  fullRequestTokensAfter: afterFull,
8843
8865
  reductions,
8866
+ collapsedDigest,
8844
8867
  repaired: repaired.report.changed ? {
8845
8868
  removedToolUses: repaired.report.removedToolUses,
8846
8869
  removedToolResults: repaired.report.removedToolResults,
@@ -8853,75 +8876,39 @@ var IntelligentCompactor = class {
8853
8876
  * This is the accurate figure for context-window pressure monitoring.
8854
8877
  */
8855
8878
  estimateFullRequest(ctx) {
8856
- const breakdown = estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []);
8857
- 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;
8858
8889
  }
8859
8890
  async summarizeAncientTurns(ctx) {
8860
8891
  const messages = ctx.messages;
8861
8892
  const cutoff = Math.max(0, messages.length - this.preserveK * 2);
8862
- if (cutoff <= 2) return 0;
8863
- const boundary = this.findSafeBoundary(messages, 0, cutoff);
8864
- 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 };
8865
8896
  const toSummarize = messages.slice(0, boundary);
8866
- const removedTokens = this.estimateTokens(toSummarize);
8897
+ const removedTokens = estimateMessages(toSummarize);
8867
8898
  let summaryText;
8868
8899
  try {
8869
8900
  summaryText = await this.callSummarizer(toSummarize, ctx);
8870
8901
  } catch {
8871
- const preservedMessages = [];
8872
- for (const m of toSummarize) {
8873
- if (m.role === "system") continue;
8874
- if (typeof m.content === "string") {
8875
- preservedMessages.push(m);
8876
- } else if (Array.isArray(m.content)) {
8877
- const textParts = m.content.filter(isTextBlock);
8878
- const toolParts = m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result");
8879
- const next = {
8880
- role: m.role,
8881
- content: [
8882
- ...textParts,
8883
- ...toolParts.length > 0 ? [{ type: "text", text: `[${toolParts.length} tool call(s) omitted]` }] : []
8884
- ]
8885
- };
8886
- preservedMessages.push(next);
8887
- }
8888
- }
8889
- summaryText = preservedMessages.map((m) => `[${m.role}]: ${typeof m.content === "string" ? m.content : m.content.filter(isTextBlock).map((b) => b.text).join(" ")}`).join("\n");
8890
- if (!summaryText) summaryText = `${toSummarize.length} earlier turns (semantic content preserved)`;
8902
+ summaryText = buildLosslessDigest(toSummarize) || `${toSummarize.length} earlier turns (semantic content preserved)`;
8891
8903
  }
8892
8904
  const summaryMsg = {
8893
8905
  role: "system",
8894
8906
  content: `[prior_turns_summary: ${summaryText}]`
8895
8907
  };
8896
- const summaryTokens = this.estimateTokens([summaryMsg]);
8908
+ const summaryTokens = estimateMessages([summaryMsg]);
8897
8909
  const tail = ctx.messages.slice(boundary);
8898
8910
  ctx.state.replaceMessages([summaryMsg, ...tail]);
8899
- return Math.max(0, removedTokens - summaryTokens);
8900
- }
8901
- findSafeBoundary(messages, from, to) {
8902
- for (let i = to; i >= from; i--) {
8903
- const m = messages[i];
8904
- if (!m) continue;
8905
- if (m.role === "user" && this.hasTextContent(m)) {
8906
- return this.findExchangeStart(messages, i);
8907
- }
8908
- }
8909
- return -1;
8910
- }
8911
- findExchangeStart(messages, userIndex) {
8912
- for (let i = userIndex - 1; i >= 0; i--) {
8913
- const m = messages[i];
8914
- if (!m) continue;
8915
- if (m.role === "assistant") {
8916
- const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
8917
- if (!hasToolUse2) {
8918
- return i + 1;
8919
- }
8920
- } else if (m.role !== "user") ; else {
8921
- return i;
8922
- }
8923
- }
8924
- return 0;
8911
+ return { saved: Math.max(0, removedTokens - summaryTokens), digest: summaryText };
8925
8912
  }
8926
8913
  async callSummarizer(messages, ctx) {
8927
8914
  const prompt = [
@@ -8956,86 +8943,6 @@ var IntelligentCompactor = class {
8956
8943
  }
8957
8944
  return [{ type: "text", text: lines.join("\n") }];
8958
8945
  }
8959
- lightweightCompact(ctx) {
8960
- return this.eliseOldToolResults(ctx);
8961
- }
8962
- eliseOldToolResults(ctx) {
8963
- const messages = ctx.messages;
8964
- let pairCount = 0;
8965
- let preserveStart = messages.length;
8966
- for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
8967
- const m = messages[i];
8968
- if (!m) continue;
8969
- if (m.role === "user" || m.role === "assistant") {
8970
- pairCount++;
8971
- preserveStart = i;
8972
- }
8973
- }
8974
- for (let i = preserveStart; i < messages.length; i++) {
8975
- const m = messages[i];
8976
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
8977
- const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
8978
- if (hasToolUse2 && i + 1 < messages.length) {
8979
- const next = messages[i + 1];
8980
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
8981
- preserveStart = i + 1;
8982
- }
8983
- }
8984
- }
8985
- let saved = 0;
8986
- let changed = false;
8987
- const nextMessages = new Array(messages.length);
8988
- for (let i = 0; i < messages.length; i++) {
8989
- const msg = messages[i];
8990
- if (i >= preserveStart) {
8991
- nextMessages[i] = msg;
8992
- continue;
8993
- }
8994
- if (!msg || !Array.isArray(msg.content)) {
8995
- nextMessages[i] = msg;
8996
- continue;
8997
- }
8998
- const newContent = msg.content.map((b) => {
8999
- if (b.type !== "tool_result") return b;
9000
- const tokens = estimateToolResultTokens(b.content);
9001
- if (tokens < this.eliseThreshold) return b;
9002
- saved += tokens;
9003
- return {
9004
- type: "tool_result",
9005
- tool_use_id: b.tool_use_id,
9006
- content: `[elided: ~${tokens} tokens]`,
9007
- is_error: b.is_error
9008
- };
9009
- });
9010
- if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
9011
- nextMessages[i] = msg;
9012
- } else {
9013
- nextMessages[i] = { ...msg, content: newContent };
9014
- changed = true;
9015
- }
9016
- }
9017
- if (changed) ctx.state.replaceMessages(nextMessages);
9018
- return saved;
9019
- }
9020
- hasTextContent(m) {
9021
- if (typeof m.content === "string") return m.content.trim().length > 0;
9022
- return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
9023
- }
9024
- estimateTokens(messages) {
9025
- let total = 0;
9026
- for (const m of messages) {
9027
- if (typeof m.content === "string") {
9028
- total += estimateTextTokens(m.content);
9029
- } else {
9030
- for (const b of m.content) {
9031
- if (b.type === "text") total += estimateTextTokens(b.text);
9032
- else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
9033
- else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
9034
- }
9035
- }
9036
- }
9037
- return total;
9038
- }
9039
8946
  };
9040
8947
 
9041
8948
  // src/models/llm-selector.ts
@@ -9061,20 +8968,6 @@ Rules:
9061
8968
  - If unsure, keep rather than collapse (errors are more costly than waste)
9062
8969
 
9063
8970
  Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
9064
- function estimateTokens(messages) {
9065
- let total = 0;
9066
- for (const m of messages) {
9067
- if (typeof m.content === "string") {
9068
- total += Math.ceil(m.content.length / 4);
9069
- } else if (Array.isArray(m.content)) {
9070
- for (const b of m.content) {
9071
- if (b.type === "text") total += Math.ceil(b.text.length / 4);
9072
- else total += Math.ceil(JSON.stringify(b).length / 4);
9073
- }
9074
- }
9075
- }
9076
- return total;
9077
- }
9078
8971
  function formatMessages(messages, maxChars = 8e3) {
9079
8972
  const lines = [];
9080
8973
  let used = 0;
@@ -9113,7 +9006,7 @@ var LLMSelector = class {
9113
9006
  async select(messages, maxToKeep) {
9114
9007
  const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
9115
9008
  const historyText = formatMessages(messages);
9116
- const totalTokens = estimateTokens(messages);
9009
+ const totalTokens = estimateMessageTokens(messages);
9117
9010
  const systemText = `${this.systemPrompt}
9118
9011
 
9119
9012
  Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
@@ -9145,10 +9038,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
9145
9038
  let startIdx = 0;
9146
9039
  for (let i = messages.length - 1; i >= 0; i--) {
9147
9040
  const m = expectDefined(messages[i]);
9148
- const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
9149
- (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
9150
- 0
9151
- );
9041
+ const cost = estimateMessageTokens([m]);
9152
9042
  if (tokenCount + cost <= budget) {
9153
9043
  tokenCount += cost;
9154
9044
  } else {
@@ -9381,52 +9271,12 @@ Summarize the following message range:`;
9381
9271
  return Math.floor(this.maxContext * 0.75);
9382
9272
  }
9383
9273
  eliseOldToolResults(ctx) {
9384
- const messages = ctx.messages;
9385
- let pairCount = 0;
9386
- let preserveStart = messages.length;
9387
- for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
9388
- const m = messages[i];
9389
- if (!m) continue;
9390
- if (m.role === "user" || m.role === "assistant") {
9391
- pairCount++;
9392
- preserveStart = i;
9393
- }
9394
- }
9395
- let saved = 0;
9396
- let changed = false;
9397
- const nextMessages = new Array(messages.length);
9398
- for (let i = 0; i < messages.length; i++) {
9399
- const msg = messages[i];
9400
- if (i >= preserveStart) {
9401
- nextMessages[i] = msg;
9402
- continue;
9403
- }
9404
- if (!msg || !Array.isArray(msg.content)) {
9405
- nextMessages[i] = msg;
9406
- continue;
9407
- }
9408
- const newContent = msg.content.map((b) => {
9409
- if (b.type !== "tool_result") return b;
9410
- const text = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
9411
- const tokens = this.roughTokenEstimate(text);
9412
- if (tokens < this.eliseThreshold) return b;
9413
- saved += tokens;
9414
- return {
9415
- type: "tool_result",
9416
- tool_use_id: b.tool_use_id,
9417
- content: `[elided: ~${tokens} tokens]`,
9418
- is_error: b.is_error
9419
- };
9420
- });
9421
- if (newContent.every((b, idx) => b === msg.content[idx])) {
9422
- nextMessages[i] = msg;
9423
- } else {
9424
- nextMessages[i] = { ...msg, content: newContent };
9425
- changed = true;
9426
- }
9427
- }
9428
- if (changed) ctx.state.replaceMessages(nextMessages);
9429
- 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;
9430
9280
  }
9431
9281
  hasTextContent(m) {
9432
9282
  if (typeof m.content === "string") return m.content.trim().length > 0;
@@ -9456,8 +9306,80 @@ Summarize the following message range:`;
9456
9306
  }
9457
9307
  };
9458
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
+
9459
9376
  // src/execution/auto-compaction-middleware.ts
9460
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
+ }
9461
9383
  var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9462
9384
  name = "AutoCompaction";
9463
9385
  compactor;
@@ -9518,7 +9440,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9518
9440
  }
9519
9441
  handler() {
9520
9442
  return async (ctx, next) => {
9521
- 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;
9522
9449
  const load = tokens / this._maxContext;
9523
9450
  const policy = this.policyProvider?.(ctx);
9524
9451
  const thresholds = policy?.thresholds ?? {
@@ -9580,7 +9507,11 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
9580
9507
  after: report.after,
9581
9508
  level: pressure.level,
9582
9509
  aggressive,
9583
- 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) } : {}
9584
9515
  });
9585
9516
  ctx.clearFileTracking();
9586
9517
  } catch (err) {
@@ -9665,6 +9596,12 @@ var DoneConditionChecker = class {
9665
9596
  return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
9666
9597
  }
9667
9598
  break;
9599
+ case "all_tasks_done":
9600
+ break;
9601
+ case "custom":
9602
+ break;
9603
+ default:
9604
+ return assertNever(this.condition.type);
9668
9605
  }
9669
9606
  return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
9670
9607
  }
@@ -9927,9 +9864,9 @@ function parseProgressFromText(text) {
9927
9864
  const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
9928
9865
  const m = text.match(re);
9929
9866
  if (!m) return null;
9930
- 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)));
9931
9868
  const note = m[2]?.trim() || void 0;
9932
- return { progress, note };
9869
+ return note === void 0 ? { progress } : { progress, note };
9933
9870
  }
9934
9871
  function recordProgress(goal, progress, note) {
9935
9872
  const clamped = Math.min(100, Math.max(0, progress));
@@ -10523,6 +10460,7 @@ ${recentJournal}` : "No prior iterations.",
10523
10460
  note: note.slice(0, 240)
10524
10461
  });
10525
10462
  await saveGoal(this.goalPath, withEntry);
10463
+ this.opts.onEternalStop?.();
10526
10464
  }
10527
10465
  /**
10528
10466
  * Manually clear the goal — equivalent to `/goal clear` typed by the user.
@@ -17052,7 +16990,10 @@ var Director = class _Director {
17052
16990
  workCompleteFlag = false;
17053
16991
  /** Pending /btw notes stashed by the leader agent (see setLeaderBtwNote). */
17054
16992
  _leaderBtwNotes = [];
17055
- /** 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. */
17056
16997
  _activeCollabSessions = /* @__PURE__ */ new Map();
17057
16998
  /** Prevents large `ask_subagent` answers from bloating the leader's context window. */
17058
16999
  largeAnswerStore;
@@ -17382,11 +17323,13 @@ var Director = class _Director {
17382
17323
  * The CollabDebugReport will reflect 'cancelled' disposition when awaited.
17383
17324
  */
17384
17325
  cancelCollabSession(sessionId, reason = "Director cancelled") {
17385
- const session = this._activeCollabSessions.get(sessionId);
17386
- if (!session || session.isCancelled()) return;
17387
- session.cancel(reason);
17388
- for (const [_role, subagentId] of session.getSubagentIds()) {
17389
- 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}`);
17390
17333
  });
17391
17334
  }
17392
17335
  }
@@ -18034,9 +17977,15 @@ var Director = class _Director {
18034
17977
  return options.onBudgetWarning?.(alert) ?? "ignore";
18035
17978
  }
18036
17979
  });
18037
- this._activeCollabSessions.set(session.sessionId, session);
18038
- session.on("session.done", () => this._activeCollabSessions.delete(session.sessionId));
18039
- 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 });
18040
17989
  return session.start();
18041
17990
  }
18042
17991
  /**
@@ -20804,6 +20753,8 @@ var SpecVersioning = class {
20804
20753
  switch (type) {
20805
20754
  case "functional":
20806
20755
  return "feature";
20756
+ case "non-functional":
20757
+ return "feature";
20807
20758
  case "security":
20808
20759
  return "feature";
20809
20760
  case "performance":
@@ -20811,7 +20762,7 @@ var SpecVersioning = class {
20811
20762
  case "ux":
20812
20763
  return "feature";
20813
20764
  default:
20814
- return "feature";
20765
+ return assertNever(type);
20815
20766
  }
20816
20767
  }
20817
20768
  };
@@ -21962,27 +21913,13 @@ function startOtlpTraceExporter(opts) {
21962
21913
  // src/infrastructure/context-manager.ts
21963
21914
  var CONTEXT_MANAGER_TOOL_NAME = "context_manager";
21964
21915
  function roughEstimate(messages) {
21965
- let total = 0;
21966
- for (const m of messages) {
21967
- if (typeof m.content === "string") {
21968
- total += Math.ceil(m.content.length / 4);
21969
- } else if (Array.isArray(m.content)) {
21970
- for (const b of m.content) {
21971
- if (b.type === "text") total += Math.ceil(b.text.length / 4);
21972
- else if (b.type === "tool_use" || b.type === "tool_result") {
21973
- total += Math.ceil(JSON.stringify(b).length / 4);
21974
- }
21975
- }
21976
- }
21977
- }
21978
- return total;
21916
+ return estimateMessageTokens(messages);
21979
21917
  }
21980
21918
  function createContextManagerTool(opts = {}) {
21981
21919
  const minCompactThreshold = opts.minCompactThreshold ?? 0;
21982
21920
  const noopRetryDeltaTokens = opts.noopRetryDeltaTokens ?? 2e3;
21983
- const maxContext = opts.maxContext ?? 128e3;
21921
+ const configuredMaxContext = opts.maxContext;
21984
21922
  const compactThresholdFraction = opts.compactThresholdFraction ?? 0.5;
21985
- const effectiveThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(maxContext * compactThresholdFraction);
21986
21923
  let lastNoopTokens = 0;
21987
21924
  return {
21988
21925
  name: CONTEXT_MANAGER_TOOL_NAME,
@@ -22076,6 +22013,8 @@ function createContextManagerTool(opts = {}) {
22076
22013
  }
22077
22014
  const fullEstimate = input.systemPrompt != null && Array.isArray(input.tools) ? estimateRequestTokens(messages, input.systemPrompt, input.tools) : { total: beforeTokens};
22078
22015
  const currentTokens = fullEstimate.total;
22016
+ const runtimeMaxContext = configuredMaxContext ?? ctx.provider?.capabilities?.maxContext ?? 128e3;
22017
+ const runtimeThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(runtimeMaxContext * compactThresholdFraction);
22079
22018
  if (lastNoopTokens > 0) {
22080
22019
  const delta = currentTokens - lastNoopTokens;
22081
22020
  if (delta < noopRetryDeltaTokens) {
@@ -22088,13 +22027,13 @@ function createContextManagerTool(opts = {}) {
22088
22027
  };
22089
22028
  }
22090
22029
  }
22091
- if (currentTokens < effectiveThreshold) {
22030
+ if (runtimeThreshold > 0 && currentTokens < runtimeThreshold) {
22092
22031
  return {
22093
22032
  action: "compact",
22094
22033
  beforeTokens,
22095
22034
  afterTokens: beforeTokens,
22096
22035
  messageCount: messages.length,
22097
- notes: `Context tokens (${currentTokens}) below compact threshold (${effectiveThreshold}). Skipping.`
22036
+ notes: `Context tokens (${currentTokens}) below compact threshold (${runtimeThreshold}, based on provider maxContext ${runtimeMaxContext}). Skipping.`
22098
22037
  };
22099
22038
  }
22100
22039
  const report = await opts.compactor.compact(ctx);
@@ -27463,9 +27402,17 @@ function createAgentLoopHandler(a, handlers) {
27463
27402
  async function compactContextIfNeeded() {
27464
27403
  await a.pipelines.contextWindow.run(a.ctx);
27465
27404
  }
27405
+ const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
27466
27406
  function emitContextPct() {
27467
- const maxContext = a.ctx.provider.capabilities.maxContext ?? 2e5;
27468
- 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
+ );
27469
27416
  a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
27470
27417
  }
27471
27418
  function injectPendingBtwNotes() {
@@ -27581,8 +27528,9 @@ function createAgentLoopHandler(a, handlers) {
27581
27528
  let res;
27582
27529
  try {
27583
27530
  res = await customRunner(a.ctx, req);
27584
- const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? []).total;
27585
- 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);
27586
27534
  recoveryRetries = 0;
27587
27535
  } catch (err) {
27588
27536
  if (controller.signal.aborted) {
@@ -30155,8 +30103,7 @@ var PhaseOrchestrator = class {
30155
30103
  /** Wait for all pending phase merges, dependency-ordered and globally serialized. */
30156
30104
  async drainMerges() {
30157
30105
  await Promise.allSettled([...this.phaseMergePromise.values()]);
30158
- await this.mergeQueue.catch(() => {
30159
- });
30106
+ await this.mergeQueue.catch((err) => console.warn(`[phase-orchestrator] mergeQueue failed: ${err}`));
30160
30107
  }
30161
30108
  /** Pause: active phases continue, but no new phase starts. */
30162
30109
  pause() {
@@ -31892,6 +31839,11 @@ var DefaultBrainArbiter = class {
31892
31839
  options: request.options,
31893
31840
  rationale: "Decision requires human authority or lacks a safe automatic option."
31894
31841
  };
31842
+ default:
31843
+ return {
31844
+ type: "deny",
31845
+ reason: `Brain could not safely decide (unknown fallback: ${String(request.fallback)}): ${request.question}`
31846
+ };
31895
31847
  }
31896
31848
  }
31897
31849
  };
@@ -33198,6 +33150,6 @@ ${formatPlan(updated)}`
33198
33150
  };
33199
33151
  }
33200
33152
 
33201
- 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, 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 };
33202
33154
  //# sourceMappingURL=index.js.map
33203
33155
  //# sourceMappingURL=index.js.map