attocode 0.2.1 → 0.2.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 (148) hide show
  1. package/CHANGELOG.md +91 -1
  2. package/README.md +7 -0
  3. package/dist/src/adapters.d.ts +6 -1
  4. package/dist/src/adapters.d.ts.map +1 -1
  5. package/dist/src/adapters.js +8 -1
  6. package/dist/src/adapters.js.map +1 -1
  7. package/dist/src/agent.d.ts +35 -4
  8. package/dist/src/agent.d.ts.map +1 -1
  9. package/dist/src/agent.js +361 -45
  10. package/dist/src/agent.js.map +1 -1
  11. package/dist/src/defaults.d.ts +1 -1
  12. package/dist/src/defaults.d.ts.map +1 -1
  13. package/dist/src/defaults.js +2 -0
  14. package/dist/src/defaults.js.map +1 -1
  15. package/dist/src/integrations/agent-registry.d.ts +2 -0
  16. package/dist/src/integrations/agent-registry.d.ts.map +1 -1
  17. package/dist/src/integrations/agent-registry.js.map +1 -1
  18. package/dist/src/integrations/async-subagent.d.ts +135 -0
  19. package/dist/src/integrations/async-subagent.d.ts.map +1 -0
  20. package/dist/src/integrations/async-subagent.js +213 -0
  21. package/dist/src/integrations/async-subagent.js.map +1 -0
  22. package/dist/src/integrations/auto-checkpoint.d.ts +98 -0
  23. package/dist/src/integrations/auto-checkpoint.d.ts.map +1 -0
  24. package/dist/src/integrations/auto-checkpoint.js +252 -0
  25. package/dist/src/integrations/auto-checkpoint.js.map +1 -0
  26. package/dist/src/integrations/complexity-classifier.d.ts +86 -0
  27. package/dist/src/integrations/complexity-classifier.d.ts.map +1 -0
  28. package/dist/src/integrations/complexity-classifier.js +233 -0
  29. package/dist/src/integrations/complexity-classifier.js.map +1 -0
  30. package/dist/src/integrations/delegation-protocol.d.ts +86 -0
  31. package/dist/src/integrations/delegation-protocol.d.ts.map +1 -0
  32. package/dist/src/integrations/delegation-protocol.js +127 -0
  33. package/dist/src/integrations/delegation-protocol.js.map +1 -0
  34. package/dist/src/integrations/dynamic-budget.d.ts +81 -0
  35. package/dist/src/integrations/dynamic-budget.d.ts.map +1 -0
  36. package/dist/src/integrations/dynamic-budget.js +151 -0
  37. package/dist/src/integrations/dynamic-budget.js.map +1 -0
  38. package/dist/src/integrations/economics.d.ts +44 -1
  39. package/dist/src/integrations/economics.d.ts.map +1 -1
  40. package/dist/src/integrations/economics.js +182 -3
  41. package/dist/src/integrations/economics.js.map +1 -1
  42. package/dist/src/integrations/environment-facts.d.ts +52 -0
  43. package/dist/src/integrations/environment-facts.d.ts.map +1 -0
  44. package/dist/src/integrations/environment-facts.js +84 -0
  45. package/dist/src/integrations/environment-facts.js.map +1 -0
  46. package/dist/src/integrations/index.d.ts +16 -1
  47. package/dist/src/integrations/index.d.ts.map +1 -1
  48. package/dist/src/integrations/index.js +31 -1
  49. package/dist/src/integrations/index.js.map +1 -1
  50. package/dist/src/integrations/injection-budget.d.ts +71 -0
  51. package/dist/src/integrations/injection-budget.d.ts.map +1 -0
  52. package/dist/src/integrations/injection-budget.js +136 -0
  53. package/dist/src/integrations/injection-budget.js.map +1 -0
  54. package/dist/src/integrations/mcp-client.d.ts.map +1 -1
  55. package/dist/src/integrations/mcp-client.js +14 -0
  56. package/dist/src/integrations/mcp-client.js.map +1 -1
  57. package/dist/src/integrations/mcp-custom-tools.d.ts +102 -0
  58. package/dist/src/integrations/mcp-custom-tools.d.ts.map +1 -0
  59. package/dist/src/integrations/mcp-custom-tools.js +232 -0
  60. package/dist/src/integrations/mcp-custom-tools.js.map +1 -0
  61. package/dist/src/integrations/mcp-tool-validator.d.ts +60 -0
  62. package/dist/src/integrations/mcp-tool-validator.d.ts.map +1 -0
  63. package/dist/src/integrations/mcp-tool-validator.js +141 -0
  64. package/dist/src/integrations/mcp-tool-validator.js.map +1 -0
  65. package/dist/src/integrations/self-improvement.d.ts +90 -0
  66. package/dist/src/integrations/self-improvement.d.ts.map +1 -0
  67. package/dist/src/integrations/self-improvement.js +217 -0
  68. package/dist/src/integrations/self-improvement.js.map +1 -0
  69. package/dist/src/integrations/smart-decomposer.d.ts +4 -0
  70. package/dist/src/integrations/smart-decomposer.d.ts.map +1 -1
  71. package/dist/src/integrations/smart-decomposer.js +55 -28
  72. package/dist/src/integrations/smart-decomposer.js.map +1 -1
  73. package/dist/src/integrations/subagent-output-store.d.ts +91 -0
  74. package/dist/src/integrations/subagent-output-store.d.ts.map +1 -0
  75. package/dist/src/integrations/subagent-output-store.js +257 -0
  76. package/dist/src/integrations/subagent-output-store.js.map +1 -0
  77. package/dist/src/integrations/swarm/index.d.ts +1 -1
  78. package/dist/src/integrations/swarm/index.d.ts.map +1 -1
  79. package/dist/src/integrations/swarm/index.js +1 -1
  80. package/dist/src/integrations/swarm/index.js.map +1 -1
  81. package/dist/src/integrations/swarm/model-selector.d.ts +1 -0
  82. package/dist/src/integrations/swarm/model-selector.d.ts.map +1 -1
  83. package/dist/src/integrations/swarm/model-selector.js +37 -3
  84. package/dist/src/integrations/swarm/model-selector.js.map +1 -1
  85. package/dist/src/integrations/swarm/swarm-config-loader.d.ts +10 -1
  86. package/dist/src/integrations/swarm/swarm-config-loader.d.ts.map +1 -1
  87. package/dist/src/integrations/swarm/swarm-config-loader.js +72 -6
  88. package/dist/src/integrations/swarm/swarm-config-loader.js.map +1 -1
  89. package/dist/src/integrations/swarm/swarm-event-bridge.d.ts.map +1 -1
  90. package/dist/src/integrations/swarm/swarm-event-bridge.js +26 -4
  91. package/dist/src/integrations/swarm/swarm-event-bridge.js.map +1 -1
  92. package/dist/src/integrations/swarm/swarm-events.d.ts +11 -0
  93. package/dist/src/integrations/swarm/swarm-events.d.ts.map +1 -1
  94. package/dist/src/integrations/swarm/swarm-events.js +4 -0
  95. package/dist/src/integrations/swarm/swarm-events.js.map +1 -1
  96. package/dist/src/integrations/swarm/swarm-orchestrator.d.ts +11 -0
  97. package/dist/src/integrations/swarm/swarm-orchestrator.d.ts.map +1 -1
  98. package/dist/src/integrations/swarm/swarm-orchestrator.js +233 -10
  99. package/dist/src/integrations/swarm/swarm-orchestrator.js.map +1 -1
  100. package/dist/src/integrations/swarm/swarm-quality-gate.d.ts +9 -2
  101. package/dist/src/integrations/swarm/swarm-quality-gate.d.ts.map +1 -1
  102. package/dist/src/integrations/swarm/swarm-quality-gate.js +128 -11
  103. package/dist/src/integrations/swarm/swarm-quality-gate.js.map +1 -1
  104. package/dist/src/integrations/swarm/task-queue.d.ts +11 -1
  105. package/dist/src/integrations/swarm/task-queue.d.ts.map +1 -1
  106. package/dist/src/integrations/swarm/task-queue.js +125 -15
  107. package/dist/src/integrations/swarm/task-queue.js.map +1 -1
  108. package/dist/src/integrations/swarm/types.d.ts +40 -1
  109. package/dist/src/integrations/swarm/types.d.ts.map +1 -1
  110. package/dist/src/integrations/swarm/types.js +6 -1
  111. package/dist/src/integrations/swarm/types.js.map +1 -1
  112. package/dist/src/integrations/swarm/worker-pool.d.ts +9 -3
  113. package/dist/src/integrations/swarm/worker-pool.d.ts.map +1 -1
  114. package/dist/src/integrations/swarm/worker-pool.js +89 -17
  115. package/dist/src/integrations/swarm/worker-pool.js.map +1 -1
  116. package/dist/src/integrations/thinking-strategy.d.ts +52 -0
  117. package/dist/src/integrations/thinking-strategy.d.ts.map +1 -0
  118. package/dist/src/integrations/thinking-strategy.js +129 -0
  119. package/dist/src/integrations/thinking-strategy.js.map +1 -0
  120. package/dist/src/integrations/tool-recommendation.d.ts +58 -0
  121. package/dist/src/integrations/tool-recommendation.d.ts.map +1 -0
  122. package/dist/src/integrations/tool-recommendation.js +215 -0
  123. package/dist/src/integrations/tool-recommendation.js.map +1 -0
  124. package/dist/src/integrations/verification-gate.d.ts +80 -0
  125. package/dist/src/integrations/verification-gate.d.ts.map +1 -0
  126. package/dist/src/integrations/verification-gate.js +146 -0
  127. package/dist/src/integrations/verification-gate.js.map +1 -0
  128. package/dist/src/integrations/work-log.d.ts +87 -0
  129. package/dist/src/integrations/work-log.d.ts.map +1 -0
  130. package/dist/src/integrations/work-log.js +275 -0
  131. package/dist/src/integrations/work-log.js.map +1 -0
  132. package/dist/src/main.js +5 -4
  133. package/dist/src/main.js.map +1 -1
  134. package/dist/src/modes.d.ts +6 -0
  135. package/dist/src/modes.d.ts.map +1 -1
  136. package/dist/src/modes.js +73 -2
  137. package/dist/src/modes.js.map +1 -1
  138. package/dist/src/tools/bash.d.ts +6 -0
  139. package/dist/src/tools/bash.d.ts.map +1 -1
  140. package/dist/src/tools/bash.js +12 -0
  141. package/dist/src/tools/bash.js.map +1 -1
  142. package/dist/src/tools/standard.d.ts +17 -1
  143. package/dist/src/tools/standard.d.ts.map +1 -1
  144. package/dist/src/tools/standard.js +64 -11
  145. package/dist/src/tools/standard.js.map +1 -1
  146. package/dist/src/types.d.ts +18 -0
  147. package/dist/src/types.d.ts.map +1 -1
  148. package/package.json +6 -2
