neuro-cli 4.1.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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,436 @@
1
+ // ============================================================
2
+ // NeuroCLI - GAP-37: Auto-Compact with Model-Aware Context Management
3
+ // Automatic context compaction that adapts to model context windows
4
+ // ============================================================
5
+ import { MODELS } from '../api/models.js';
6
+ import { ContextManager } from './context.js';
7
+ // ---- Token Estimation ----
8
+ const CJK_RANGES = /[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
9
+ function estimateTokensForText(text) {
10
+ const cjkMatches = text.match(CJK_RANGES);
11
+ const cjkCount = cjkMatches ? cjkMatches.length : 0;
12
+ const otherCount = text.length - cjkCount;
13
+ return Math.ceil(cjkCount / 2 + otherCount / 4);
14
+ }
15
+ // ---- Default Config ----
16
+ export const DEFAULT_AUTO_COMPACT_CONFIG = {
17
+ enabled: true,
18
+ warningThreshold: 0.7,
19
+ compactThreshold: 0.85,
20
+ emergencyThreshold: 0.95,
21
+ preserveRecentCount: 6,
22
+ preserveSystemPrompt: true,
23
+ compactStrategy: 'hybrid',
24
+ tokenBudget: {
25
+ systemPrompt: 0.15, // 15% of context window
26
+ conversation: 0.45, // 45% of context window
27
+ tools: 0.10, // 10% of context window
28
+ output: 0.30, // 30% of context window (reserved for output)
29
+ },
30
+ };
31
+ // ---- Fallback Context Window ----
32
+ const DEFAULT_CONTEXT_WINDOW = 128000;
33
+ export class AutoCompactManager {
34
+ config;
35
+ currentModelId;
36
+ contextManager = null;
37
+ compactor = null;
38
+ // Callbacks
39
+ warningCallbacks = [];
40
+ compactCallbacks = [];
41
+ emergencyCallbacks = [];
42
+ // Track last level to avoid duplicate callbacks
43
+ lastLevel = 'normal';
44
+ constructor(config = {}) {
45
+ this.config = { ...DEFAULT_AUTO_COMPACT_CONFIG, ...config };
46
+ // Deep merge tokenBudget
47
+ if (config.tokenBudget) {
48
+ this.config.tokenBudget = {
49
+ ...DEFAULT_AUTO_COMPACT_CONFIG.tokenBudget,
50
+ ...config.tokenBudget,
51
+ };
52
+ }
53
+ this.currentModelId = '';
54
+ }
55
+ /**
56
+ * Update the current model and recalculate model-aware thresholds.
57
+ * Rebuilds ContextManager and ContextCompactor for the new model.
58
+ */
59
+ setModel(modelId) {
60
+ this.currentModelId = modelId;
61
+ const model = MODELS[modelId];
62
+ const contextWindow = model?.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
63
+ this.contextManager = new ContextManager(modelId, contextWindow);
64
+ // ContextCompactor requires a ContextManager and an OpenRouterClient.
65
+ // We keep a null reference because we implement our own compaction logic
66
+ // that delegates to the ContextManager's token counting and summarization,
67
+ // falling back to the compactor only when available.
68
+ this.compactor = null;
69
+ }
70
+ /**
71
+ * Set the ContextCompactor for LLM-backed summarization.
72
+ * This is optional — without it, auto-compact uses local strategies only.
73
+ */
74
+ setCompactor(compactor) {
75
+ this.compactor = compactor;
76
+ }
77
+ // ---- Monitoring ----
78
+ /**
79
+ * Compute detailed context usage breakdown for the given messages and model.
80
+ */
81
+ getContextUsage(messages, modelId) {
82
+ const model = MODELS[modelId];
83
+ const maxTokens = model?.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
84
+ // Calculate token counts per category
85
+ let systemPromptTokens = 0;
86
+ let conversationTokens = 0;
87
+ let toolsTokens = 0;
88
+ for (const msg of messages) {
89
+ const msgTokens = this.estimateMessageTokens(msg);
90
+ if (msg.role === 'system') {
91
+ systemPromptTokens += msgTokens;
92
+ }
93
+ else if (msg.role === 'tool') {
94
+ toolsTokens += msgTokens;
95
+ }
96
+ else {
97
+ conversationTokens += msgTokens;
98
+ }
99
+ // Tool calls on assistant messages count toward tools budget
100
+ if (msg.toolCalls) {
101
+ for (const tc of msg.toolCalls) {
102
+ toolsTokens += estimateTokensForText(tc.function.name + tc.function.arguments);
103
+ }
104
+ }
105
+ }
106
+ const totalTokens = systemPromptTokens + conversationTokens + toolsTokens;
107
+ const outputReserve = Math.floor(maxTokens * this.config.tokenBudget.output);
108
+ const availableTokens = Math.max(0, maxTokens - totalTokens - outputReserve);
109
+ const usagePercent = totalTokens / maxTokens;
110
+ let level = 'normal';
111
+ if (usagePercent >= this.config.emergencyThreshold) {
112
+ level = 'emergency';
113
+ }
114
+ else if (usagePercent >= this.config.compactThreshold) {
115
+ level = 'compact';
116
+ }
117
+ else if (usagePercent >= this.config.warningThreshold) {
118
+ level = 'warning';
119
+ }
120
+ return {
121
+ totalTokens,
122
+ maxTokens,
123
+ usagePercent,
124
+ breakdown: {
125
+ systemPrompt: systemPromptTokens,
126
+ conversation: conversationTokens,
127
+ tools: toolsTokens,
128
+ available: availableTokens,
129
+ },
130
+ level,
131
+ };
132
+ }
133
+ /**
134
+ * Determine whether compaction is needed and at what level.
135
+ * Fires callbacks when the level transitions.
136
+ */
137
+ shouldCompact(messages, modelId) {
138
+ const usage = this.getContextUsage(messages, modelId);
139
+ const needed = usage.level === 'compact' || usage.level === 'emergency';
140
+ // Fire callbacks on level transitions
141
+ if (usage.level !== this.lastLevel) {
142
+ if (usage.level === 'warning') {
143
+ for (const cb of this.warningCallbacks) {
144
+ cb(usage);
145
+ }
146
+ }
147
+ else if (usage.level === 'compact') {
148
+ // Also fire warning callbacks at compact level if transitioning from normal
149
+ if (this.lastLevel === 'normal') {
150
+ for (const cb of this.warningCallbacks) {
151
+ cb(usage);
152
+ }
153
+ }
154
+ }
155
+ else if (usage.level === 'emergency') {
156
+ for (const cb of this.emergencyCallbacks) {
157
+ cb(usage);
158
+ }
159
+ }
160
+ this.lastLevel = usage.level;
161
+ }
162
+ return { needed, level: usage.level };
163
+ }
164
+ // ---- Compaction ----
165
+ /**
166
+ * Compact messages according to the configured or specified strategy.
167
+ * Returns the compacted message list along with usage metrics.
168
+ */
169
+ async compact(messages, strategy) {
170
+ if (!this.config.enabled) {
171
+ return messages;
172
+ }
173
+ const effectiveModelId = this.currentModelId || 'openrouter/free';
174
+ const usage = this.getContextUsage(messages, effectiveModelId);
175
+ const effectiveStrategy = strategy ?? this.config.compactStrategy;
176
+ let result;
177
+ switch (effectiveStrategy) {
178
+ case 'summarize':
179
+ result = await this.compactBySummarize(messages, usage);
180
+ break;
181
+ case 'drop-oldest':
182
+ result = this.compactByDropOldest(messages, usage);
183
+ break;
184
+ case 'hybrid':
185
+ result = await this.compactByHybrid(messages, usage);
186
+ break;
187
+ default:
188
+ result = this.compactByDropOldest(messages, usage);
189
+ }
190
+ // Fire compact callbacks
191
+ const newUsage = this.getContextUsage(result, effectiveModelId);
192
+ for (const cb of this.compactCallbacks) {
193
+ cb(newUsage, result);
194
+ }
195
+ // Update tracked level
196
+ this.lastLevel = newUsage.level;
197
+ return result;
198
+ }
199
+ /**
200
+ * Strategy: Summarize older messages into a single context message.
201
+ * Keeps last N messages verbatim, summarizes everything else.
202
+ */
203
+ async compactBySummarize(messages, usage) {
204
+ const model = MODELS[this.currentModelId];
205
+ const maxTokens = model?.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
206
+ const targetTokens = Math.floor(maxTokens * this.config.compactThreshold * 0.75); // compact to 75% of threshold
207
+ const { systemMessages, nonSystemMessages } = this.partitionMessages(messages);
208
+ if (nonSystemMessages.length <= this.config.preserveRecentCount) {
209
+ // Nothing to summarize
210
+ return messages;
211
+ }
212
+ // Split into "old" and "recent"
213
+ const preserveCount = this.config.preserveRecentCount;
214
+ const recentMessages = nonSystemMessages.slice(-preserveCount);
215
+ const oldMessages = nonSystemMessages.slice(0, -preserveCount);
216
+ // Build summary of old messages
217
+ const summaryContent = this.buildSummary(oldMessages);
218
+ const summaryMessage = {
219
+ role: 'system',
220
+ content: `## Earlier Conversation Summary\n${summaryContent}`,
221
+ timestamp: Date.now(),
222
+ };
223
+ let result = [...systemMessages, summaryMessage, ...recentMessages];
224
+ // If we still exceed the target, try LLM-backed compaction
225
+ const resultUsage = this.getContextUsage(result, this.currentModelId);
226
+ if (resultUsage.usagePercent >= this.config.compactThreshold && this.compactor) {
227
+ try {
228
+ const compactionResult = await this.compactor.compact(result);
229
+ result = compactionResult.messages;
230
+ }
231
+ catch {
232
+ // Fall through — keep local summary result
233
+ }
234
+ }
235
+ // If still over budget, progressively drop oldest from the old messages section
236
+ if (this.getContextUsage(result, this.currentModelId).usagePercent >= this.config.emergencyThreshold) {
237
+ result = this.emergencyDrop(result);
238
+ }
239
+ return result;
240
+ }
241
+ /**
242
+ * Strategy: Drop oldest messages (except system prompt and recent).
243
+ */
244
+ compactByDropOldest(messages, usage) {
245
+ const model = MODELS[this.currentModelId];
246
+ const maxTokens = model?.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
247
+ const targetTokens = Math.floor(maxTokens * this.config.compactThreshold * 0.75);
248
+ const { systemMessages, nonSystemMessages } = this.partitionMessages(messages);
249
+ if (nonSystemMessages.length <= this.config.preserveRecentCount) {
250
+ return messages;
251
+ }
252
+ // Start by keeping system + recent; progressively add older messages from newest to oldest
253
+ const preserveCount = this.config.preserveRecentCount;
254
+ const recentMessages = nonSystemMessages.slice(-preserveCount);
255
+ const olderMessages = nonSystemMessages.slice(0, -preserveCount);
256
+ // Work backwards through older messages, adding as many as we can afford
257
+ const keptOlder = [];
258
+ let currentTokens = this.estimateMessagesTokens([...systemMessages, ...recentMessages]);
259
+ for (let i = olderMessages.length - 1; i >= 0; i--) {
260
+ const msgTokens = this.estimateMessageTokens(olderMessages[i]);
261
+ if (currentTokens + msgTokens <= targetTokens) {
262
+ keptOlder.unshift(olderMessages[i]);
263
+ currentTokens += msgTokens;
264
+ }
265
+ else {
266
+ break;
267
+ }
268
+ }
269
+ // If we dropped some messages, add a brief context note
270
+ const droppedCount = olderMessages.length - keptOlder.length;
271
+ let result;
272
+ if (droppedCount > 0) {
273
+ const contextNote = {
274
+ role: 'system',
275
+ content: `## Context Note\n${droppedCount} earlier message(s) were dropped to fit the context window. Key details may have been lost.`,
276
+ timestamp: Date.now(),
277
+ };
278
+ result = [...systemMessages, contextNote, ...keptOlder, ...recentMessages];
279
+ }
280
+ else {
281
+ result = [...systemMessages, ...keptOlder, ...recentMessages];
282
+ }
283
+ return result;
284
+ }
285
+ /**
286
+ * Strategy: Hybrid — summarize first, then drop oldest if still over budget.
287
+ */
288
+ async compactByHybrid(messages, usage) {
289
+ // First pass: summarize
290
+ const summarized = await this.compactBySummarize(messages, usage);
291
+ // Check if still over budget
292
+ const postUsage = this.getContextUsage(summarized, this.currentModelId);
293
+ if (postUsage.usagePercent < this.config.compactThreshold) {
294
+ return summarized;
295
+ }
296
+ // Second pass: drop oldest from the summarized result
297
+ return this.compactByDropOldest(summarized, postUsage);
298
+ }
299
+ /**
300
+ * Emergency compaction: keep only system prompt + last 3 exchanges (6 messages).
301
+ * This is the last resort when everything else fails.
302
+ */
303
+ emergencyDrop(messages) {
304
+ const { systemMessages, nonSystemMessages } = this.partitionMessages(messages);
305
+ // Keep exactly the last 3 exchanges (6 messages: user/assistant/tool pairs)
306
+ const emergencyPreserveCount = 6;
307
+ const recentMessages = nonSystemMessages.slice(-emergencyPreserveCount);
308
+ const emergencyNote = {
309
+ role: 'system',
310
+ content: '## Emergency Compaction\nContext was critically over budget. Older messages were dropped. Only the most recent exchanges are preserved.',
311
+ timestamp: Date.now(),
312
+ };
313
+ return [...systemMessages, emergencyNote, ...recentMessages];
314
+ }
315
+ // ---- Token Estimation ----
316
+ /**
317
+ * Estimate the number of tokens in a text string for a given model.
318
+ * Uses ~4 chars/token for English, ~2 chars/token for CJK.
319
+ */
320
+ estimateTokens(text, _modelId) {
321
+ return estimateTokensForText(text);
322
+ }
323
+ /**
324
+ * Estimate total tokens across an array of messages,
325
+ * including tool calls.
326
+ */
327
+ estimateMessagesTokens(messages) {
328
+ return messages.reduce((sum, msg) => sum + this.estimateMessageTokens(msg), 0);
329
+ }
330
+ /**
331
+ * Estimate tokens for a single message including tool calls.
332
+ */
333
+ estimateMessageTokens(msg) {
334
+ let tokens = estimateTokensForText(msg.content);
335
+ if (msg.toolCalls) {
336
+ for (const tc of msg.toolCalls) {
337
+ tokens += estimateTokensForText(tc.function.name + tc.function.arguments);
338
+ }
339
+ }
340
+ // Overhead per message: role label, formatting, etc. (~4 tokens)
341
+ tokens += 4;
342
+ return tokens;
343
+ }
344
+ // ---- Callbacks ----
345
+ onWarning(callback) {
346
+ this.warningCallbacks.push(callback);
347
+ }
348
+ onCompact(callback) {
349
+ this.compactCallbacks.push(callback);
350
+ }
351
+ onEmergency(callback) {
352
+ this.emergencyCallbacks.push(callback);
353
+ }
354
+ // ---- Helpers ----
355
+ /**
356
+ * Partition messages into system and non-system buckets.
357
+ */
358
+ partitionMessages(messages) {
359
+ const systemMessages = [];
360
+ const nonSystemMessages = [];
361
+ for (const msg of messages) {
362
+ if (msg.role === 'system') {
363
+ systemMessages.push(msg);
364
+ }
365
+ else {
366
+ nonSystemMessages.push(msg);
367
+ }
368
+ }
369
+ return { systemMessages, nonSystemMessages };
370
+ }
371
+ /**
372
+ * Build a concise summary from a list of messages.
373
+ * Prioritizes user intent, tool interactions, and assistant decisions.
374
+ */
375
+ buildSummary(messages) {
376
+ const parts = [];
377
+ for (const msg of messages) {
378
+ const preview = msg.content.length > 150
379
+ ? msg.content.slice(0, 150) + '...'
380
+ : msg.content;
381
+ switch (msg.role) {
382
+ case 'user':
383
+ parts.push(`[User]: ${preview}`);
384
+ break;
385
+ case 'assistant':
386
+ if (msg.toolCalls?.length) {
387
+ const toolNames = msg.toolCalls.map(tc => tc.function.name).join(', ');
388
+ parts.push(`[Assistant called: ${toolNames}] ${preview}`);
389
+ }
390
+ else {
391
+ parts.push(`[Assistant]: ${preview}`);
392
+ }
393
+ break;
394
+ case 'tool':
395
+ parts.push(`[Tool result]: ${preview.slice(0, 80)}`);
396
+ break;
397
+ default:
398
+ break;
399
+ }
400
+ }
401
+ return parts.join('\n');
402
+ }
403
+ }
404
+ // ---- Convenience: Auto-Compact Middleware ----
405
+ /**
406
+ * Middleware function that can be called before each LLM call.
407
+ * Checks context usage, auto-compacts if needed, and returns
408
+ * the (possibly compacted) message list.
409
+ */
410
+ export async function autoCompactMiddleware(manager, messages, modelId) {
411
+ if (!manager['config'].enabled) {
412
+ return messages;
413
+ }
414
+ manager.setModel(modelId);
415
+ const { needed, level } = manager.shouldCompact(messages, modelId);
416
+ if (!needed) {
417
+ return messages;
418
+ }
419
+ // Choose strategy based on severity
420
+ let strategy;
421
+ if (level === 'emergency') {
422
+ strategy = 'drop-oldest'; // Fastest, most aggressive
423
+ }
424
+ else if (level === 'compact') {
425
+ strategy = manager['config'].compactStrategy;
426
+ }
427
+ else {
428
+ return messages;
429
+ }
430
+ return manager.compact(messages, strategy);
431
+ }
432
+ // ---- Convenience: Create a pre-configured AutoCompactManager ----
433
+ export function createAutoCompactManager(overrides) {
434
+ return new AutoCompactManager(overrides);
435
+ }
436
+ //# sourceMappingURL=auto-compact.js.map
@@ -0,0 +1,232 @@
1
+ export type SafetyLevel = 'conservative' | 'moderate' | 'aggressive';
2
+ export interface AutoModeConfig {
3
+ /** Whether auto mode is currently enabled */
4
+ enabled: boolean;
5
+ /** Safety level controlling what operations are permitted */
6
+ safetyLevel: SafetyLevel;
7
+ /** Maximum iterations before auto mode stops (default 50) */
8
+ maxIterations: number;
9
+ /** Maximum spend in USD – 0 = unlimited */
10
+ maxCost: number;
11
+ /** Maximum execution time in ms – 0 = unlimited */
12
+ maxTimeMs: number;
13
+ /** Commands that are ALWAYS blocked, even in auto mode */
14
+ blockedCommands: string[];
15
+ /** File glob patterns that can never be modified in auto mode */
16
+ blockedPatterns: string[];
17
+ /** Auto git commit after each change */
18
+ autoCommit: boolean;
19
+ /** Auto run tests after each change */
20
+ autoTest: boolean;
21
+ /** Pause auto mode when an error is encountered */
22
+ pauseOnError: boolean;
23
+ }
24
+ export interface AutoModeStats {
25
+ /** Number of iterations completed */
26
+ iterations: number;
27
+ /** Number of files changed (created, modified, or deleted) */
28
+ filesChanged: number;
29
+ /** Number of commands executed */
30
+ commandsRun: number;
31
+ /** Total elapsed time in ms */
32
+ timeElapsedMs: number;
33
+ /** Total cost accrued in USD */
34
+ totalCost: number;
35
+ /** Number of operations blocked by safety */
36
+ blockedOperations: number;
37
+ /** Number of errors encountered */
38
+ errors: number;
39
+ }
40
+ export interface GoalDefinition {
41
+ /** Unique identifier */
42
+ id: string;
43
+ /** Short human-readable name */
44
+ name: string;
45
+ /** High-level goal description */
46
+ description: string;
47
+ /** Optional success criteria – when all pass, the goal is considered complete */
48
+ successCriteria: string[];
49
+ /** Optional list of sub-goals */
50
+ subGoals: GoalDefinition[];
51
+ /** ISO timestamp when the goal was created */
52
+ createdAt: string;
53
+ /** ISO timestamp when the goal was completed (if applicable) */
54
+ completedAt?: string;
55
+ /** Current status */
56
+ status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
57
+ /** Progress from 0 to 1 */
58
+ progress: number;
59
+ }
60
+ export interface RoutineStep {
61
+ /** Optional label for the step */
62
+ label?: string;
63
+ /** The prompt or command to execute */
64
+ prompt: string;
65
+ /** Optional model override for this step */
66
+ model?: string;
67
+ /** Whether to pause between this step and the next */
68
+ pauseAfter?: boolean;
69
+ /** Maximum iterations allowed for this step */
70
+ maxIterations?: number;
71
+ }
72
+ export interface RoutineDefinition {
73
+ /** Unique identifier */
74
+ id: string;
75
+ /** Short human-readable name */
76
+ name: string;
77
+ /** Description of what the routine accomplishes */
78
+ description: string;
79
+ /** Ordered list of steps */
80
+ steps: RoutineStep[];
81
+ /** ISO timestamp when the routine was created */
82
+ createdAt: string;
83
+ /** ISO timestamp of last run */
84
+ lastRunAt?: string;
85
+ /** Number of times this routine has been run */
86
+ runCount: number;
87
+ /** Tags for categorisation */
88
+ tags: string[];
89
+ }
90
+ export interface AutoModeCheckpoint {
91
+ id: string;
92
+ timestamp: string;
93
+ iteration: number;
94
+ goalId?: string;
95
+ routineId?: string;
96
+ snapshot: string;
97
+ }
98
+ /**
99
+ * Minimal interface that any execution engine must satisfy for AutoMode
100
+ * to orchestrate it. This avoids a hard import of NeuroEngine while
101
+ * keeping the two modules loosely coupled.
102
+ */
103
+ export interface AutoModeEngine {
104
+ /** Run a single prompt through the engine and return the assistant text */
105
+ runPrompt(prompt: string, model?: string): Promise<{
106
+ text: string;
107
+ inputTokens: number;
108
+ outputTokens: number;
109
+ cost: number;
110
+ filesChanged: number;
111
+ commandsRun: number;
112
+ error?: string;
113
+ }>;
114
+ }
115
+ export declare class AutoMode {
116
+ private config;
117
+ private stats;
118
+ private goals;
119
+ private routines;
120
+ private checkpoints;
121
+ private startTime;
122
+ private running;
123
+ private abortController;
124
+ private onStatusChange?;
125
+ constructor(config?: Partial<AutoModeConfig>, onStatusChange?: (status: AutoModeStatus) => void);
126
+ /** Enable auto mode – skip all approval prompts, run safety checks in background */
127
+ enable(): void;
128
+ /** Disable auto mode, return to interactive */
129
+ disable(): void;
130
+ /** Check whether auto mode is currently enabled */
131
+ isEnabled(): boolean;
132
+ /** Check whether auto mode is currently running a task */
133
+ isRunning(): boolean;
134
+ getConfig(): Readonly<AutoModeConfig>;
135
+ updateConfig(updates: Partial<AutoModeConfig>): void;
136
+ setSafetyLevel(level: SafetyLevel): void;
137
+ /**
138
+ * Run a task fully autonomously.
139
+ *
140
+ * The engine will be called repeatedly until:
141
+ * - maxIterations is reached
142
+ * - maxCost is exceeded
143
+ * - maxTimeMs is exceeded
144
+ * - The task appears complete (engine signals no further action needed)
145
+ * - An error occurs and pauseOnError is true
146
+ * - The user aborts
147
+ */
148
+ executeAuto(prompt: string, engine: AutoModeEngine, maxIterations?: number): Promise<AutoModeResult>;
149
+ /** Abort the currently running auto execution */
150
+ abort(): void;
151
+ /** Set a high-level goal and let the agent work towards it autonomously */
152
+ setGoal(name: string, description: string, successCriteria?: string[]): GoalDefinition;
153
+ /** Get a goal by ID */
154
+ getGoal(goalId: string): GoalDefinition | undefined;
155
+ /** List all goals */
156
+ listGoals(): GoalDefinition[];
157
+ /** Update goal progress */
158
+ updateGoalProgress(goalId: string, progress: number, status?: GoalDefinition['status']): void;
159
+ /** Add a sub-goal */
160
+ addSubGoal(parentId: string, name: string, description: string): GoalDefinition | null;
161
+ /** Delete a goal */
162
+ deleteGoal(goalId: string): boolean;
163
+ /**
164
+ * Execute a goal autonomously – will iterate until the goal is reached
165
+ * or limits are hit
166
+ */
167
+ executeGoal(goalId: string, engine: AutoModeEngine): Promise<AutoModeResult>;
168
+ /** Create a new routine */
169
+ createRoutine(name: string, description: string, steps: RoutineStep[], tags?: string[]): RoutineDefinition;
170
+ /** Get a routine by ID */
171
+ getRoutine(routineId: string): RoutineDefinition | undefined;
172
+ /** List all routines, optionally filtered by tag */
173
+ listRoutines(tag?: string): RoutineDefinition[];
174
+ /** Delete a routine */
175
+ deleteRoutine(routineId: string): boolean;
176
+ /** Update a routine's steps */
177
+ updateRoutineSteps(routineId: string, steps: RoutineStep[]): boolean;
178
+ /** Replay a saved routine */
179
+ executeRoutine(routineId: string, engine: AutoModeEngine): Promise<AutoModeResult>;
180
+ /**
181
+ * Pre-flight safety check – examines the prompt for dangerous commands
182
+ * or patterns that should be blocked even in auto mode.
183
+ */
184
+ preFlightSafetyCheck(prompt: string): {
185
+ allowed: boolean;
186
+ reason?: string;
187
+ };
188
+ /**
189
+ * Runtime safety check for a specific command about to be executed.
190
+ * This is meant to be called by the engine before running any shell command.
191
+ */
192
+ isCommandAllowed(command: string): {
193
+ allowed: boolean;
194
+ reason?: string;
195
+ };
196
+ /**
197
+ * Runtime safety check for a file path about to be modified.
198
+ * This is meant to be called by the engine before any file write.
199
+ */
200
+ isFileModificationAllowed(filePath: string): {
201
+ allowed: boolean;
202
+ reason?: string;
203
+ };
204
+ getStats(): Readonly<AutoModeStats>;
205
+ resetStats(): void;
206
+ listCheckpoints(): AutoModeCheckpoint[];
207
+ getLatestCheckpoint(): AutoModeCheckpoint | undefined;
208
+ /** Create a checkpoint of the current state */
209
+ createCheckpoint(iteration: number, goalId?: string, routineId?: string): AutoModeCheckpoint;
210
+ private freshStats;
211
+ private emitStatus;
212
+ private buildBlockedCommands;
213
+ private buildBlockedPatterns;
214
+ private detectCompletion;
215
+ private buildGoalPrompt;
216
+ private autoGitCommit;
217
+ private autoRunTests;
218
+ private matchesGlobPattern;
219
+ private persistState;
220
+ private loadState;
221
+ private persistGoals;
222
+ private persistRoutines;
223
+ }
224
+ export type AutoModeStatus = 'disabled' | 'enabled' | 'running';
225
+ export interface AutoModeResult {
226
+ ok: boolean;
227
+ iterations: number;
228
+ finalText: string;
229
+ completed?: boolean;
230
+ error?: string;
231
+ }
232
+ //# sourceMappingURL=auto-mode.d.ts.map