salmon-loop 0.3.2 → 0.5.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 (227) hide show
  1. package/dist/cli/authorization/non-interactive.js +9 -13
  2. package/dist/cli/authorization/provider.js +2 -10
  3. package/dist/cli/chat.js +12 -6
  4. package/dist/cli/commands/allowlist.js +1 -1
  5. package/dist/cli/commands/chat.js +13 -13
  6. package/dist/cli/commands/config.js +2 -2
  7. package/dist/cli/commands/mode.js +2 -2
  8. package/dist/cli/commands/parallel.js +1 -1
  9. package/dist/cli/commands/run/handler.js +9 -4
  10. package/dist/cli/commands/run/loop-params.js +2 -0
  11. package/dist/cli/commands/run/parse-options.js +14 -26
  12. package/dist/cli/commands/run/runtime-llm.js +15 -12
  13. package/dist/cli/commands/run/runtime-options.js +3 -1
  14. package/dist/cli/config.js +0 -8
  15. package/dist/cli/headless/openai-responses-canonical-applier.js +1 -7
  16. package/dist/cli/locales/en.js +2 -2
  17. package/dist/cli/reporters/standard.js +12 -3
  18. package/dist/cli/reporters/stream-json.js +2 -1
  19. package/dist/cli/slash/runtime.js +2 -2
  20. package/dist/cli/ui/hooks/useLoopEvents.js +1 -1
  21. package/dist/cli/ui/hooks/useLoopState.js +1 -1
  22. package/dist/core/adapters/fs/file-adapter.js +3 -1
  23. package/dist/core/adapters/git/git-adapter.js +6 -3
  24. package/dist/core/adapters/git/git-runner.js +5 -2
  25. package/dist/core/adapters/git/lock-manager.js +7 -4
  26. package/dist/core/ast/parser.js +18 -9
  27. package/dist/core/checkpoint-domain/manifest-store.js +21 -13
  28. package/dist/core/checkpoint-domain/service.js +3 -1
  29. package/dist/core/config/limits.js +1 -1
  30. package/dist/core/config/model-pricing.js +61 -0
  31. package/dist/core/config/schema.js +738 -0
  32. package/dist/core/config/validate.js +11 -922
  33. package/dist/core/context/ast/skeleton-extractor.js +225 -0
  34. package/dist/core/context/ast/source-outline.js +24 -1
  35. package/dist/core/context/budget/dynamic-adjuster.js +20 -5
  36. package/dist/core/context/builder.js +7 -3
  37. package/dist/core/context/cache/store-factory.js +3 -1
  38. package/dist/core/context/dependencies.js +2 -1
  39. package/dist/core/context/effectiveness/persistence.js +50 -0
  40. package/dist/core/context/effectiveness/tracker.js +24 -0
  41. package/dist/core/context/gatherers/architecture-gatherer.js +2 -1
  42. package/dist/core/context/gatherers/artifact-gatherer.js +7 -4
  43. package/dist/core/context/gatherers/ast-gatherer.js +34 -40
  44. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +0 -1
  45. package/dist/core/context/gatherers/git-history-gatherer.js +3 -1
  46. package/dist/core/context/gatherers/knowledge-gatherer.js +21 -2
  47. package/dist/core/context/gatherers/metadata-gatherer.js +12 -7
  48. package/dist/core/context/gatherers/ripgrep-gatherer.js +6 -3
  49. package/dist/core/context/service.js +12 -2
  50. package/dist/core/context/steps/context-gather.js +14 -3
  51. package/dist/core/context/steps/context-targets.js +1 -0
  52. package/dist/core/context/targeting/target-resolver.js +29 -11
  53. package/dist/core/context/token/cache.js +5 -2
  54. package/dist/core/context/token/encoding-registry.js +7 -6
  55. package/dist/core/context/truncation/strategies/json.js +5 -2
  56. package/dist/core/context/truncation/type-detector.js +3 -1
  57. package/dist/core/extensions/index.js +48 -3
  58. package/dist/core/extensions/load.js +3 -2
  59. package/dist/core/extensions/merge.js +5 -1
  60. package/dist/core/extensions/paths.js +8 -2
  61. package/dist/core/extensions/schemas.js +21 -0
  62. package/dist/core/facades/cli-authorization-provider.js +1 -0
  63. package/dist/core/facades/cli-command-chat.js +2 -0
  64. package/dist/core/facades/cli-run-handler.js +1 -0
  65. package/dist/core/facades/cli-utils-serialize.js +2 -0
  66. package/dist/core/feedback/parsers.js +290 -1
  67. package/dist/core/grizzco/dsl/llm-strategy.js +4 -3
  68. package/dist/core/grizzco/engine/observability/loop-telemetry.js +5 -2
  69. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +30 -13
  70. package/dist/core/grizzco/engine/pipeline/pipeline.js +149 -240
  71. package/dist/core/grizzco/engine/transaction/attempt-failure.js +49 -24
  72. package/dist/core/grizzco/engine/transaction/authorization-summary.js +2 -1
  73. package/dist/core/grizzco/engine/transaction/transaction-runner.js +40 -34
  74. package/dist/core/grizzco/execution/RejectionManager.js +7 -5
  75. package/dist/core/grizzco/runtime/apply-back-runtime.js +5 -2
  76. package/dist/core/grizzco/services/implementations/default/GitConfigService.js +2 -1
  77. package/dist/core/grizzco/services/registry.js +18 -0
  78. package/dist/core/grizzco/steps/audit.js +20 -10
  79. package/dist/core/grizzco/steps/autopilot.js +21 -32
  80. package/dist/core/grizzco/steps/display-report.js +4 -11
  81. package/dist/core/grizzco/steps/explore.js +14 -4
  82. package/dist/core/grizzco/steps/generateReview.js +3 -1
  83. package/dist/core/grizzco/steps/patch/prompt-input.js +4 -1
  84. package/dist/core/grizzco/steps/patch.js +1 -0
  85. package/dist/core/grizzco/steps/plan.js +58 -49
  86. package/dist/core/grizzco/steps/research.js +3 -1
  87. package/dist/core/grizzco/steps/tool-runtime.js +3 -0
  88. package/dist/core/grizzco/steps/verify.js +7 -1
  89. package/dist/core/grizzco/validation/AstValidationService.js +3 -1
  90. package/dist/core/grizzco/workers/strata-sync-worker.js +2 -1
  91. package/dist/core/history/input-history.js +3 -1
  92. package/dist/core/intent/chat-intent.js +3 -1
  93. package/dist/core/llm/ai-sdk/message-mapper.js +37 -26
  94. package/dist/core/llm/ai-sdk/request-params.js +2 -6
  95. package/dist/core/llm/ai-sdk/result-mapper.js +14 -8
  96. package/dist/core/llm/ai-sdk/retry-classifier.js +17 -7
  97. package/dist/core/llm/ai-sdk/retry-executor.js +1 -1
  98. package/dist/core/llm/contracts/repair.js +16 -8
  99. package/dist/core/llm/errors.js +18 -14
  100. package/dist/core/llm/output-policy.js +8 -0
  101. package/dist/core/llm/redact.js +1 -3
  102. package/dist/core/llm/retry-utils.js +8 -2
  103. package/dist/core/llm/stream-utils.js +5 -3
  104. package/dist/core/llm/sub-agent-factory.js +51 -0
  105. package/dist/core/llm/tool-calling-stub.js +48 -0
  106. package/dist/core/llm/utils.js +17 -6
  107. package/dist/core/mcp/bridge/prompt-command-provider.js +4 -3
  108. package/dist/core/mcp/bridge/resource-context-provider.js +3 -1
  109. package/dist/core/mcp/bridge/tool-bridge.js +5 -14
  110. package/dist/core/mcp/catalog/discovery.js +3 -1
  111. package/dist/core/mcp/client/connection-manager.js +7 -4
  112. package/dist/core/mcp/client/transport-factory.js +7 -3
  113. package/dist/core/mcp/host/sampling-provider.js +1 -1
  114. package/dist/core/mcp/schema/json-schema-to-zod.js +2 -1
  115. package/dist/core/memory/relevant-retrieval.js +6 -4
  116. package/dist/core/observability/audit-file.js +2 -1
  117. package/dist/core/observability/audit-trail.js +3 -1
  118. package/dist/core/observability/authorization-decisions.js +13 -12
  119. package/dist/core/observability/error-mapping.js +2 -1
  120. package/dist/core/observability/logger.js +2 -1
  121. package/dist/core/observability/monitor.js +24 -0
  122. package/dist/core/observability/run-outcome-reporter.js +1 -0
  123. package/dist/core/observability/token-usage.js +5 -4
  124. package/dist/core/permission-gate/default-gate.js +5 -8
  125. package/dist/core/plan/storage.js +7 -4
  126. package/dist/core/plugin/loader.js +8 -5
  127. package/dist/core/prompts/registry.js +12 -30
  128. package/dist/core/prompts/runtime.js +3 -1
  129. package/dist/core/prompts/templates/system/autopilot_system.hbs +28 -4
  130. package/dist/core/protocols/a2a/sdk/executor.js +3 -1
  131. package/dist/core/protocols/a2a/sdk/server.js +5 -4
  132. package/dist/core/protocols/acp/acp-command-runner.js +7 -6
  133. package/dist/core/protocols/acp/acp-session-persistence.js +13 -10
  134. package/dist/core/protocols/acp/formal-agent.js +13 -6
  135. package/dist/core/protocols/acp/permission-provider.js +3 -2
  136. package/dist/core/protocols/acp/stdio-server.js +6 -6
  137. package/dist/core/reflection/engine.js +114 -14
  138. package/dist/core/runtime/agent-server-runtime.js +3 -2
  139. package/dist/core/runtime/batch-runner.js +81 -0
  140. package/dist/core/runtime/initialize.js +71 -6
  141. package/dist/core/runtime/loop-finalize.js +3 -0
  142. package/dist/core/runtime/loop-session-runner.js +5 -0
  143. package/dist/core/runtime/loop.js +4 -0
  144. package/dist/core/runtime/paths.js +9 -6
  145. package/dist/core/runtime/spawn-interactive.js +5 -4
  146. package/dist/core/security/redaction.js +3 -2
  147. package/dist/core/session/compaction/index.js +4 -3
  148. package/dist/core/session/compression.js +3 -1
  149. package/dist/core/session/manager.js +26 -38
  150. package/dist/core/session/pruning-strategy.js +2 -1
  151. package/dist/core/session/token-tracker.js +27 -9
  152. package/dist/core/skills/parser.js +3 -2
  153. package/dist/core/skills/permissions.js +2 -2
  154. package/dist/core/skills/runtime/MicroTaskRunner.js +1 -1
  155. package/dist/core/skills/runtime/SkillRunner.js +5 -2
  156. package/dist/core/slash/steps/slash-execute.js +7 -5
  157. package/dist/core/slash/strategy.js +1 -1
  158. package/dist/core/strata/checkpoint/manager.js +16 -10
  159. package/dist/core/strata/checkpoint/snapshot-create.js +5 -4
  160. package/dist/core/strata/checkpoint/snapshot-write-tree.js +7 -3
  161. package/dist/core/strata/engine/shadow-merge-engine.js +4 -2
  162. package/dist/core/strata/interaction/file-system-provider.js +2 -1
  163. package/dist/core/strata/layers/file-state-resolver.js +9 -7
  164. package/dist/core/strata/layers/immutable-git-layer.js +3 -1
  165. package/dist/core/strata/layers/shadow-driver/readonly-lock.js +8 -6
  166. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +2 -1
  167. package/dist/core/strata/layers/worktree.js +9 -10
  168. package/dist/core/strata/runtime/environment.js +2 -1
  169. package/dist/core/strata/runtime/synchronizer.js +28 -26
  170. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +1 -11
  171. package/dist/core/structured-output/json-extract.js +3 -1
  172. package/dist/core/structured-output/json-schema-validator.js +1 -13
  173. package/dist/core/sub-agent/artifacts/store.js +2 -1
  174. package/dist/core/sub-agent/context-snapshot.js +12 -6
  175. package/dist/core/sub-agent/controller.js +70 -1
  176. package/dist/core/sub-agent/core/loop.js +25 -3
  177. package/dist/core/sub-agent/core/manager.js +343 -117
  178. package/dist/core/sub-agent/registry-defaults.js +12 -0
  179. package/dist/core/sub-agent/registry.js +8 -0
  180. package/dist/core/sub-agent/summary.js +96 -0
  181. package/dist/core/sub-agent/team.js +98 -0
  182. package/dist/core/sub-agent/tools/task-await.js +109 -0
  183. package/dist/core/sub-agent/tools/task-spawn.js +52 -7
  184. package/dist/core/sub-agent/tools/team.js +92 -0
  185. package/dist/core/sub-agent/types.js +11 -2
  186. package/dist/core/target-runtime/profile.js +3 -1
  187. package/dist/core/tools/audit.js +3 -2
  188. package/dist/core/tools/budget.js +7 -12
  189. package/dist/core/tools/builtin/ast.js +144 -0
  190. package/dist/core/tools/builtin/code-search/backends/powershell.js +3 -1
  191. package/dist/core/tools/builtin/code-search/backends/rg.js +3 -1
  192. package/dist/core/tools/builtin/code-search/executor.js +46 -43
  193. package/dist/core/tools/builtin/code-search/parse/plain-grep.js +3 -1
  194. package/dist/core/tools/builtin/code-search/parse/rg-json.js +3 -1
  195. package/dist/core/tools/builtin/fs.js +90 -7
  196. package/dist/core/tools/builtin/git.js +242 -0
  197. package/dist/core/tools/builtin/glob.js +79 -0
  198. package/dist/core/tools/builtin/index.js +53 -111
  199. package/dist/core/tools/builtin/interaction.js +13 -15
  200. package/dist/core/tools/builtin/knowledge.js +146 -4
  201. package/dist/core/tools/builtin/proposal.js +14 -3
  202. package/dist/core/tools/builtin/verify.js +35 -3
  203. package/dist/core/tools/capability/executor.js +5 -5
  204. package/dist/core/tools/headless-payload.js +1 -3
  205. package/dist/core/tools/mapper.js +8 -42
  206. package/dist/core/tools/parallel/persistence.js +17 -5
  207. package/dist/core/tools/parallel/scheduler.js +23 -21
  208. package/dist/core/tools/permissions/permission-rules.js +69 -115
  209. package/dist/core/tools/plugins/loader.js +4 -3
  210. package/dist/core/tools/router.js +112 -58
  211. package/dist/core/tools/session.js +64 -102
  212. package/dist/core/tools/streaming/ToolCallAccumulator.js +1 -3
  213. package/dist/core/tools/tool-visibility.js +2 -1
  214. package/dist/core/tools/types.js +10 -0
  215. package/dist/core/types/batch.js +2 -0
  216. package/dist/core/utils/error.js +79 -0
  217. package/dist/core/utils/sanitizer.js +5 -2
  218. package/dist/core/utils/serialize.js +66 -0
  219. package/dist/core/utils/zod.js +29 -0
  220. package/dist/core/verification/detect-runner.js +86 -0
  221. package/dist/core/verification/runner.js +76 -0
  222. package/dist/core/version.js +3 -1
  223. package/dist/core/workspace/capabilities.js +3 -2
  224. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +9 -8
  225. package/dist/languages/python/index.js +154 -0
  226. package/dist/locales/en.js +8 -1
  227. package/package.json +2 -1