package/dist/src/agent.js CHANGED
@@ -21,7 +21,11 @@
21
21
  import { buildConfig, isFeatureEnabled, getEnabledFeatures, getSubagentTimeout, getSubagentMaxIterations, } from './defaults.js';
22
22
  import { createModeManager, formatModeList, parseMode, calculateTaskSimilarity, SUBAGENT_PLAN_MODE_ADDITION, } from './modes.js';
23
23
  import { createLSPFileTools, } from './agent-tools/index.js';
24
- import { HookManager, MemoryManager, PlanningManager, ObservabilityManager, SafetyManager, RoutingManager, MultiAgentManager, ReActManager, ExecutionPolicyManager, ThreadManager, RulesManager, DEFAULT_RULE_SOURCES, ExecutionEconomicsManager, STANDARD_BUDGET, SUBAGENT_BUDGET, TIMEOUT_WRAPUP_PROMPT, AgentRegistry, filterToolsForAgent, formatAgentList, createCancellationManager, isCancellationError, createLinkedToken, createGracefulTimeout, race, createResourceManager, createLSPManager, createSemanticCacheManager, createSkillManager, formatSkillList, createContextEngineering, stableStringify, createCodebaseContext, buildContextFromChunks, createSharedFileCache, createBudgetPool, createPendingPlanManager, createInteractivePlanner, createRecursiveContext, createLearningStore, createCompactor, createAutoCompactionManager, createFileChangeTracker, createCapabilitiesRegistry, createSharedBlackboard, createTaskManager, createSwarmOrchestrator, createThrottledProvider, FREE_TIER_THROTTLE, PAID_TIER_THROTTLE, } from './integrations/index.js';
24
+ import { HookManager, MemoryManager, PlanningManager, ObservabilityManager, SafetyManager, RoutingManager, MultiAgentManager, ReActManager, ExecutionPolicyManager, ThreadManager, RulesManager, DEFAULT_RULE_SOURCES, ExecutionEconomicsManager, STANDARD_BUDGET, SUBAGENT_BUDGET, TIMEOUT_WRAPUP_PROMPT, AgentRegistry, filterToolsForAgent, formatAgentList, createCancellationManager, isCancellationError, createLinkedToken, createGracefulTimeout, race, createResourceManager, createLSPManager, createSemanticCacheManager, createSkillManager, formatSkillList, createContextEngineering, stableStringify, createCodebaseContext, buildContextFromChunks, createSharedFileCache, createBudgetPool, createDynamicBudgetPool, createPendingPlanManager, createInteractivePlanner, createRecursiveContext, createLearningStore, createCompactor, createAutoCompactionManager, createFileChangeTracker, createCapabilitiesRegistry, createSharedBlackboard, createTaskManager, createSwarmOrchestrator, createThrottledProvider, FREE_TIER_THROTTLE, PAID_TIER_THROTTLE, createWorkLog, createVerificationGate,
25
+ // Phase 2: Orchestration
26
+ classifyComplexity, getScalingGuidance, buildDelegationPrompt, createMinimalDelegationSpec, getSubagentQualityPrompt, ToolRecommendationEngine, createToolRecommendationEngine, createInjectionBudgetManager,
27
+ // Phase 3: Advanced
28
+ getThinkingSystemPrompt, createSelfImprovementProtocol, createSubagentOutputStore, createSerperSearchTool, getEnvironmentFacts, formatFactsBlock, createAutoCheckpointManager, createSubagentSupervisor, createSubagentHandle, } from './integrations/index.js';
25
29
  // Lesson 26: Tracing & Evaluation integration
