mixdog 0.9.0 → 0.9.2

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 (240) hide show
  1. package/package.json +10 -3
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/background-task-meta-smoke.mjs +1 -1
  6. package/scripts/bench-run.mjs +262 -0
  7. package/scripts/compact-smoke.mjs +12 -0
  8. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  9. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  10. package/scripts/internal-comms-bench.mjs +727 -0
  11. package/scripts/internal-comms-smoke.mjs +75 -0
  12. package/scripts/lead-workflow-smoke.mjs +4 -4
  13. package/scripts/live-worker-smoke.mjs +9 -9
  14. package/scripts/output-style-bench.mjs +285 -0
  15. package/scripts/output-style-smoke.mjs +13 -10
  16. package/scripts/patch-replay.mjs +90 -0
  17. package/scripts/provider-stream-stall-test.mjs +276 -0
  18. package/scripts/provider-toolcall-test.mjs +599 -1
  19. package/scripts/routing-corpus.mjs +281 -0
  20. package/scripts/session-bench.mjs +1526 -0
  21. package/scripts/session-diag.mjs +595 -0
  22. package/scripts/session-ingest-smoke.mjs +2 -2
  23. package/scripts/task-bench.mjs +207 -0
  24. package/scripts/tool-failures.mjs +6 -6
  25. package/scripts/tool-smoke.mjs +306 -66
  26. package/scripts/toolcall-args-test.mjs +81 -0
  27. package/src/agents/debugger/AGENT.md +4 -4
  28. package/src/agents/heavy-worker/AGENT.md +4 -2
  29. package/src/agents/reviewer/AGENT.md +4 -4
  30. package/src/agents/worker/AGENT.md +4 -2
  31. package/src/app.mjs +10 -6
  32. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  33. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  34. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  35. package/src/headless-role.mjs +14 -14
  36. package/src/help.mjs +1 -0
  37. package/src/lib/mixdog-debug.cjs +0 -22
  38. package/src/lib/plugin-paths.cjs +1 -7
  39. package/src/lib/rules-builder.cjs +34 -56
  40. package/src/mixdog-session-runtime.mjs +710 -319
  41. package/src/output-styles/default.md +12 -7
  42. package/src/output-styles/minimal.md +25 -0
  43. package/src/output-styles/oneline.md +21 -0
  44. package/src/output-styles/simple.md +10 -9
  45. package/src/repl.mjs +12 -4
  46. package/src/rules/agent/00-common.md +7 -5
  47. package/src/rules/agent/30-explorer.md +7 -8
  48. package/src/rules/lead/01-general.md +3 -1
  49. package/src/rules/lead/lead-tool.md +7 -0
  50. package/src/rules/shared/01-tool.md +17 -12
  51. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  52. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  53. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  54. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  55. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  56. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  57. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  58. package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
  59. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  60. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  61. package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
  62. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  63. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  65. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +359 -106
  66. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +63 -51
  67. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
  68. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +86 -30
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +254 -280
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +191 -50
  75. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  76. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  78. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +265 -1
  79. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  80. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  81. package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
  82. package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
  83. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  84. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  85. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  86. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  87. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -32
  89. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  90. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +1 -44
  91. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  92. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
  93. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  94. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  95. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  96. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  97. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
  99. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  100. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
  104. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  105. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
  106. package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
  107. package/src/runtime/channels/backends/discord.mjs +99 -9
  108. package/src/runtime/channels/backends/telegram.mjs +501 -0
  109. package/src/runtime/channels/index.mjs +441 -1254
  110. package/src/runtime/channels/lib/cli-worker-host.mjs +1 -8
  111. package/src/runtime/channels/lib/config.mjs +54 -3
  112. package/src/runtime/channels/lib/drop-trace.mjs +1 -1
  113. package/src/runtime/channels/lib/executor.mjs +0 -3
  114. package/src/runtime/channels/lib/format.mjs +4 -2
  115. package/src/runtime/channels/lib/memory-client.mjs +0 -38
  116. package/src/runtime/channels/lib/output-forwarder.mjs +77 -71
  117. package/src/runtime/channels/lib/runtime-paths.mjs +29 -6
  118. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  119. package/src/runtime/channels/lib/session-discovery.mjs +0 -4
  120. package/src/runtime/channels/lib/telegram-format.mjs +283 -0
  121. package/src/runtime/channels/lib/tool-format.mjs +1 -2
  122. package/src/runtime/channels/lib/transcript-discovery.mjs +20 -11
  123. package/src/runtime/channels/lib/webhook.mjs +59 -31
  124. package/src/runtime/channels/tool-defs.mjs +1 -1
  125. package/src/runtime/lib/keychain-cjs.cjs +0 -1
  126. package/src/runtime/memory/data/runtime-manifest.json +6 -7
  127. package/src/runtime/memory/index.mjs +187 -43
  128. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  129. package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
  130. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -4
  131. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  132. package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
  133. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  134. package/src/runtime/memory/lib/memory-ops-policy.mjs +0 -1
  135. package/src/runtime/memory/lib/memory.mjs +101 -4
  136. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  137. package/src/runtime/memory/lib/runtime-fetcher.mjs +43 -18
  138. package/src/runtime/memory/lib/session-ingest.mjs +116 -7
  139. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  140. package/src/runtime/memory/tool-defs.mjs +6 -3
  141. package/src/runtime/search/index.mjs +2 -7
  142. package/src/runtime/search/lib/config.mjs +0 -4
  143. package/src/runtime/search/lib/state.mjs +1 -15
  144. package/src/runtime/search/lib/web-tools.mjs +0 -1
  145. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  146. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  147. package/src/runtime/shared/child-spawn-gate.mjs +0 -6
  148. package/src/runtime/shared/config.mjs +9 -0
  149. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  150. package/src/runtime/shared/schedules-store.mjs +21 -19
  151. package/src/runtime/shared/tool-surface.mjs +98 -13
  152. package/src/runtime/shared/transcript-writer.mjs +129 -0
  153. package/src/runtime/shared/update-checker.mjs +214 -0
  154. package/src/standalone/agent-tool.mjs +255 -109
  155. package/src/standalone/channel-admin.mjs +133 -40
  156. package/src/standalone/channel-worker.mjs +8 -291
  157. package/src/standalone/explore-tool.mjs +2 -2
  158. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  159. package/src/standalone/provider-admin.mjs +11 -0
  160. package/src/standalone/seeds.mjs +1 -11
  161. package/src/standalone/usage-dashboard.mjs +1 -1
  162. package/src/tui/App.jsx +2137 -750
  163. package/src/tui/components/ConfirmBar.jsx +47 -0
  164. package/src/tui/components/ContextPanel.jsx +5 -3
  165. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  166. package/src/tui/components/Markdown.jsx +22 -98
  167. package/src/tui/components/Message.jsx +14 -35
  168. package/src/tui/components/Picker.jsx +87 -12
  169. package/src/tui/components/PromptInput.jsx +146 -9
  170. package/src/tui/components/QueuedCommands.jsx +1 -1
  171. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  172. package/src/tui/components/Spinner.jsx +7 -7
  173. package/src/tui/components/StatusLine.jsx +40 -21
  174. package/src/tui/components/TextEntryPanel.jsx +51 -7
  175. package/src/tui/components/ToolExecution.jsx +177 -100
  176. package/src/tui/components/TurnDone.jsx +4 -4
  177. package/src/tui/components/UsagePanel.jsx +1 -1
  178. package/src/tui/components/tool-output-format.mjs +312 -40
  179. package/src/tui/components/tool-output-format.test.mjs +180 -1
  180. package/src/tui/display-width.mjs +69 -0
  181. package/src/tui/display-width.test.mjs +35 -0
  182. package/src/tui/dist/index.mjs +7324 -2393
  183. package/src/tui/engine.mjs +287 -126
  184. package/src/tui/index.jsx +117 -7
  185. package/src/tui/keyboard-protocol.mjs +42 -0
  186. package/src/tui/lib/voice-recorder.mjs +453 -0
  187. package/src/tui/markdown/format-token.mjs +354 -142
  188. package/src/tui/markdown/format-token.test.mjs +155 -17
  189. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  190. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  191. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  192. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  193. package/src/tui/markdown/table-layout.mjs +9 -9
  194. package/src/tui/paste-attachments.mjs +0 -11
  195. package/src/tui/prompt-history-store.mjs +129 -0
  196. package/src/tui/prompt-history-store.test.mjs +52 -0
  197. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  198. package/src/tui/theme.mjs +41 -647
  199. package/src/tui/themes/base.mjs +86 -0
  200. package/src/tui/themes/basic.mjs +85 -0
  201. package/src/tui/themes/catppuccin.mjs +72 -0
  202. package/src/tui/themes/dracula.mjs +70 -0
  203. package/src/tui/themes/everforest.mjs +71 -0
  204. package/src/tui/themes/gruvbox.mjs +71 -0
  205. package/src/tui/themes/index.mjs +71 -0
  206. package/src/tui/themes/indigo.mjs +78 -0
  207. package/src/tui/themes/kanagawa.mjs +80 -0
  208. package/src/tui/themes/light.mjs +81 -0
  209. package/src/tui/themes/nord.mjs +72 -0
  210. package/src/tui/themes/onedark.mjs +16 -0
  211. package/src/tui/themes/rosepine.mjs +70 -0
  212. package/src/tui/themes/teal.mjs +81 -0
  213. package/src/tui/themes/tokyonight.mjs +79 -0
  214. package/src/tui/themes/utils.mjs +106 -0
  215. package/src/tui/themes/warm.mjs +79 -0
  216. package/src/tui/transcript-tool-failures.mjs +13 -2
  217. package/src/ui/markdown.mjs +1 -1
  218. package/src/ui/model-display.mjs +2 -2
  219. package/src/ui/statusline.mjs +26 -27
  220. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -623
  221. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  222. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  223. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  224. package/src/workflows/default/WORKFLOW.md +39 -12
  225. package/src/workflows/sequential/WORKFLOW.md +46 -0
  226. package/src/workflows/solo/WORKFLOW.md +7 -0
  227. package/vendor/ink/build/display-width.js +62 -0
  228. package/vendor/ink/build/ink.js +154 -20
  229. package/vendor/ink/build/measure-text.js +4 -1
  230. package/vendor/ink/build/output.js +115 -9
  231. package/vendor/ink/build/render-node-to-output.js +4 -1
  232. package/vendor/ink/build/render.js +4 -0
  233. package/src/hooks/lib/permission-rules.cjs +0 -170
  234. package/src/hooks/lib/settings-loader.cjs +0 -112
  235. package/src/lib/hook-pipe-path.cjs +0 -10
  236. package/src/output-styles/extreme-simple.md +0 -20
  237. package/src/rules/lead/04-workflow.md +0 -51
  238. package/src/runtime/channels/lib/hook-pipe-server.mjs +0 -671
  239. package/src/workflows/default/workflow.json +0 -13
  240. package/src/workflows/solo/workflow.json +0 -7