@@ -0,0 +1,225 @@
1
+ import { AstParser } from '../../ast/parser.js';
2
+ import { createLanguageSupportOrchestrator } from '../../language-support/orchestrator.js';
3
+ import { getLogger } from '../../observability/logger.js';
4
+ import { tryGetPluginRegistry } from '../../plugin/registry.js';
5
+ const MAX_SKELETON_LINES = 200;
6
+ /**
7
+ * Top-level declaration node types that should be included in the skeleton
8
+ * even if not captured by the definitions query. This handles imports, exports,
9
+ * and other top-level statements.
10
+ */
11
+ const TOP_LEVEL_INCLUDE_TYPES = new Set([
12
+ // JS/TS
13
+ 'import_statement',
14
+ 'export_statement',
15
+ 'import_declaration',
16
+ 'export_declaration',
17
+ // Python
18
+ 'import_from_statement',
19
+ // Rust
20
+ 'use_declaration',
21
+ 'extern_crate_declaration',
22
+ // Go
23
+ 'import_declaration',
24
+ 'package_clause',
25
+ ]);
26
+ /**
27
+ * Extract a cross-language code skeleton using tree-sitter AST.
28
+ *
29
+ * For each definition (function, class, method, type, etc.), the skeleton
30
+ * includes only the signature line — not the body. Imports and top-level
31
+ * declarations are preserved in full.
32
+ *
33
+ * Falls back to undefined if tree-sitter parsing fails (e.g., no grammar
34
+ * installed for the language). The caller should fall back to regex-based
35
+ * outlineSource in that case.
36
+ */
37
+ export async function extractSkeleton(sourceCode, lang) {
38
+ const plugin = tryGetPluginRegistry()?.getByExtension(`.${lang}`) ?? tryGetPluginRegistry()?.getById(lang);
39
+ if (!plugin)
40
+ return undefined;
41
+ try {
42
+ const tree = await AstParser.parse(sourceCode, lang);
43
+ if (!tree?.rootNode)
44
+ return undefined;
45
+ const lines = sourceCode.split('\n');
46
+ const includeLines = new Set();
47
+ const signatureLines = new Map(); // startRow -> signatureEndRow
48
+ // 1. Collect definition nodes via the plugin's definitions query
49
+ const orchestrator = createLanguageSupportOrchestrator();
50
+ const queryStr = await orchestrator.getASTQuery(lang, 'definitions');
51
+ if (queryStr) {
52
+ const captures = await AstParser.queryCapturesFromQuery(tree, lang, queryStr);
53
+ for (const capture of captures) {
54
+ if (capture.name === 'def') {
55
+ const sigEnd = findSignatureEnd(lines, capture.line - 1);
56
+ signatureLines.set(capture.line - 1, sigEnd);
57
+ }
58
+ }
59
+ }
60
+ // 2. Walk top-level nodes for imports and declarations not caught by the query
61
+ for (const child of tree.rootNode.children ?? []) {
62
+ const nodeType = child.type;
63
+ const startRow = child.startPosition.row;
64
+ if (TOP_LEVEL_INCLUDE_TYPES.has(nodeType)) {
65
+ // Include import/export lines in full
66
+ for (let r = child.startPosition.row; r <= child.endPosition.row; r++) {
67
+ includeLines.add(r);
68
+ }
69
+ }
70
+ else if (signatureLines.has(startRow)) {
71
+ // Already captured by definitions query — include signature only
72
+ // (handled below)
73
+ }
74
+ else if (isLikelyDeclaration(nodeType)) {
75
+ // Top-level declaration not caught by definitions query
76
+ const sigEnd = findSignatureEnd(lines, startRow);
77
+ signatureLines.set(startRow, sigEnd);
78
+ }
79
+ }
80
+ // 3. Also handle nested definitions (class methods, etc.)
81
+ collectNestedDefinitions(tree.rootNode, lines, signatureLines, 0);
82
+ // 4. Build the skeleton
83
+ const out = [];
84
+ const includedRows = new Set();
85
+ // First pass: include full import/export lines
86
+ for (const row of includeLines) {
87
+ if (out.length >= MAX_SKELETON_LINES)
88
+ break;
89
+ if (!includedRows.has(row)) {
90
+ out.push(lines[row]);
91
+ includedRows.add(row);
92
+ }
93
+ }
94
+ // Second pass: include signature lines from definitions
95
+ const sortedSigs = [...signatureLines.entries()].sort((a, b) => a[0] - b[0]);
96
+ for (const [startRow, sigEndRow] of sortedSigs) {
97
+ if (out.length >= MAX_SKELETON_LINES)
98
+ break;
99
+ for (let r = startRow; r <= sigEndRow; r++) {
100
+ if (out.length >= MAX_SKELETON_LINES)
101
+ break;
102
+ if (!includedRows.has(r)) {
103
+ out.push(lines[r]);
104
+ includedRows.add(r);
105
+ }
106
+ }
107
+ }
108
+ const result = out.join('\n').trim();
109
+ return result.length > 0 ? result : undefined;
110
+ }
111
+ catch (error) {
112
+ // Tree-sitter parsing failed (no grammar, parse error, etc.)
113
+ getLogger().debug(`[SkeletonExtractor] parse failed: ${error instanceof Error ? error.message : String(error)}`);
114
+ return undefined;
115
+ }
116
+ }
117
+ /**
118
+ * Recursively collect nested definitions (e.g., class methods) that may not
119
+ * be captured by the top-level definitions query.
120
+ */
121
+ function collectNestedDefinitions(node, lines, signatureLines, depth) {
122
+ if (depth > 3)
123
+ return; // Prevent deep recursion
124
+ for (const child of node.children ?? []) {
125
+ const startRow = child.startPosition.row;
126
+ if (isDefinitionNode(child.type) && !signatureLines.has(startRow)) {
127
+ const sigEnd = findSignatureEnd(lines, startRow);
128
+ signatureLines.set(startRow, sigEnd);
129
+ }
130
+ // Recurse into class/struct/impl bodies to find methods
131
+ if (isContainerNode(child.type)) {
132
+ collectNestedDefinitions(child, lines, signatureLines, depth + 1);
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Find the last line of a definition's signature (before the body starts).
138
+ * For single-line definitions, returns startRow.
139
+ * For multi-line definitions, returns the line containing the body-starting token.
140
+ */
141
+ function findSignatureEnd(lines, startRow) {
142
+ // Look up to 10 lines ahead for the body start
143
+ const maxLook = Math.min(startRow + 10, lines.length - 1);
144
+ for (let r = startRow; r <= maxLook; r++) {
145
+ const line = lines[r];
146
+ // C-family: body starts with '{'
147
+ if (line.includes('{'))
148
+ return r;
149
+ // Python: body starts with ':'
150
+ // Heuristic: the colon is at the end of a def/class/if/for/while line
151
+ if (/:\s*$/.test(line) && r > startRow)
152
+ return r;
153
+ // Single-line Python def: def foo(): pass
154
+ if (/:\s+\S/.test(line) && r === startRow)
155
+ return r;
156
+ // Rust: body starts with '{' or 'where' clause
157
+ if (line.includes('{'))
158
+ return r;
159
+ }
160
+ // Fallback: just the first line
161
+ return startRow;
162
+ }
163
+ /**
164
+ * Check if a node type is a definition-like node that should appear in skeleton.
165
+ */
166
+ function isDefinitionNode(type) {
167
+ return (type.includes('function') ||
168
+ type.includes('method') ||
169
+ type.includes('class') ||
170
+ type.includes('interface') ||
171
+ type.includes('type_alias') ||
172
+ type.includes('type_definition') ||
173
+ type.includes('enum') ||
174
+ type.includes('struct') ||
175
+ type.includes('impl') ||
176
+ type.includes('trait') ||
177
+ type.includes('module') ||
178
+ type.includes('namespace') ||
179
+ type === 'decorated_definition' ||
180
+ type === 'function_definition' ||
181
+ type === 'async_function_definition' ||
182
+ type === 'class_definition');
183
+ }
184
+ /**
185
+ * Check if a node type is a container that may hold nested definitions.
186
+ */
187
+ function isContainerNode(type) {
188
+ return (type.includes('class_body') ||
189
+ type.includes('interface_body') ||
190
+ type.includes('struct_body') ||
191
+ type.includes('impl_body') ||
192
+ type.includes('declaration_list') ||
193
+ type === 'block' ||
194
+ type === 'statement_block' ||
195
+ type === 'class_definition' ||
196
+ type === 'impl_item');
197
+ }
198
+ /**
199
+ * Check if a top-level node type looks like a declaration worth including.
200
+ */
201
+ function isLikelyDeclaration(type) {
202
+ return (type.includes('declaration') ||
203
+ type.includes('definition') ||
204
+ type.includes('variable') ||
205
+ type.includes('const') ||
206
+ type.includes('let') ||
207
+ type.includes('type_alias') ||
208
+ type.includes('struct') ||
209
+ type.includes('enum') ||
210
+ type.includes('trait') ||
211
+ type.includes('impl'));
212
+ }
213
+ /**
214
+ * Detect the tree-sitter language ID from a file path.
215
+ * Returns undefined if no matching plugin is found.
216
+ */
217
+ export function detectLang(filePath) {
218
+ const dotIndex = filePath.lastIndexOf('.');
219
+ if (dotIndex < 0)
220
+ return undefined;
221
+ const ext = filePath.slice(dotIndex).toLowerCase();
222
+ const plugin = tryGetPluginRegistry()?.getByExtension(ext);
223
+ return plugin?.meta.id;
224
+ }
225
+ //# sourceMappingURL=skeleton-extractor.js.map
@@ -1,4 +1,25 @@
1
+ import { extractSkeleton } from './skeleton-extractor.js';
1
2
  const MAX_OUTLINE_LINES = 200;
3
+ /**
4
+ * Generate an outline/skeleton of source code.
5
+ *
6
+ * When a language plugin with tree-sitter support is available, uses AST-based
7
+ * skeleton extraction (signature-only for definitions, full lines for imports).
8
+ * Falls back to regex-based line filtering for unsupported languages.
9
+ */
10
+ export async function outlineSourceAsync(sourceCode, lang) {
11
+ if (lang) {
12
+ const skeleton = await extractSkeleton(sourceCode, lang);
13
+ if (skeleton)
14
+ return skeleton;
15
+ }
16
+ return outlineSource(sourceCode);
17
+ }
18
+ /**
19
+ * Regex-based outline generator (fallback for unsupported languages).
20
+ * Recognizes JS/TS-style keywords: function, class, interface, type, enum,
21
+ * const/let/var declarations, import/export statements.
22
+ */
2
23
  export function outlineSource(sourceCode) {
3
24
  const lines = sourceCode.split('\n');
4
25
  const out = [];
@@ -16,7 +37,9 @@ export function outlineSource(sourceCode) {
16
37
  continue;
17
38
  }
18
39
  if (/^\s*(?:export\s+)?(?:declare\s+)?(interface|type|class|enum|function)\b/.test(line) ||
19
- /^\s*(?:export\s+)?(?:const|let|var)\s+[A-Za-z0-9_$]+\s*[:=]/.test(line)) {
40
+ /^\s*(?:export\s+)?(?:const|let|var)\s+[A-Za-z0-9_$]+\s*[:=]/.test(line) ||
41
+ /^\s*(?:async\s+)?(?:def|class)\s+\w+/.test(line) ||
42
+ /^\s*(?:from|import)\s+\w+/.test(line)) {
20
43
  include(line);
21
44
  }
22
45
  }
@@ -1,8 +1,5 @@
1
- /**
2
- * Dynamic Budget Adjuster
3
- *
4
- * Collects runtime metrics and adjusts budget based on actual usage patterns.
5
- */
1
+ import { getLogger } from '../../observability/logger.js';
2
+ import { getEffectivenessTracker } from '../effectiveness/tracker.js';
6
3
  /**
7
4
  * Budget adjustment strategy based on runtime feedback.
8
5
  */
@@ -76,6 +73,24 @@ export class DynamicBudgetAdjuster {
76
73
  if (successRate > 0.7 && truncationRate < 0.3) {
77
74
  return null; // Current budget is working well
78
75
  }
76
+ // Strategy 5: Effectiveness-based adjustment
77
+ // If context effectiveness is low, increase budget to allow more targeted context
78
+ try {
79
+ const effectivenessTracker = getEffectivenessTracker();
80
+ const metrics = effectivenessTracker.getMetrics();
81
+ if (metrics.totalSessions > 3 && metrics.tokenEfficiency < 0.3) {
82
+ const newBudget = Math.min(currentBudget * (1 + this.adjustmentStep * 0.5), this.maxBudget);
83
+ return {
84
+ newBudget: Math.round(newBudget),
85
+ reason: `Low context token efficiency (${metrics.tokenEfficiency.toFixed(2)})`,
86
+ confidence: 0.5,
87
+ };
88
+ }
89
+ }
90
+ catch (error) {
91
+ // Effectiveness tracker not available, skip
92
+ getLogger().debug(`[DynamicBudgetAdjuster] effectiveness tracker unavailable: ${error instanceof Error ? error.message : String(error)}`);
93
+ }
79
94
  return null;
80
95
  }
81
96
  /**
@@ -3,6 +3,7 @@ import { defaultPathAdapter } from '../adapters/path/path-adapter.js';
3
3
  import { ConfigError } from '../config/errors.js';
4
4
  import { LIMITS } from '../config/limits.js';
5
5
  import { resolveConfig } from '../config/resolve.js';
6
+ import { getLogger } from '../observability/logger.js';
6
7
  import { createDefaultPermissionGate } from '../permission-gate/default-gate.js';
7
8
  import { ensureInSandbox, normalizePath } from '../utils/path.js';
8
9
  /**
@@ -30,7 +31,8 @@ function getExtensionsPatternForPlugins(plugins) {
30
31
  return Array.from(extensions).join('|');
31
32
  }
32
33
  const fileAdapter = new FileAdapter();
33
- import { outlineSource } from './ast/source-outline.js';
34
+ import { detectLang } from './ast/skeleton-extractor.js';
35
+ import { outlineSourceAsync } from './ast/source-outline.js';
34
36
  import { createContextCacheStore } from './cache/store-factory.js';
35
37
  import { applySmartCompression } from './compression/smart-compress.js';
36
38
  import { findFileDependencies } from './dependencies.js';
@@ -85,7 +87,8 @@ async function readRepoFileText(repoPath, relativePath) {
85
87
  const fullPath = ensureInSandbox(repoPath, defaultPathAdapter.join(repoPath, normalized));
86
88
  return await fileAdapter.readFile(fullPath, 'utf-8');
87
89
  }
88
- catch {
90
+ catch (error) {
91
+ getLogger().debug(`[ContextBuilder] readRepoFileText failed for ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
89
92
  return null;
90
93
  }
91
94
  }
@@ -134,6 +137,7 @@ export class ContextBuilder {
134
137
  snapshotHash: options.snapshotHash,
135
138
  checkpointManager: options.checkpointManager,
136
139
  signal: options.signal,
140
+ contextFiles: options.contextFiles,
137
141
  });
138
142
  return result;
139
143
  }
@@ -239,7 +243,7 @@ export class ContextBuilder {
239
243
  if (content === null)
240
244
  continue;
241
245
  const isLarge = content.length > LIMITS.largeFileThresholdBytes;
242
- const outline = outlineSource(content);
246
+ const outline = await outlineSourceAsync(content, detectLang(p));
243
247
  newRelatedFiles.push({
244
248
  path: p,
245
249
  kind: normalizedFailed.includes(p) ? 'failed' : 'dependency',
@@ -1,5 +1,6 @@
1
1
  import { FileAdapter } from '../../adapters/fs/file-adapter.js';
2
2
  import { recordAuditEvent } from '../../observability/audit-trail.js';
3
+ import { getLogger } from '../../observability/logger.js';
3
4
  import { resolveContextCachePath } from './path-resolver.js';
4
5
  import { MemoryContextCacheStore, PersistentContextCacheStore, } from './store.js';
5
6
  export async function createContextCacheStore(repoPath, rawConfig, options) {
@@ -24,8 +25,9 @@ export async function createContextCacheStore(repoPath, rawConfig, options) {
24
25
  try {
25
26
  await cleanupAdapter.deleteFile(details.filePath);
26
27
  }
27
- catch {
28
+ catch (error) {
28
29
  // best-effort cleanup only
30
+ getLogger().debug(`[ContextCache] cleanup failed for ${details.filePath}: ${error instanceof Error ? error.message : String(error)}`);
29
31
  }
30
32
  };
31
33
  if (pathResolution.mode === 'persistent' && pathResolution.filePath) {
@@ -75,7 +75,8 @@ async function findDirectDependencies(filePath, repoPath, fsAdapter) {
75
75
  }
76
76
  return dependencies;
77
77
  }
78
- catch {
78
+ catch (error) {
79
+ getLogger().debug(`[Dependencies] failed to find direct dependencies for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
79
80
  return [];
80
81
  }
81
82
  }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Effectiveness data persistence.
3
+ *
4
+ * Saves and restores tracker state across sessions.
5
+ * Data is stored in `.salmonloop/runtime/effectiveness.json`.
6
+ */
7
+ import { mkdir, readFile, writeFile } from '../../adapters/fs/node-fs.js';
8
+ import { defaultPathAdapter } from '../../adapters/path/path-adapter.js';
9
+ import { logIgnoredError } from '../../observability/ignored-error.js';
10
+ import { getLogger } from '../../observability/logger.js';
11
+ import { getEffectivenessTracker } from './tracker.js';
12
+ const EFFECTIVENESS_FILE = 'effectiveness.json';
13
+ function getEffectivenessPath(repoRoot) {
14
+ return defaultPathAdapter.join(repoRoot, '.salmonloop', 'runtime', EFFECTIVENESS_FILE);
15
+ }
16
+ /**
17
+ * Persist current tracker state to disk.
18
+ */
19
+ export async function persistEffectiveness(repoRoot) {
20
+ try {
21
+ const tracker = getEffectivenessTracker();
22
+ const data = tracker.serialize();
23
+ const filePath = getEffectivenessPath(repoRoot);
24
+ await mkdir(defaultPathAdapter.join(repoRoot, '.salmonloop', 'runtime'), { recursive: true });
25
+ await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
26
+ }
27
+ catch (error) {
28
+ logIgnoredError('[Effectiveness] Failed to persist', error);
29
+ }
30
+ }
31
+ /**
32
+ * Restore tracker state from disk.
33
+ * Silently returns if no saved data exists.
34
+ */
35
+ export async function restoreEffectiveness(repoRoot) {
36
+ try {
37
+ const filePath = getEffectivenessPath(repoRoot);
38
+ const raw = await readFile(filePath, 'utf-8');
39
+ const data = JSON.parse(raw);
40
+ if (data && typeof data === 'object') {
41
+ const tracker = getEffectivenessTracker();
42
+ tracker.deserialize(data);
43
+ }
44
+ }
45
+ catch (error) {
46
+ // File missing or corrupted — start fresh
47
+ getLogger().debug(`[Effectiveness] restore failed, starting fresh: ${error instanceof Error ? error.message : String(error)}`);
48
+ }
49
+ }
50
+ //# sourceMappingURL=persistence.js.map
@@ -176,6 +176,30 @@ export class ContextEffectivenessTracker {
176
176
  this.totalExecutions = 0;
177
177
  this.sessionCount = 0;
178
178
  }
179
+ /**
180
+ * Serialize tracker state for persistence.
181
+ */
182
+ serialize() {
183
+ return {
184
+ usageRecords: this.usageRecords,
185
+ failureRecords: this.failureRecords,
186
+ totalTokensUsed: this.totalTokensUsed,
187
+ successfulExecutions: this.successfulExecutions,
188
+ totalExecutions: this.totalExecutions,
189
+ sessionCount: this.sessionCount,
190
+ };
191
+ }
192
+ /**
193
+ * Restore tracker state from serialized data.
194
+ */
195
+ deserialize(data) {
196
+ this.usageRecords = data.usageRecords ?? [];
197
+ this.failureRecords = data.failureRecords ?? [];
198
+ this.totalTokensUsed = data.totalTokensUsed ?? 0;
199
+ this.successfulExecutions = data.successfulExecutions ?? 0;
200
+ this.totalExecutions = data.totalExecutions ?? 0;
201
+ this.sessionCount = data.sessionCount ?? 0;
202
+ }
179
203
  /**
180
204
  * Aggregate file statistics.
181
205
  */
@@ -18,8 +18,9 @@ export class ArchitectureGatherer {
18
18
  return parsed;
19
19
  }
20
20
  }
21
- catch {
21
+ catch (error) {
22
22
  // Cache miss or corrupted, proceed to scan
23
+ getLogger().debug(`[ArchitectureGatherer] cache read failed: ${error instanceof Error ? error.message : String(error)}`);
23
24
  }
24
25
  const topology = {
25
26
  modules: [],
@@ -1,5 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { FileAdapter } from '../../adapters/fs/file-adapter.js';
3
+ import { getLogger } from '../../observability/logger.js';
3
4
  import { safeJoin } from '../../utils/path.js';
4
5
  export class ArtifactGatherer {
5
6
  static COMMON_BUILD_DIRS = ['dist', 'build', 'out', 'target', 'bin'];
@@ -25,8 +26,9 @@ export class ArtifactGatherer {
25
26
  const rootEntries = await this.fileAdapter.readdir(repoPath);
26
27
  artifacts.buildDirs = rootEntries.filter((e) => ArtifactGatherer.COMMON_BUILD_DIRS.includes(e));
27
28
  }
28
- catch {
29
- /* Ignore */
29
+ catch (error) {
30
+ /* Ignore - best-effort build dir detection */
31
+ getLogger().debug(`[ArtifactGatherer] build dir scan failed: ${error instanceof Error ? error.message : String(error)}`);
30
32
  }
31
33
  // 2. Lock Files & Hashes
32
34
  for (const lock of ArtifactGatherer.CRITICAL_LOCK_FILES) {
@@ -40,8 +42,9 @@ export class ArtifactGatherer {
40
42
  artifacts.lockFiles?.push({ path: lock, hash });
41
43
  }
42
44
  }
43
- catch {
44
- /* Ignore */
45
+ catch (error) {
46
+ /* Ignore - best-effort lock file detection */
47
+ getLogger().debug(`[ArtifactGatherer] lock file scan failed for ${lock}: ${error instanceof Error ? error.message : String(error)}`);
45
48
  }
46
49
  }
47
50
  // 3. Selective Env Vars (Names only, no values for security)
@@ -8,7 +8,8 @@ import { getPluginRegistry } from '../../plugin/registry.js';
8
8
  import { normalizePath, safeJoin } from '../../utils/path.js';
9
9
  import { extractImportSpecifiers } from '../ast/import-extractor.js';
10
10
  import { resolveImportCandidates } from '../ast/module-resolver.js';
11
- import { outlineSource } from '../ast/source-outline.js';
11
+ import { detectLang } from '../ast/skeleton-extractor.js';
12
+ import { outlineSourceAsync } from '../ast/source-outline.js';
12
13
  const AST_DEEP_TRIGGER_PATTERN = /\b(refactor|rename|migrate|cross[- ]?file|across|dependency|dependencies|module|architecture|global)\b/i;
13
14
  function getImportScanDepth(req) {
14
15
  const instruction = req.instruction || '';
@@ -23,7 +24,7 @@ async function buildSymbolMap(symbols, primaryFile, callNames, relatedFiles) {
23
24
  const nodes = [];
24
25
  const edges = [];
25
26
  const definitionNodeByName = new Map();
26
- // Process primary file symbols
27
+ // Phase 1: Collect all definitions (primary file first, then imported files)
27
28
  for (const def of definitions) {
28
29
  const id = `def:${def.name}:${def.location.start.line}:${def.location.start.column}`;
29
30
  definitionNodeByName.set(def.name, id);
@@ -35,27 +36,6 @@ async function buildSymbolMap(symbols, primaryFile, callNames, relatedFiles) {
35
36
  location: def.location,
36
37
  });
37
38
  }
38
- for (const ref of references) {
39
- const refId = `ref:${ref.name}:${ref.location.start.line}:${ref.location.start.column}`;
40
- nodes.push({
41
- id: refId,
42
- name: ref.name,
43
- kind: 'reference',
44
- path: primaryFile,
45
- location: ref.location,
46
- });
47
- const targetDefId = definitionNodeByName.get(ref.name);
48
- if (!targetDefId)
49
- continue;
50
- const edgeType = callNames.includes(ref.name) ? 'call' : 'reference';
51
- edges.push({
52
- from: refId,
53
- to: targetDefId,
54
- type: edgeType,
55
- confidence: 'high',
56
- });
57
- }
58
- // Process imported files
59
39
  for (const related of relatedFiles) {
60
40
  if (related.kind !== 'import' || related.mode !== 'full')
61
41
  continue;
@@ -74,7 +54,6 @@ async function buildSymbolMap(symbols, primaryFile, callNames, relatedFiles) {
74
54
  path: related.path,
75
55
  location: def.location,
76
56
  });
77
- // Allow cross-file references to find these definitions
78
57
  if (!definitionNodeByName.has(def.name)) {
79
58
  definitionNodeByName.set(def.name, id);
80
59
  }
@@ -84,6 +63,27 @@ async function buildSymbolMap(symbols, primaryFile, callNames, relatedFiles) {
84
63
  getLogger().debug(` [CONTEXT] Failed to extract symbols from ${related.path}: ${e}`);
85
64
  }
86
65
  }
66
+ // Phase 2: Create reference nodes and edges (now all definitions are available)
67
+ for (const ref of references) {
68
+ const refId = `ref:${ref.name}:${ref.location.start.line}:${ref.location.start.column}`;
69
+ nodes.push({
70
+ id: refId,
71
+ name: ref.name,
72
+ kind: 'reference',
73
+ path: primaryFile,
74
+ location: ref.location,
75
+ });
76
+ const targetDefId = definitionNodeByName.get(ref.name);
77
+ if (!targetDefId)
78
+ continue;
79
+ const edgeType = callNames.includes(ref.name) ? 'call' : 'reference';
80
+ edges.push({
81
+ from: refId,
82
+ to: targetDefId,
83
+ type: edgeType,
84
+ confidence: 'high',
85
+ });
86
+ }
87
87
  return { symbolMap: { nodes, edges } };
88
88
  }
89
89
  function summarizeControlFlow(primaryText) {
@@ -181,25 +181,17 @@ export class AstGatherer {
181
181
  async gatherCallNames(parsedTree, lang, callsQuery) {
182
182
  if (!parsedTree || !lang || !callsQuery)
183
183
  return [];
184
- const queryFn = AstParser.queryCapturesFromQuery;
185
- if (typeof queryFn !== 'function')
186
- return [];
187
- const captures = (await queryFn(parsedTree, lang, callsQuery));
184
+ const captures = await AstParser.queryCapturesFromQuery(parsedTree, lang, callsQuery);
188
185
  return Array.from(new Set(captures
189
186
  .filter((c) => c.name === 'callee' && c.text.trim().length > 0)
190
187
  .map((c) => c.text.trim())));
191
188
  }
192
189
  async gatherDeepAnalysis(primaryText, parsedTree, lang, flowPack) {
193
- const queryFn = AstParser.queryCapturesFromQuery;
194
- if (!parsedTree ||
195
- !lang ||
196
- !flowPack?.control ||
197
- !flowPack?.exceptions ||
198
- typeof queryFn !== 'function') {
190
+ if (!parsedTree || !lang || !flowPack?.control || !flowPack?.exceptions) {
199
191
  return summarizeControlFlow(primaryText);
200
192
  }
201
- const controlCaptures = (await queryFn(parsedTree, lang, flowPack.control));
202
- const exceptionCaptures = (await queryFn(parsedTree, lang, flowPack.exceptions));
193
+ const controlCaptures = await AstParser.queryCapturesFromQuery(parsedTree, lang, flowPack.control);
194
+ const exceptionCaptures = await AstParser.queryCapturesFromQuery(parsedTree, lang, flowPack.exceptions);
203
195
  const branchCount = controlCaptures.filter((c) => c.name === 'branch').length;
204
196
  const loopCount = controlCaptures.filter((c) => c.name === 'loop').length;
205
197
  const asyncBoundaryCount = controlCaptures.filter((c) => c.name === 'async').length;
@@ -306,7 +298,7 @@ export class AstGatherer {
306
298
  continue;
307
299
  if (!relatedSeen.has(normalized)) {
308
300
  relatedSeen.add(normalized);
309
- const outline = outlineSource(content);
301
+ const outline = await outlineSourceAsync(content, detectLang(normalized));
310
302
  const isLarge = content.length > LIMITS.largeFileThresholdBytes;
311
303
  related.push({
312
304
  path: normalized,
@@ -347,8 +339,9 @@ export class AstGatherer {
347
339
  await this.fileAdapter.readFile(safeJoin(req.repoPath, c), 'utf-8');
348
340
  return c;
349
341
  }
350
- catch {
351
- // continue
342
+ catch (error) {
343
+ // continue - file not found, try next candidate
344
+ getLogger().debug(`[AstGatherer] candidate file not found: ${c}: ${error instanceof Error ? error.message : String(error)}`);
352
345
  }
353
346
  }
354
347
  return null;
@@ -362,7 +355,8 @@ export class AstGatherer {
362
355
  try {
363
356
  return await this.fileAdapter.readFile(safeJoin(req.repoPath, filePath), 'utf-8');
364
357
  }
365
- catch {
358
+ catch (error) {
359
+ getLogger().debug(`[AstGatherer] readRepoFile failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
366
360
  return null;
367
361
  }
368
362
  }
@@ -39,7 +39,6 @@ export class GhostDependencyGatherer {
39
39
  mode: 'outline',
40
40
  content: `// Ghost Dependency: Shares tokens [${Array.from(data.tokens).join(', ')}]
41
41
  `,
42
- reason: 'ghost_dependency',
43
42
  }));
44
43
  }
45
44
  }
@@ -1,4 +1,5 @@
1
1
  import { GitAdapter } from '../../adapters/git/git-adapter.js';
2
+ import { getLogger } from '../../observability/logger.js';
2
3
  import { normalizePath } from '../../utils/path.js';
3
4
  function buildChurnIndex(logOutput) {
4
5
  const churn = {};
@@ -48,8 +49,9 @@ export class GitHistoryGatherer {
48
49
  churnByFile: Object.keys(churnByNumstat).length > 0 ? churnByNumstat : buildChurnIndex(churnLog),
49
50
  };
50
51
  }
51
- catch {
52
+ catch (error) {
52
53
  // Not a git repo or git not found
54
+ getLogger().debug(`[GitHistoryGatherer] git history gather failed: ${error instanceof Error ? error.message : String(error)}`);
53
55
  return {};
54
56
  }
55
57
  }