26
30
  import { createTraceCollector } from './tracing/trace-collector.js';
27
31
  // Model registry for context window limits
@@ -43,31 +47,107 @@ export const PARALLELIZABLE_TOOLS = new Set([
43
47
  'search_code', 'get_file_info',
44
48
  ]);
45
49
  /**
46
- * Groups consecutive tool calls into batches for parallel/sequential execution.
47
- * Consecutive parallelizable tools form a single parallel batch.
48
- * Non-parallelizable tools break the sequence, starting a new batch.
50
+ * Tools that can run in parallel IF they target different files.
51
+ * write_file and edit_file on different paths are safe to parallelize.
49
52
  */
50
- export function groupToolCallsIntoBatches(toolCalls, isParallelizable = (tc) => PARALLELIZABLE_TOOLS.has(tc.name)) {
53
+ export const CONDITIONALLY_PARALLEL_TOOLS = new Set([
54
+ 'write_file', 'edit_file',
55
+ ]);
56
+ /**
57
+ * Extract the target file path from a tool call's arguments.
58
+ * Returns null if no file path can be determined.
59
+ */
60
+ export function extractToolFilePath(toolCall) {
61
+ // Check common argument patterns
62
+ const args = toolCall;
63
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
64
+ if (typeof args[key] === 'string')
65
+ return args[key];
66
+ }
67
+ // Check nested args object
68
+ if (args.args && typeof args.args === 'object') {
69
+ const nested = args.args;
70
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
71
+ if (typeof nested[key] === 'string')
72
+ return nested[key];
73
+ }
74
+ }
75
+ // Check input object (common in structured tool calls)
76
+ if (args.input && typeof args.input === 'object') {
77
+ const input = args.input;
78
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
79
+ if (typeof input[key] === 'string')
80
+ return input[key];
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ /**
86
+ * Check if a conditionally-parallel tool call conflicts with any tool
87
+ * in the current accumulator (same file path).
88
+ */
89
+ function hasFileConflict(toolCall, accumulator) {
90
+ const path = extractToolFilePath(toolCall);
91
+ if (!path)
92
+ return true; // Can't determine path → assume conflict
93
+ for (const existing of accumulator) {
94
+ const existingPath = extractToolFilePath(existing);
95
+ if (existingPath === path)
96
+ return true; // Same file → conflict
97
+ }
98
+ return false;
99
+ }
100
+ /**
101
+ * Groups tool calls into batches for parallel/sequential execution.
102
+ * Uses accumulate-and-flush: parallelizable tools accumulate until a
103
+ * non-parallelizable tool flushes them as a batch. This produces optimal
104
+ * batching even for non-consecutive parallelizable tools.
105
+ *
106
+ * Enhanced with conditional parallelism: write_file/edit_file on
107
+ * DIFFERENT files can be batched together for parallel execution.
108
+ *
109
+ * Example: [read1, read2, write, read3, grep] → [[read1, read2], [write], [read3, grep]]
110
+ * (Previous algorithm produced 4 batches; this produces 3)
111
+ *
112
+ * Enhanced: [write_a, write_b, write_a] → [[write_a, write_b], [write_a]]
113
+ * (Different files parallelized, same file sequential)
114
+ */
115
+ export function groupToolCallsIntoBatches(toolCalls, isParallelizable = (tc) => PARALLELIZABLE_TOOLS.has(tc.name), isConditionallyParallel = (tc) => CONDITIONALLY_PARALLEL_TOOLS.has(tc.name)) {
116
+ if (toolCalls.length === 0)
117
+ return [];
51
118
  const batches = [];
52
- let currentBatch = [];
53
- let currentIsParallel = false;
119
+ let parallelAccum = [];
54
120
  for (const toolCall of toolCalls) {
55
- const isParallel = isParallelizable(toolCall);
56
- if (batches.length === 0 && currentBatch.length === 0) {
57
- currentBatch.push(toolCall);
58
- currentIsParallel = isParallel;
121
+ if (isParallelizable(toolCall)) {
122
+ parallelAccum.push(toolCall);
59
123
  }
60
- else if (isParallel && currentIsParallel) {
61
- currentBatch.push(toolCall);
124
+ else if (isConditionallyParallel(toolCall)) {
125
+ // Can parallelize if no file conflict with existing accumulator
126
+ if (!hasFileConflict(toolCall, parallelAccum)) {
127
+ parallelAccum.push(toolCall);
128
+ }
129
+ else {
130
+ // Conflict: flush current batch, start new one with this tool
131
+ if (parallelAccum.length > 0) {
132
+ batches.push(parallelAccum);
133
+ parallelAccum = [];
134
+ }
135
+ parallelAccum.push(toolCall);
136
+ }
62
137
  }
63
138
  else {
64
- batches.push(currentBatch);
65
- currentBatch = [toolCall];
66
- currentIsParallel = isParallel;
139
+ // Flush any accumulated parallel tools as a single batch
140
+ if (parallelAccum.length > 0) {
141
+ batches.push(parallelAccum);
142
+ parallelAccum = [];
143
+ }
144
+ // Non-parallelizable tool gets its own batch
145
+ batches.push([toolCall]);
67
146
  }
68
147
  }
69
- if (currentBatch.length > 0) {
70
- batches.push(currentBatch);
148
+ // Flush remaining parallel tools
149
+ if (parallelAccum.length > 0) {
150
+ batches.push(parallelAccum);
71
151
  }
72
152
  return batches;
73
153
  }
@@ -117,6 +197,15 @@ export class ProductionAgent {
117
197
  taskManager = null;
118
198
  store = null;
119
199
  swarmOrchestrator = null;
200
+ workLog = null;
201
+ verificationGate = null;
202
+ // Phase 2-4 integration modules
203
+ injectionBudget = null;
204
+ selfImprovement = null;
205
+ subagentOutputStore = null;
206
+ autoCheckpointManager = null;
207
+ toolRecommendation = null;
208
+ lastComplexityAssessment = null;
120
209
  // Duplicate spawn prevention - tracks recently spawned tasks to prevent doom loops
121
210
  // Map<taskKey, { timestamp: number; result: string; queuedChanges: number }>
122
211
  spawnedTasks = new Map();
@@ -311,6 +400,19 @@ export class ProductionAgent {
311
400
  maxIterations: this.config.maxIterations,
312
401
  targetIterations: Math.min(baseBudget.targetIterations ?? 20, this.config.maxIterations),
313
402
  });
403
+ // Work Log - compaction-resilient summary of agent work
404
+ // Always enabled - minimal overhead and critical for long-running tasks
405
+ this.workLog = createWorkLog();
406
+ // Verification Gate - opt-in completion verification
407
+ if (this.config.verificationCriteria) {
408
+ this.verificationGate = createVerificationGate(this.config.verificationCriteria);
409
+ }
410
+ // Phase 2-4: Orchestration & Advanced modules (always enabled, lightweight)
411
+ this.injectionBudget = createInjectionBudgetManager();
412
+ this.selfImprovement = createSelfImprovementProtocol(undefined, this.learningStore ?? undefined);
413
+ this.subagentOutputStore = createSubagentOutputStore({ persistToFile: false });
414
+ this.autoCheckpointManager = createAutoCheckpointManager({ enabled: true });
415
+ this.toolRecommendation = createToolRecommendationEngine();
314
416
  // Agent Registry - always enabled for subagent support
315
417
  this.agentRegistry = new AgentRegistry();
316
418
  // Load user agents asynchronously - tracked for ensureReady()
@@ -341,6 +443,15 @@ export class ProductionAgent {
341
443
  for (const tool of taskTools) {
342
444
  this.tools.set(tool.name, tool);
343
445
  }
446
+ // Built-in web search (Serper API) — gracefully handles missing API key
447
+ const serperCustomTool = createSerperSearchTool();
448
+ this.tools.set('web_search', {
449
+ name: serperCustomTool.name,
450
+ description: serperCustomTool.description,
451
+ parameters: serperCustomTool.inputSchema,
452
+ execute: serperCustomTool.execute,
453
+ dangerLevel: 'safe',
454
+ });
344
455
  // Swarm Mode (experimental)
345
456
  if (this.config.swarm) {
346
457
  const swarmConfig = this.config.swarm;
@@ -837,6 +948,10 @@ export class ProductionAgent {
837
948
  try {
838
949
  // Check for cancellation before starting
839
950
  cancellationToken?.throwIfCancellationRequested();
951
+ // Classify task complexity for scaling guidance
952
+ this.lastComplexityAssessment = classifyComplexity(task, {
953
+ hasActivePlan: !!this.state.plan,
954
+ });
840
955
  // Check if swarm mode should handle this task
841
956
  if (this.swarmOrchestrator) {
842
957
  const swarmResult = await this.runSwarm(task);
@@ -1119,6 +1234,14 @@ export class ProductionAgent {
1119
1234
  content: `[CONTEXT REDUCED: Earlier messages were removed to stay within budget. Conversation continues from recent context.]`,
1120
1235
  });
1121
1236
  messages.push(...recentMessages);
1237
+ // Inject work log after emergency truncation to prevent amnesia
1238
+ if (this.workLog?.hasContent()) {
1239
+ const workLogMessage = {
1240
+ role: 'user',
1241
+ content: this.workLog.toCompactString(),
1242
+ };
1243
+ messages.push(workLogMessage);
1244
+ }
1122
1245
  // Update state messages too
1123
1246
  this.state.messages.length = 0;
1124
1247
  this.state.messages.push(...messages);
@@ -1299,6 +1422,35 @@ export class ProductionAgent {
1299
1422
  }
1300
1423
  }
1301
1424
  // =====================================================================
1425
+ // INJECTION BUDGET ANALYSIS (Phase 2 - monitoring mode)
1426
+ // Collects stats on context injections without gating; logs when
1427
+ // budget would have dropped items. Validates system before enabling gating.
1428
+ // =====================================================================
1429
+ if (this.injectionBudget) {
1430
+ const proposals = [];
1431
+ if (budgetInjectedPrompt) {
1432
+ proposals.push({ name: 'budget_warning', priority: 0, maxTokens: 500, content: budgetInjectedPrompt });
1433
+ }
1434
+ // Approximate recitation content (actual injection handled above)
1435
+ if (this.contextEngineering) {
1436
+ const failureCtx = this.contextEngineering.getFailureContext(5);
1437
+ if (failureCtx) {
1438
+ proposals.push({ name: 'failure_context', priority: 2, maxTokens: 300, content: failureCtx });
1439
+ }
1440
+ }
1441
+ if (proposals.length > 0) {
1442
+ const accepted = this.injectionBudget.allocate(proposals);
1443
+ const stats = this.injectionBudget.getLastStats();
1444
+ if (stats && stats.droppedNames.length > 0 && process.env.DEBUG) {
1445
+ console.log(`[injection-budget] Would drop: ${stats.droppedNames.join(', ')} (${stats.proposedTokens} proposed, ${stats.acceptedTokens} accepted)`);
1446
+ }
1447
+ // Log total injection overhead for observability
1448
+ if (stats && process.env.DEBUG_LLM) {
1449
+ console.log(`[injection-budget] Iteration ${this.state.iteration}: ${accepted.length}/${proposals.length} injections, ~${stats.acceptedTokens} tokens`);
1450
+ }
1451
+ }
1452
+ }
1453
+ // =====================================================================
1302
1454
  // RESILIENT LLM CALL: Empty response retries + max_tokens continuation
1303
1455
  // =====================================================================
1304
1456
  // Get resilience config
@@ -1594,6 +1746,24 @@ export class ProductionAgent {
1594
1746
  });
1595
1747
  incompleteActionRetries = 0;
1596
1748
  }
1749
+ // Verification gate: if criteria not met, nudge agent to verify before completing
1750
+ if (this.verificationGate && !forceTextOnly) {
1751
+ const vResult = this.verificationGate.check();
1752
+ if (!vResult.satisfied && !vResult.forceAllow && vResult.nudge) {
1753
+ // Inject nudge and continue the loop
1754
+ const nudgeMessage = {
1755
+ role: 'user',
1756
+ content: vResult.nudge,
1757
+ };
1758
+ messages.push(nudgeMessage);
1759
+ this.state.messages.push(nudgeMessage);
1760
+ this.observability?.logger?.info('Verification gate nudge', {
1761
+ missing: vResult.missing,
1762
+ nudgeCount: this.verificationGate.getState().nudgeCount,
1763
+ });
1764
+ continue;
1765
+ }
1766
+ }
1597
1767
  // No tool calls (or forced to ignore), agent is done - compact tool outputs to save context
1598
1768
  // The model has "consumed" the tool outputs and produced a response,
1599
1769
  // so we can replace verbose outputs with compact summaries
@@ -1630,12 +1800,33 @@ export class ProductionAgent {
1630
1800
  // Execute tool calls (we know toolCalls is defined here due to the check above)
1631
1801
  const toolCalls = response.toolCalls;
1632
1802
  const toolResults = await this.executeToolCalls(toolCalls);
1633
- // Record tool calls for economics/progress tracking
1803
+ // Record tool calls for economics/progress tracking + work log
1634
1804
  for (let i = 0; i < toolCalls.length; i++) {
1635
1805
  const toolCall = toolCalls[i];
1636
1806
  const result = toolResults[i];
1637
1807
  executedToolNames.add(toolCall.name);
1638
1808
  this.economics?.recordToolCall(toolCall.name, toolCall.arguments, result?.result);
1809
+ // Record in work log for compaction resilience
1810
+ const toolOutput = result?.result && typeof result.result === 'object' && 'output' in result.result
1811
+ ? String(result.result.output)
1812
+ : typeof result?.result === 'string' ? result.result : undefined;
1813
+ this.workLog?.recordToolExecution(toolCall.name, toolCall.arguments, toolOutput);
1814
+ // Record in verification gate
1815
+ if (this.verificationGate) {
1816
+ if (toolCall.name === 'bash') {
1817
+ const toolRes = result?.result;
1818
+ const output = toolRes && typeof toolRes === 'object' && 'output' in toolRes
1819
+ ? String(toolRes.output)
1820
+ : typeof toolRes === 'string' ? toolRes : '';
1821
+ const exitCode = toolRes && typeof toolRes === 'object' && toolRes.metadata
1822
+ ? toolRes.metadata.exitCode ?? null
1823
+ : null;
1824
+ this.verificationGate.recordBashExecution(String(toolCall.arguments.command || ''), output, exitCode);
1825
+ }
1826
+ if (['write_file', 'edit_file'].includes(toolCall.name)) {
1827
+ this.verificationGate.recordFileChange();
1828
+ }
1829
+ }
1639
1830
  }
1640
1831
  // Add tool results to messages (with truncation and proactive budget management)
1641
1832
  const MAX_TOOL_OUTPUT_CHARS = 8000; // ~2000 tokens max per tool output
@@ -1657,6 +1848,15 @@ export class ProductionAgent {
1657
1848
  messages.push(...compactionResult.compactedMessages);
1658
1849
  this.state.messages.length = 0;
1659
1850
  this.state.messages.push(...compactionResult.compactedMessages);
1851
+ // Inject work log after compaction to prevent amnesia
1852
+ if (this.workLog?.hasContent()) {
1853
+ const workLogMessage = {
1854
+ role: 'user',
1855
+ content: this.workLog.toCompactString(),
1856
+ };
1857
+ messages.push(workLogMessage);
1858
+ this.state.messages.push(workLogMessage);
1859
+ }
1660
1860
  }
1661
1861
  else if (compactionResult.status === 'hard_limit') {
1662
1862
  // Hard limit reached - this is serious, emit error
@@ -1851,12 +2051,25 @@ export class ProductionAgent {
1851
2051
  }
1852
2052
  }
1853
2053
  // Build system prompt using cache-aware builder if available (Trick P)
1854
- // Combine memory, learnings, and codebase context
1855
- const combinedContext = [
2054
+ // Combine memory, learnings, codebase context, and environment facts
2055
+ const combinedContextParts = [
2056
+ // Environment facts — temporal/platform grounding (prevents stale date hallucinations)
2057
+ formatFactsBlock(getEnvironmentFacts()),
1856
2058
  ...(memoryContext.length > 0 ? memoryContext : []),
1857
2059
  ...(learningsContext ? [learningsContext] : []),
1858
2060
  ...(codebaseContextStr ? [`\n## Relevant Code\n${codebaseContextStr}`] : []),
1859
- ].join('\n');
2061
+ ];
2062
+ // Inject thinking directives and scaling guidance for non-simple tasks
2063
+ if (this.lastComplexityAssessment) {
2064
+ const thinkingPrompt = getThinkingSystemPrompt(this.lastComplexityAssessment.tier);
2065
+ if (thinkingPrompt) {
2066
+ combinedContextParts.push(thinkingPrompt);
2067
+ }
2068
+ if (this.lastComplexityAssessment.tier !== 'simple') {
2069
+ combinedContextParts.push(getScalingGuidance(this.lastComplexityAssessment));
2070
+ }
2071
+ }
2072
+ const combinedContext = combinedContextParts.join('\n');
1860
2073
  const promptOptions = {
1861
2074
  rules: rulesContent + (skillsPrompt ? '\n\n' + skillsPrompt : ''),
1862
2075
  tools: toolDescriptions,
@@ -1996,6 +2209,8 @@ export class ProductionAgent {
1996
2209
  },
1997
2210
  },
1998
2211
  });
2212
+ // Pause duration budget during LLM call - network time shouldn't count against agent
2213
+ this.economics?.pauseDuration();
1999
2214
  try {
2000
2215
  let response;
2001
2216
  let actualModel = model;
@@ -2135,6 +2350,10 @@ export class ProductionAgent {
2135
2350
  this.observability?.tracer?.endSpan(spanId);
2136
2351
  throw error;
2137
2352
  }
2353
+ finally {
2354
+ // Resume duration budget after LLM call completes (success or failure)
2355
+ this.economics?.resumeDuration();
2356
+ }
2138
2357
  }
2139
2358
  /**
2140
2359
  * Execute an async callback while excluding wall-clock wait time from duration budgeting.
@@ -2467,6 +2686,8 @@ export class ProductionAgent {
2467
2686
  this.blackboard.release(filePath, agentId);
2468
2687
  }
2469
2688
  }
2689
+ // Self-improvement: record success pattern
2690
+ this.selfImprovement?.recordSuccess(toolCall.name, toolCall.arguments, typeof result === 'string' ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200));
2470
2691
  this.observability?.tracer?.endSpan(spanId);
2471
2692
  return { callId: toolCall.id, result };
2472
2693
  }
@@ -2494,6 +2715,12 @@ export class ProductionAgent {
2494
2715
  error,
2495
2716
  intent: `Execute tool ${toolCall.name}`,
2496
2717
  });
2718
+ // Self-improvement: enhance error message with diagnosis for better LLM recovery
2719
+ if (this.selfImprovement) {
2720
+ const enhanced = this.selfImprovement.enhanceErrorMessage(toolCall.name, error.message, toolCall.arguments);
2721
+ this.emit({ type: 'tool.blocked', tool: toolCall.name, reason: enhanced });
2722
+ return { callId: toolCall.id, result: `Error: ${enhanced}`, error: enhanced };
2723
+ }
2497
2724
  this.emit({ type: 'tool.blocked', tool: toolCall.name, reason: error.message });
2498
2725
  return { callId: toolCall.id, result: `Error: ${error.message}`, error: error.message };
2499
2726
  }
@@ -3522,6 +3749,19 @@ export class ProductionAgent {
3522
3749
  }
3523
3750
  // Create the checkpoint
3524
3751
  const label = `auto-iter-${this.state.iteration}`;
3752
+ // Supplementary: also save to AutoCheckpointManager (file-based)
3753
+ if (this.autoCheckpointManager) {
3754
+ try {
3755
+ this.autoCheckpointManager.save({
3756
+ label,
3757
+ sessionId: this.agentId,
3758
+ iteration: this.state.iteration,
3759
+ });
3760
+ }
3761
+ catch {
3762
+ // Non-critical — don't fail the main checkpoint path
3763
+ }
3764
+ }
3525
3765
  return this.createCheckpoint(label);
3526
3766
  }
3527
3767
  // =========================================================================
@@ -3675,7 +3915,18 @@ export class ProductionAgent {
3675
3915
  let workerResultId;
3676
3916
  try {
3677
3917
  // Filter tools for this agent
3678
- const agentTools = filterToolsForAgent(agentDef, Array.from(this.tools.values()));
3918
+ let agentTools = filterToolsForAgent(agentDef, Array.from(this.tools.values()));
3919
+ // Apply tool recommendations to improve subagent focus (only for large tool sets)
3920
+ if (this.toolRecommendation && agentTools.length > 15) {
3921
+ const taskType = ToolRecommendationEngine.inferTaskType(agentName);
3922
+ const recommendations = this.toolRecommendation.recommendTools(task, taskType, agentTools.map(t => t.name));
3923
+ if (recommendations.length > 0) {
3924
+ const recommendedNames = new Set(recommendations.map(r => r.toolName));
3925
+ // Always keep spawn tools even if not recommended
3926
+ const alwaysKeep = new Set(['spawn_agent', 'spawn_agents_parallel']);
3927
+ agentTools = agentTools.filter(t => recommendedNames.has(t.name) || alwaysKeep.has(t.name));
3928
+ }
3929
+ }
3679
3930
  // Resolve model - abstract tiers (fast/balanced/quality) should use parent's model
3680
3931
  // Only use agentDef.model if it's an actual model ID (contains '/')
3681
3932
  const resolvedModel = (agentDef.model && agentDef.model.includes('/'))
@@ -3767,14 +4018,30 @@ export class ProductionAgent {
3767
4018
  // BUDGET AWARENESS: Always inject so subagent understands its limits
3768
4019
  const subagentBudgetTokens = constraints?.maxTokens ?? SUBAGENT_BUDGET.maxTokens ?? 100000;
3769
4020
  const subagentBudgetMinutes = Math.round((SUBAGENT_BUDGET.maxDuration ?? 240000) / 60000);
3770
- constraintParts.push(`**RESOURCE AWARENESS (CRITICAL):**\n` +
3771
- `- Token budget: ~${(subagentBudgetTokens / 1000).toFixed(0)}k tokens\n` +
3772
- `- Time limit: ~${subagentBudgetMinutes} minutes\n` +
3773
- `- You will receive warnings at 70% usage. When warned, WRAP UP immediately.\n` +
3774
- `- Do not explore indefinitely - be focused and efficient.\n` +
3775
- `- If approaching limits, summarize findings and return.\n` +
3776
- `- **STRUCTURED WRAPUP:** When told to wrap up, respond with ONLY this JSON (no tool calls):\n` +
3777
- ` {"findings":[...], "actionsTaken":[...], "failures":[...], "remainingWork":[...], "suggestedNextSteps":[...]}`);
4021
+ if (isSwarmWorker) {
4022
+ // V6: Calmer resource awareness for swarm workers — prevents weaker models
4023
+ // from confabulating budget warnings and wrapping up without doing work
4024
+ constraintParts.push(`**Resource Info:**\n` +
4025
+ `- Token budget: ~${(subagentBudgetTokens / 1000).toFixed(0)}k tokens (you have plenty)\n` +
4026
+ `- Time limit: ~${subagentBudgetMinutes} minutes\n` +
4027
+ `- Focus on completing your task. Do NOT wrap up prematurely.\n` +
4028
+ `- You will receive a system warning IF you approach budget limits. Until then, work normally.\n` +
4029
+ `- **IMPORTANT:** Budget warnings come from the SYSTEM, not from your own assessment. ` +
4030
+ `Do not preemptively claim budget issues.\n` +
4031
+ `- **STRUCTURED WRAPUP:** When told to wrap up, respond with ONLY this JSON (no tool calls):\n` +
4032
+ ` {"findings":[...], "actionsTaken":[...], "failures":[...], "remainingWork":[...], "suggestedNextSteps":[...]}`);
4033
+ }
4034
+ else {
4035
+ // Original RESOURCE AWARENESS text for regular subagents
4036
+ constraintParts.push(`**RESOURCE AWARENESS (CRITICAL):**\n` +
4037
+ `- Token budget: ~${(subagentBudgetTokens / 1000).toFixed(0)}k tokens\n` +
4038
+ `- Time limit: ~${subagentBudgetMinutes} minutes\n` +
4039
+ `- You will receive warnings at 70% usage. When warned, WRAP UP immediately.\n` +
4040
+ `- Do not explore indefinitely - be focused and efficient.\n` +
4041
+ `- If approaching limits, summarize findings and return.\n` +
4042
+ `- **STRUCTURED WRAPUP:** When told to wrap up, respond with ONLY this JSON (no tool calls):\n` +
4043
+ ` {"findings":[...], "actionsTaken":[...], "failures":[...], "remainingWork":[...], "suggestedNextSteps":[...]}`);
4044
+ }
3778
4045
  if (constraints) {
3779
4046
  if (constraints.focusAreas && constraints.focusAreas.length > 0) {
3780
4047
  constraintParts.push(`**FOCUS AREAS (limit exploration to these paths):**\n${constraints.focusAreas.map(a => ` - ${a}`).join('\n')}`);
@@ -3790,11 +4057,19 @@ export class ProductionAgent {
3790
4057
  }
3791
4058
  }
3792
4059
  const constraintContext = `\n\n**EXECUTION CONSTRAINTS:**\n${constraintParts.join('\n\n')}\n`;
4060
+ // Build delegation-enhanced system prompt
4061
+ let delegationContext = '';
4062
+ if (this.lastComplexityAssessment && this.lastComplexityAssessment.tier !== 'simple') {
4063
+ const spec = createMinimalDelegationSpec(task, agentName);
4064
+ delegationContext = '\n\n' + buildDelegationPrompt(spec);
4065
+ }
4066
+ // Quality self-assessment prompt for subagent
4067
+ const qualityPrompt = '\n\n' + getSubagentQualityPrompt();
3793
4068
  // Build subagent system prompt with subagent-specific plan mode addition
3794
4069
  const parentMode = this.getMode();
3795
4070
  const subagentSystemPrompt = parentMode === 'plan'
3796
- ? `${agentDef.systemPrompt}\n\n${SUBAGENT_PLAN_MODE_ADDITION}${blackboardContext}${constraintContext}`
3797
- : `${agentDef.systemPrompt}${blackboardContext}${constraintContext}`;
4071
+ ? `${agentDef.systemPrompt}\n\n${SUBAGENT_PLAN_MODE_ADDITION}${blackboardContext}${constraintContext}${delegationContext}${qualityPrompt}`
4072
+ : `${agentDef.systemPrompt}${blackboardContext}${constraintContext}${delegationContext}${qualityPrompt}`;
3798
4073
  // Allocate budget from pool (or use default) — track allocation ID for release later
3799
4074
  const pooledBudget = this.getSubagentBudget(agentName, constraints);
3800
4075
  const poolAllocationId = pooledBudget.allocationId;
@@ -4006,6 +4281,25 @@ export class ProductionAgent {
4006
4281
  },
4007
4282
  structured,
4008
4283
  };
4284
+ // Save full output to subagent output store (avoids telephone problem)
4285
+ if (this.subagentOutputStore) {
4286
+ const outputEntry = {
4287
+ id: agentId,
4288
+ agentId,
4289
+ agentName,
4290
+ task,
4291
+ fullOutput: finalOutput,
4292
+ structured,
4293
+ filesModified: [],
4294
+ filesCreated: [],
4295
+ timestamp: new Date(),
4296
+ tokensUsed: result.metrics.totalTokens,
4297
+ durationMs: duration,
4298
+ };
4299
+ const storeId = this.subagentOutputStore.save(outputEntry);
4300
+ // Attach reference so downstream consumers can retrieve full output
4301
+ spawnResultFinal.outputStoreId = storeId;
4302
+ }
4009
4303
  if (workerResultId && this.store?.hasWorkerResultsFeature()) {
4010
4304
  try {
4011
4305
  this.store.completeWorkerResult(workerResultId, {
@@ -4310,23 +4604,34 @@ export class ProductionAgent {
4310
4604
  count: tasks.length,
4311
4605
  agents: tasks.map(t => t.agent),
4312
4606
  });
4313
- // Pre-divide budget pool equally to prevent first-come starvation.
4314
- // Temporarily lower maxPerChild so each spawnAgent's normal reserve() call
4315
- // gets an equal share instead of racing for the full maxPerChild allocation.
4607
+ // Use DynamicBudgetPool for parallel spawns (prevents child starvation,
4608
+ // enables priority-based allocation). Falls back to regular pool for single tasks.
4316
4609
  let settled;
4610
+ const originalPool = this.budgetPool;
4611
+ // SubagentSupervisor for unified monitoring of concurrent subagents
4612
+ const supervisor = tasks.length > 1 ? createSubagentSupervisor() : null;
4317
4613
  if (this.budgetPool && tasks.length > 1) {
4614
+ // Swap to DynamicBudgetPool for this parallel batch
4318
4615
  const poolStats = this.budgetPool.getStats();
4319
- // equalShare is always ≤ remaining ≤ totalTokens ≤ originalMaxPerChild
4320
- // (guaranteed by createBudgetPool capping maxPerChild to poolTokens)
4321
- // so we don't need Math.min(equalShare, originalMaxPerChild) here.
4322
- const equalShare = Math.floor(poolStats.tokensRemaining / tasks.length);
4323
- this.budgetPool.setMaxPerChild(equalShare);
4616
+ const dynamicPool = createDynamicBudgetPool(poolStats.tokensRemaining, 0.1);
4617
+ dynamicPool.setExpectedChildren(tasks.length);
4618
+ // Temporarily replace the budget pool so spawnAgent's reserve() uses the dynamic one
4619
+ this.budgetPool = dynamicPool;
4324
4620
  try {
4325
- const promises = tasks.map(({ agent, task }) => this.spawnAgent(agent, task));
4621
+ const promises = tasks.map(({ agent, task }) => {
4622
+ const spawnPromise = this.spawnAgent(agent, task);
4623
+ // Register with supervisor for monitoring
4624
+ if (supervisor) {
4625
+ const handle = createSubagentHandle(`parallel-${agent}-${Date.now()}`, agent, task, spawnPromise, {});
4626
+ supervisor.add(handle);
4627
+ }
4628
+ return spawnPromise;
4629
+ });
4326
4630
  settled = await Promise.allSettled(promises);
4327
4631
  }
4328
4632
  finally {
4329
- this.budgetPool.resetMaxPerChild();
4633
+ this.budgetPool = originalPool;
4634
+ supervisor?.stop();
4330
4635
  }
4331
4636
  }
4332
4637
  else {
@@ -5041,8 +5346,19 @@ If the task is a simple question or doesn't need specialized handling, set bestA
5041
5346
  this.unsubscribers = [];
5042
5347
  // Flush trace collector before cleanup
5043
5348
  await this.traceCollector?.flush();
5044
- // Clear blackboard (releases file claim locks)
5045
- this.blackboard?.clear();
5349
+ // Per-agent blackboard cleanup: release only this agent's claims and subscriptions
5350
+ // so parallel siblings don't lose their data. Only root agent clears everything.
5351
+ if (this.blackboard) {
5352
+ if (this.parentIterations > 0 && this.agentId) {
5353
+ // Subagent: release only our claims and subscriptions
5354
+ this.blackboard.releaseAll(this.agentId);
5355
+ this.blackboard.unsubscribeAgent(this.agentId);
5356
+ }
5357
+ else {
5358
+ // Root agent: full clear
5359
+ this.blackboard.clear();
5360
+ }
5361
+ }
5046
5362
  // Wait for any pending init before cleanup
5047
5363
  if (this.initPromises.length > 0) {
5048
5364
  try {