@@ -66,7 +66,7 @@ function codePointTokenWeight(cp) {
66
66
  // surrogate-pair emoji are scored once, at the high emoji weight), takes the
67
67
  // max of the weighted sum and the chars/4 ASCII floor, then applies the safety
68
68
  // multiplier. Always overcounts relative to chars/4 for non-ASCII text.
69
- function estimateTokens(text) {
69
+ export function estimateTokens(text) {
70
70
  const s = String(text ?? '');
71
71
  if (s.length === 0) return 0;
72
72
  let weighted = 0;
@@ -74,7 +74,7 @@ function estimateTokens(text) {
74
74
  const asciiFloor = s.length / 4; // never below the legacy chars/4 lower bound
75
75
  return Math.ceil(Math.max(weighted, asciiFloor) * TOKEN_ESTIMATE_SAFETY_MULTIPLIER);
76
76
  }
77
- function messageEstimateText(m) {
77
+ export function messageEstimateText(m) {
78
78
  if (!m || typeof m !== 'object') return '';
79
79
  let text = typeof m.content === 'string' ? m.content : JSON.stringify(m.content ?? '');
80
80
  if (m.role === 'assistant' && Array.isArray(m.toolCalls) && m.toolCalls.length) {
@@ -84,13 +84,260 @@ function messageEstimateText(m) {
84
84
  if (m.role === 'tool' && m.toolCallId) text += `\n${m.toolCallId}`;
85
85
  return text;
86
86
  }
87
- function estimateMessageTokens(m) {
87
+ export function estimateMessageTokens(m) {
88
88
  return estimateTokens(messageEstimateText(m)) + 4;
89
89
  }
90
90
  export function estimateMessagesTokens(messages) {
91
91
  return messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
92
92
  }
93
93
 
94
+ export const DEFAULT_COMPACTION_BUFFER_TOKENS = 0;
95
+ export const DEFAULT_COMPACTION_BUFFER_RATIO = 0.1;
96
+ export const MAX_COMPACTION_BUFFER_RATIO = 0.25;
97
+ export const DEFAULT_COMPACTION_KEEP_TOKENS = 8_000;
98
+ const LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO = 0.1;
99
+
100
+ export function positiveTokenInt(value) {
101
+ const n = Number(value);
102
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
103
+ }
104
+
105
+ function envTokenInt(name) {
106
+ return positiveTokenInt(process.env[name]);
107
+ }
108
+
109
+ export function normalizeCompactionBufferRatio(value, fallback = DEFAULT_COMPACTION_BUFFER_RATIO) {
110
+ const n = Number(value);
111
+ if (Number.isFinite(n) && n > 0) return n > 1 ? n / 100 : n;
112
+ return fallback;
113
+ }
114
+
115
+ // Percent-named inputs (bufferPercent / bufferPct / *_BUFFER_PERCENT) carry a
116
+ // PERCENT: 1 means 1% (0.01). Ratio-named inputs (bufferRatio / bufferFraction)
117
+ // carry a fraction: 0.01 means 1%, and a legacy value > 1 is read as a percent.
118
+ export function resolveBufferRatioCandidate(percentInputs = [], ratioInputs = []) {
119
+ for (const raw of percentInputs) {
120
+ const n = Number(raw);
121
+ if (Number.isFinite(n) && n > 0) return Math.min(1, n / 100);
122
+ }
123
+ for (const raw of ratioInputs) {
124
+ const n = Number(raw);
125
+ if (Number.isFinite(n) && n > 0) return n > 1 ? n / 100 : n;
126
+ }
127
+ return null;
128
+ }
129
+
130
+ export function resolveCompactBufferRatio(cfg = {}) {
131
+ const resolved = resolveBufferRatioCandidate(
132
+ [cfg.bufferPercent, cfg.bufferPct, process.env.MIXDOG_AGENT_COMPACT_BUFFER_PERCENT],
133
+ [cfg.bufferRatio, cfg.bufferFraction, process.env.MIXDOG_AGENT_COMPACT_BUFFER_RATIO],
134
+ );
135
+ return normalizeCompactionBufferRatio(resolved, DEFAULT_COMPACTION_BUFFER_RATIO);
136
+ }
137
+
138
+ export function compactionBufferTokensForBoundary(boundaryTokens, opts = {}) {
139
+ const boundary = Math.max(0, Math.floor(Number(boundaryTokens) || 0));
140
+ const explicit = Math.max(0, Math.floor(Number(opts.explicitTokens) || 0));
141
+ if (!boundary) return explicit;
142
+ const maxRatio = normalizeCompactionBufferRatio(opts.maxRatio, MAX_COMPACTION_BUFFER_RATIO);
143
+ const cap = Math.max(0, Math.floor(boundary * maxRatio));
144
+ if (explicit > 0) return Math.max(0, Math.min(explicit, cap));
145
+ const ratio = normalizeCompactionBufferRatio(opts.ratio, DEFAULT_COMPACTION_BUFFER_RATIO);
146
+ return Math.max(0, Math.min(Math.floor(boundary * ratio), cap));
147
+ }
148
+
149
+ export function isPersistedZeroBufferTelemetry(cfg = {}, boundaryTokens = 0) {
150
+ const boundary = positiveTokenInt(boundaryTokens);
151
+ if (!boundary) return false;
152
+ if (envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')) return false;
153
+ for (const envName of ['MIXDOG_AGENT_COMPACT_BUFFER_PERCENT', 'MIXDOG_AGENT_COMPACT_BUFFER_RATIO']) {
154
+ const n = Number(process.env[envName]);
155
+ if (Number.isFinite(n) && n > 0) return false;
156
+ }
157
+ for (const key of ['bufferPercent', 'bufferPct', 'bufferFraction']) {
158
+ const n = Number(cfg?.[key]);
159
+ if (Number.isFinite(n) && n > 0) return false;
160
+ }
161
+ const ratio = Number(cfg?.bufferRatio);
162
+ if (Number.isFinite(ratio) && ratio > 0) return false;
163
+ const explicitTokens = Number(cfg?.bufferTokens ?? cfg?.buffer);
164
+ if (!Number.isFinite(explicitTokens) || explicitTokens !== 0) return false;
165
+ return true;
166
+ }
167
+
168
+ export function isLegacyDefaultBufferTelemetry(cfg = {}, boundaryTokens = 0) {
169
+ const boundary = positiveTokenInt(boundaryTokens);
170
+ if (!boundary) return false;
171
+ if (envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')) return false;
172
+ for (const envName of ['MIXDOG_AGENT_COMPACT_BUFFER_PERCENT', 'MIXDOG_AGENT_COMPACT_BUFFER_RATIO']) {
173
+ const n = Number(process.env[envName]);
174
+ if (Number.isFinite(n) && n > 0) return false;
175
+ }
176
+ // Percent/fraction-named fields are operator config. Legacy/default
177
+ // telemetry persisted bufferTokens + bufferRatio after a check/compact pass.
178
+ for (const key of ['bufferPercent', 'bufferPct', 'bufferFraction']) {
179
+ const n = Number(cfg?.[key]);
180
+ if (Number.isFinite(n) && n > 0) return false;
181
+ }
182
+ const explicitTokens = positiveTokenInt(cfg?.bufferTokens ?? cfg?.buffer);
183
+ const ratio = Number(cfg?.bufferRatio);
184
+ if (!explicitTokens || !Number.isFinite(ratio) || Math.abs(ratio - LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO) > 1e-9) return false;
185
+ const expectedTokens = Math.floor(boundary * LEGACY_DEFAULT_COMPACTION_BUFFER_RATIO);
186
+ const cfgBoundary = positiveTokenInt(cfg?.boundaryTokens);
187
+ const cfgTrigger = positiveTokenInt(cfg?.triggerTokens);
188
+ return explicitTokens === expectedTokens
189
+ || (cfgBoundary === boundary && cfgTrigger > 0 && explicitTokens === Math.max(0, boundary - cfgTrigger));
190
+ }
191
+
192
+ export function compactBufferConfigForBoundary(cfg = {}, boundaryTokens = 0) {
193
+ const base = cfg || {};
194
+ if (!isLegacyDefaultBufferTelemetry(base, boundaryTokens)
195
+ && !isPersistedZeroBufferTelemetry(base, boundaryTokens)) {
196
+ return base;
197
+ }
198
+ return {
199
+ ...base,
200
+ bufferTokens: null,
201
+ buffer: null,
202
+ bufferRatio: null,
203
+ };
204
+ }
205
+
206
+ export function resolveCompactBufferTokens(boundaryTokens, cfg = {}, opts = {}) {
207
+ const boundary = positiveTokenInt(boundaryTokens);
208
+ const effectiveCfg = compactBufferConfigForBoundary(cfg, boundary);
209
+ const configured = positiveTokenInt(effectiveCfg.bufferTokens ?? effectiveCfg.buffer)
210
+ || envTokenInt('MIXDOG_AGENT_COMPACT_BUFFER_TOKENS')
211
+ || 0;
212
+ if (!boundary) return configured || positiveTokenInt(opts.defaultTokens) || DEFAULT_COMPACTION_BUFFER_TOKENS;
213
+ return compactionBufferTokensForBoundary(boundary, {
214
+ explicitTokens: configured,
215
+ ratio: resolveCompactBufferRatio(effectiveCfg),
216
+ maxRatio: opts.maxRatio ?? MAX_COMPACTION_BUFFER_RATIO,
217
+ });
218
+ }
219
+
220
+ export function resolveCompactTriggerTokens(sessionOrConfig = {}, boundaryTokens = 0) {
221
+ const boundary = positiveTokenInt(boundaryTokens);
222
+ if (!boundary) return null;
223
+ const cfg = sessionOrConfig?.compaction || sessionOrConfig || {};
224
+ const autoLimit = positiveTokenInt(sessionOrConfig?.autoCompactTokenLimit ?? cfg?.autoCompactTokenLimit);
225
+ if (autoLimit && autoLimit < boundary) return Math.max(1, autoLimit);
226
+ const buffer = resolveCompactBufferTokens(boundary, cfg);
227
+ return Math.max(1, boundary - buffer);
228
+ }
229
+
230
+ function stripSystemReminder(text) {
231
+ return String(text || '')
232
+ .replace(/^\s*<system-reminder>\s*/i, '')
233
+ .replace(/\s*<\/system-reminder>\s*$/i, '')
234
+ .trim();
235
+ }
236
+
237
+ function splitMarkdownSections(text) {
238
+ const sections = [];
239
+ let current = [];
240
+ for (const line of String(text || '').split(/\r?\n/)) {
241
+ if (/^#\s+/.test(line) && current.length) {
242
+ const body = current.join('\n').trim();
243
+ if (body) sections.push(body);
244
+ current = [line];
245
+ } else {
246
+ current.push(line);
247
+ }
248
+ }
249
+ const tail = current.join('\n').trim();
250
+ if (tail) sections.push(tail);
251
+ return sections;
252
+ }
253
+
254
+ function reminderSectionBucket(section) {
255
+ const heading = String(section.match(/^#\s+([^\n]+)/)?.[1] || '').trim().toLowerCase();
256
+ if (heading.includes('core memory')) return 'memory';
257
+ if (heading.includes('active workflow') || heading.includes('available agents') || heading.includes('workflow')) return 'workflow';
258
+ if (heading.includes('workspace')) return 'workspace';
259
+ if (heading.includes('environment')) return 'environment';
260
+ return 'other';
261
+ }
262
+
263
+ export function summarizeContextMessages(messages) {
264
+ const rows = {
265
+ system: { count: 0, tokens: 0 },
266
+ user: { count: 0, tokens: 0 },
267
+ assistant: { count: 0, tokens: 0 },
268
+ tool: { count: 0, tokens: 0 },
269
+ other: { count: 0, tokens: 0 },
270
+ };
271
+ const semantic = {
272
+ system: { count: 0, tokens: 0 },
273
+ chat: { count: 0, tokens: 0 },
274
+ assistant: { count: 0, tokens: 0 },
275
+ toolResults: { count: 0, tokens: 0 },
276
+ reminders: { count: 0, tokens: 0, otherTokens: 0 },
277
+ workflow: { tokens: 0 },
278
+ memory: { tokens: 0 },
279
+ workspace: { tokens: 0 },
280
+ environment: { tokens: 0 },
281
+ other: { tokens: 0 },
282
+ };
283
+ let toolCallCount = 0;
284
+ let toolCallTokens = 0;
285
+ let toolResultCount = 0;
286
+ let toolResultTokens = 0;
287
+ for (const message of messages || []) {
288
+ const role = rows[message?.role] ? message.role : 'other';
289
+ const text = messageEstimateText(message);
290
+ const tokens = estimateMessageTokens(message);
291
+ rows[role].count += 1;
292
+ rows[role].tokens += tokens;
293
+ if (role === 'system') {
294
+ semantic.system.count += 1;
295
+ semantic.system.tokens += tokens;
296
+ } else if (role === 'user') {
297
+ if (String(text || '').trim().startsWith('<system-reminder>')) {
298
+ semantic.reminders.count += 1;
299
+ semantic.reminders.tokens += tokens;
300
+ let sectionTokens = 0;
301
+ for (const section of splitMarkdownSections(stripSystemReminder(text))) {
302
+ const bucket = reminderSectionBucket(section);
303
+ const sectionTokenCount = estimateTokens(section);
304
+ semantic[bucket].tokens += sectionTokenCount;
305
+ sectionTokens += sectionTokenCount;
306
+ }
307
+ semantic.reminders.otherTokens += Math.max(0, tokens - sectionTokens);
308
+ } else {
309
+ semantic.chat.count += 1;
310
+ semantic.chat.tokens += tokens;
311
+ }
312
+ } else if (role === 'assistant') {
313
+ semantic.assistant.count += 1;
314
+ semantic.assistant.tokens += tokens;
315
+ } else if (role === 'tool') {
316
+ semantic.toolResults.count += 1;
317
+ semantic.toolResults.tokens += tokens;
318
+ }
319
+ if (message?.role === 'assistant' && Array.isArray(message.toolCalls) && message.toolCalls.length) {
320
+ toolCallCount += message.toolCalls.length;
321
+ try { toolCallTokens += estimateTokens(JSON.stringify(message.toolCalls)); }
322
+ catch { toolCallTokens += estimateTokens(`[${message.toolCalls.length} tool calls]`); }
323
+ }
324
+ if (message?.role === 'tool') {
325
+ toolResultCount += 1;
326
+ toolResultTokens += tokens;
327
+ }
328
+ }
329
+ return {
330
+ count: Array.isArray(messages) ? messages.length : 0,
331
+ estimatedTokens: Array.isArray(messages) ? estimateMessagesTokens(messages) : 0,
332
+ roles: rows,
333
+ semantic,
334
+ toolCallCount,
335
+ toolCallTokens,
336
+ toolResultCount,
337
+ toolResultTokens,
338
+ };
339
+ }
340
+
94
341
  // Per-request overhead the provider injects that never appears in the
95
342
  // `messages` array: function-calling preamble + system-prompt framing the
96
343
  // provider wraps around the request. The chars/4 message estimate misses all