salmon-loop 0.4.1 → 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 (146) hide show
  1. package/dist/cli/authorization/provider.js +2 -10
  2. package/dist/cli/commands/config.js +2 -2
  3. package/dist/cli/commands/mode.js +2 -2
  4. package/dist/cli/commands/run/handler.js +3 -1
  5. package/dist/cli/commands/run/loop-params.js +1 -0
  6. package/dist/cli/commands/run/runtime-options.js +3 -1
  7. package/dist/cli/config.js +0 -8
  8. package/dist/cli/locales/en.js +2 -2
  9. package/dist/cli/reporters/standard.js +10 -0
  10. package/dist/core/adapters/fs/file-adapter.js +3 -1
  11. package/dist/core/adapters/git/git-adapter.js +6 -3
  12. package/dist/core/adapters/git/git-runner.js +5 -2
  13. package/dist/core/adapters/git/lock-manager.js +7 -4
  14. package/dist/core/checkpoint-domain/manifest-store.js +21 -13
  15. package/dist/core/checkpoint-domain/service.js +3 -1
  16. package/dist/core/config/limits.js +1 -1
  17. package/dist/core/config/model-pricing.js +61 -0
  18. package/dist/core/context/ast/skeleton-extractor.js +225 -0
  19. package/dist/core/context/ast/source-outline.js +24 -1
  20. package/dist/core/context/budget/dynamic-adjuster.js +20 -5
  21. package/dist/core/context/builder.js +7 -3
  22. package/dist/core/context/cache/store-factory.js +3 -1
  23. package/dist/core/context/dependencies.js +2 -1
  24. package/dist/core/context/effectiveness/persistence.js +50 -0
  25. package/dist/core/context/effectiveness/tracker.js +24 -0
  26. package/dist/core/context/gatherers/architecture-gatherer.js +2 -1
  27. package/dist/core/context/gatherers/artifact-gatherer.js +7 -4
  28. package/dist/core/context/gatherers/ast-gatherer.js +30 -28
  29. package/dist/core/context/gatherers/git-history-gatherer.js +3 -1
  30. package/dist/core/context/gatherers/knowledge-gatherer.js +18 -2
  31. package/dist/core/context/gatherers/metadata-gatherer.js +12 -7
  32. package/dist/core/context/gatherers/ripgrep-gatherer.js +6 -3
  33. package/dist/core/context/service.js +4 -2
  34. package/dist/core/context/steps/context-gather.js +14 -3
  35. package/dist/core/context/steps/context-targets.js +1 -0
  36. package/dist/core/context/targeting/target-resolver.js +29 -11
  37. package/dist/core/context/token/cache.js +5 -2
  38. package/dist/core/context/truncation/strategies/json.js +5 -2
  39. package/dist/core/context/truncation/type-detector.js +3 -1
  40. package/dist/core/extensions/paths.js +2 -2
  41. package/dist/core/facades/cli-authorization-provider.js +1 -0
  42. package/dist/core/feedback/parsers.js +290 -1
  43. package/dist/core/grizzco/dsl/llm-strategy.js +1 -1
  44. package/dist/core/grizzco/engine/observability/loop-telemetry.js +5 -2
  45. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +15 -3
  46. package/dist/core/grizzco/engine/transaction/attempt-failure.js +44 -20
  47. package/dist/core/grizzco/engine/transaction/transaction-runner.js +40 -34
  48. package/dist/core/grizzco/execution/RejectionManager.js +7 -5
  49. package/dist/core/grizzco/runtime/apply-back-runtime.js +3 -1
  50. package/dist/core/grizzco/services/implementations/default/GitConfigService.js +2 -1
  51. package/dist/core/grizzco/steps/autopilot.js +21 -32
  52. package/dist/core/grizzco/steps/explore.js +5 -2
  53. package/dist/core/grizzco/steps/generateReview.js +3 -1
  54. package/dist/core/grizzco/steps/research.js +3 -1
  55. package/dist/core/grizzco/steps/verify.js +7 -1
  56. package/dist/core/grizzco/validation/AstValidationService.js +3 -1
  57. package/dist/core/history/input-history.js +3 -1
  58. package/dist/core/intent/chat-intent.js +3 -1
  59. package/dist/core/llm/ai-sdk/message-mapper.js +13 -8
  60. package/dist/core/llm/ai-sdk/request-params.js +1 -3
  61. package/dist/core/llm/ai-sdk/retry-classifier.js +12 -4
  62. package/dist/core/llm/ai-sdk/retry-executor.js +1 -1
  63. package/dist/core/llm/errors.js +5 -4
  64. package/dist/core/llm/retry-utils.js +8 -2
  65. package/dist/core/llm/stream-utils.js +5 -3
  66. package/dist/core/llm/sub-agent-factory.js +3 -0
  67. package/dist/core/mcp/bridge/resource-context-provider.js +3 -1
  68. package/dist/core/mcp/catalog/discovery.js +3 -1
  69. package/dist/core/mcp/client/connection-manager.js +4 -2
  70. package/dist/core/mcp/client/transport-factory.js +7 -3
  71. package/dist/core/observability/audit-file.js +2 -1
  72. package/dist/core/observability/audit-trail.js +3 -1
  73. package/dist/core/observability/logger.js +2 -1
  74. package/dist/core/observability/monitor.js +24 -0
  75. package/dist/core/observability/run-outcome-reporter.js +1 -0
  76. package/dist/core/permission-gate/default-gate.js +5 -8
  77. package/dist/core/plan/storage.js +7 -4
  78. package/dist/core/plugin/loader.js +3 -1
  79. package/dist/core/prompts/registry.js +1 -1
  80. package/dist/core/prompts/runtime.js +3 -1
  81. package/dist/core/prompts/templates/system/autopilot_system.hbs +28 -4
  82. package/dist/core/protocols/a2a/sdk/executor.js +3 -1
  83. package/dist/core/protocols/a2a/sdk/server.js +3 -1
  84. package/dist/core/protocols/acp/acp-command-runner.js +7 -6
  85. package/dist/core/protocols/acp/acp-session-persistence.js +13 -10
  86. package/dist/core/protocols/acp/formal-agent.js +3 -2
  87. package/dist/core/protocols/acp/permission-provider.js +3 -2
  88. package/dist/core/reflection/engine.js +114 -14
  89. package/dist/core/runtime/batch-runner.js +81 -0
  90. package/dist/core/runtime/initialize.js +2 -1
  91. package/dist/core/runtime/loop-finalize.js +3 -0
  92. package/dist/core/runtime/loop-session-runner.js +5 -0
  93. package/dist/core/runtime/loop.js +4 -0
  94. package/dist/core/runtime/paths.js +9 -6
  95. package/dist/core/runtime/spawn-interactive.js +5 -4
  96. package/dist/core/security/redaction.js +3 -2
  97. package/dist/core/session/compression.js +3 -1
  98. package/dist/core/session/manager.js +2 -1
  99. package/dist/core/session/pruning-strategy.js +2 -1
  100. package/dist/core/session/token-tracker.js +11 -4
  101. package/dist/core/skills/permissions.js +2 -2
  102. package/dist/core/strata/checkpoint/manager.js +16 -10
  103. package/dist/core/strata/checkpoint/snapshot-create.js +5 -4
  104. package/dist/core/strata/checkpoint/snapshot-write-tree.js +7 -3
  105. package/dist/core/strata/engine/shadow-merge-engine.js +4 -2
  106. package/dist/core/strata/interaction/file-system-provider.js +2 -1
  107. package/dist/core/strata/layers/file-state-resolver.js +9 -7
  108. package/dist/core/strata/layers/immutable-git-layer.js +3 -1
  109. package/dist/core/strata/layers/shadow-driver/readonly-lock.js +8 -6
  110. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +2 -1
  111. package/dist/core/strata/layers/worktree.js +2 -1
  112. package/dist/core/strata/runtime/environment.js +2 -1
  113. package/dist/core/strata/runtime/synchronizer.js +18 -17
  114. package/dist/core/structured-output/json-extract.js +3 -1
  115. package/dist/core/sub-agent/artifacts/store.js +2 -1
  116. package/dist/core/sub-agent/core/manager.js +24 -1
  117. package/dist/core/sub-agent/registry-defaults.js +2 -2
  118. package/dist/core/sub-agent/summary.js +96 -0
  119. package/dist/core/sub-agent/tools/task-spawn.js +7 -4
  120. package/dist/core/target-runtime/profile.js +3 -1
  121. package/dist/core/tools/audit.js +3 -2
  122. package/dist/core/tools/budget.js +3 -1
  123. package/dist/core/tools/builtin/ast.js +144 -0
  124. package/dist/core/tools/builtin/code-search/backends/powershell.js +3 -1
  125. package/dist/core/tools/builtin/code-search/backends/rg.js +3 -1
  126. package/dist/core/tools/builtin/code-search/parse/plain-grep.js +3 -1
  127. package/dist/core/tools/builtin/code-search/parse/rg-json.js +3 -1
  128. package/dist/core/tools/builtin/fs.js +76 -1
  129. package/dist/core/tools/builtin/git.js +242 -0
  130. package/dist/core/tools/builtin/glob.js +79 -0
  131. package/dist/core/tools/builtin/index.js +12 -4
  132. package/dist/core/tools/builtin/knowledge.js +146 -4
  133. package/dist/core/tools/builtin/proposal.js +3 -1
  134. package/dist/core/tools/builtin/verify.js +35 -3
  135. package/dist/core/tools/permissions/permission-rules.js +3 -1
  136. package/dist/core/tools/router.js +88 -5
  137. package/dist/core/tools/session.js +10 -5
  138. package/dist/core/types/batch.js +2 -0
  139. package/dist/core/utils/sanitizer.js +5 -2
  140. package/dist/core/utils/serialize.js +5 -2
  141. package/dist/core/verification/detect-runner.js +86 -0
  142. package/dist/core/verification/runner.js +76 -0
  143. package/dist/core/version.js +3 -1
  144. package/dist/languages/python/index.js +154 -0
  145. package/dist/locales/en.js +6 -0
  146. package/package.json +2 -1
@@ -6,6 +6,7 @@ export class KnowledgeGatherer {
6
6
  static KNOWLEDGE_SUBDIR = 'knowledge';
7
7
  static SNAPSHOT_FILE = 'snapshot.json';
8
8
  static COMPACTION_THRESHOLD = 20; // Compact after 20 events
9
+ static MAX_DECISIONS = 50; // Keep only the most recent N decisions
9
10
  fileAdapter = new FileAdapter();
10
11
  async gather(req) {
11
12
  const { repoPath } = req;
@@ -15,6 +16,7 @@ export class KnowledgeGatherer {
15
16
  project_rules: undefined,
16
17
  architectural_decisions: [],
17
18
  user_preferences: undefined,
19
+ lessons_learned: [],
18
20
  };
19
21
  const allDeprecated = new Set();
20
22
  try {
@@ -58,23 +60,36 @@ export class KnowledgeGatherer {
58
60
  if (data.user_preferences) {
59
61
  aggregated.user_preferences = data.user_preferences;
60
62
  }
63
+ if (data.lessons_learned && Array.isArray(data.lessons_learned)) {
64
+ if (!Array.isArray(aggregated.lessons_learned)) {
65
+ aggregated.lessons_learned = [];
66
+ }
67
+ aggregated.lessons_learned.push(...data.lessons_learned);
68
+ }
61
69
  }
62
- catch {
70
+ catch (error) {
63
71
  // Skip corrupted files
72
+ getLogger().debug(`[KnowledgeGatherer] failed to load event file ${file}: ${error instanceof Error ? error.message : String(error)}`);
64
73
  }
65
74
  }
66
75
  // Filter out deprecated rules from aggregated project_rules
67
76
  if (aggregated.project_rules) {
68
77
  aggregated.project_rules = aggregated.project_rules.filter((r) => !allDeprecated.has(r));
69
78
  }
79
+ // Prune stale architectural decisions (keep only the most recent N)
80
+ if (aggregated.architectural_decisions &&
81
+ aggregated.architectural_decisions.length > KnowledgeGatherer.MAX_DECISIONS) {
82
+ aggregated.architectural_decisions = aggregated.architectural_decisions.slice(-KnowledgeGatherer.MAX_DECISIONS);
83
+ }
70
84
  // 3. Optional Compaction
71
85
  if (eventFiles.length >= KnowledgeGatherer.COMPACTION_THRESHOLD) {
72
86
  // Run compaction in background (non-blocking)
73
87
  this.compact(knowledgeDir, aggregated, eventFiles).catch((e) => getLogger().debug(`[KnowledgeGatherer] Compaction failed: ${e}`));
74
88
  }
75
89
  }
76
- catch {
90
+ catch (error) {
77
91
  // Directory missing or other read errors, return empty aggregated state
92
+ getLogger().debug(`[KnowledgeGatherer] knowledge directory read failed: ${error instanceof Error ? error.message : String(error)}`);
78
93
  }
79
94
  return {
80
95
  project_rules: aggregated.project_rules,
@@ -82,6 +97,7 @@ export class KnowledgeGatherer {
82
97
  ? aggregated.architectural_decisions
83
98
  : undefined,
84
99
  user_preferences: aggregated.user_preferences,
100
+ lessons_learned: aggregated.lessons_learned?.length ? aggregated.lessons_learned : undefined,
85
101
  };
86
102
  }
87
103
  async compact(knowledgeDir, aggregated, filesToMerge) {
@@ -1,4 +1,5 @@
1
1
  import { FileAdapter } from '../../adapters/fs/file-adapter.js';
2
+ import { getLogger } from '../../observability/logger.js';
2
3
  import { safeJoin } from '../../utils/path.js';
3
4
  export class MetadataGatherer {
4
5
  fileAdapter = new FileAdapter();
@@ -10,16 +11,18 @@ export class MetadataGatherer {
10
11
  const pkgRaw = await this.fileAdapter.readFile(safeJoin(repoPath, 'package.json'), 'utf-8');
11
12
  metadata.packageJson = JSON.parse(pkgRaw);
12
13
  }
13
- catch {
14
- // Ignored
14
+ catch (error) {
15
+ // Ignored - best-effort metadata
16
+ getLogger().debug(`[MetadataGatherer] package.json read failed: ${error instanceof Error ? error.message : String(error)}`);
15
17
  }
16
18
  // 2. README.md (first 1000 chars)
17
19
  try {
18
20
  const readmeRaw = await this.fileAdapter.readFile(safeJoin(repoPath, 'README.md'), 'utf-8');
19
21
  metadata.readmeHeader = readmeRaw.slice(0, 1000);
20
22
  }
21
- catch {
22
- // Ignored
23
+ catch (error) {
24
+ // Ignored - best-effort metadata
25
+ getLogger().debug(`[MetadataGatherer] README.md read failed: ${error instanceof Error ? error.message : String(error)}`);
23
26
  }
24
27
  // 3. AI Instructions (GEMINI.md, CLAUDE.md, ARCH.md)
25
28
  const aiFiles = ['GEMINI.md', 'CLAUDE.md', 'ARCH.md', '.gemini/ARCH.md'];
@@ -28,8 +31,9 @@ export class MetadataGatherer {
28
31
  const content = await this.fileAdapter.readFile(safeJoin(repoPath, file), 'utf-8');
29
32
  metadata.aiInstructions = (metadata.aiInstructions || '') + `\n--- ${file} ---\n${content}`;
30
33
  }
31
- catch {
32
- // Ignored
34
+ catch (error) {
35
+ // Ignored - best-effort metadata
36
+ getLogger().debug(`[MetadataGatherer] AI instruction file ${file} not found: ${error instanceof Error ? error.message : String(error)}`);
33
37
  }
34
38
  }
35
39
  // 4. List common config files
@@ -50,8 +54,9 @@ export class MetadataGatherer {
50
54
  await this.fileAdapter.readFile(safeJoin(repoPath, config), 'utf-8');
51
55
  metadata.configFiles.push(config);
52
56
  }
53
- catch {
57
+ catch (error) {
54
58
  // Ignored: config not found
59
+ getLogger().debug(`[MetadataGatherer] config file ${config} not found: ${error instanceof Error ? error.message : String(error)}`);
55
60
  }
56
61
  }
57
62
  return metadata;
@@ -30,7 +30,8 @@ export class RipgrepGatherer {
30
30
  try {
31
31
  entries = await fileAdapter.readdirWithTypes(absoluteCurrent);
32
32
  }
33
- catch {
33
+ catch (error) {
34
+ getLogger().debug(`[RipgrepGatherer] readdir failed for ${absoluteCurrent}: ${error instanceof Error ? error.message : String(error)}`);
34
35
  continue;
35
36
  }
36
37
  entries.sort((a, b) => a.name.localeCompare(b.name));
@@ -77,7 +78,8 @@ export class RipgrepGatherer {
77
78
  return results;
78
79
  }
79
80
  }
80
- catch {
81
+ catch (error) {
82
+ getLogger().debug(`[RipgrepGatherer] file read failed for ${absoluteFile}: ${error instanceof Error ? error.message : String(error)}`);
81
83
  continue;
82
84
  }
83
85
  }
@@ -149,8 +151,9 @@ export class RipgrepGatherer {
149
151
  });
150
152
  }
151
153
  }
152
- catch {
154
+ catch (error) {
153
155
  // Ignore malformed JSON.
156
+ getLogger().debug(`[RipgrepGatherer] JSON parse failed for ripgrep output line: ${error instanceof Error ? error.message : String(error)}`);
154
157
  }
155
158
  }
156
159
  return results;
@@ -167,7 +167,8 @@ export class ContextService {
167
167
  const stat = await this.fileAdapter.stat(absoluteFile);
168
168
  parts.push(this.formatStatSignature(relativeFile, stat));
169
169
  }
170
- catch {
170
+ catch (error) {
171
+ getLogger().debug(`[ContextService] stat failed for ${relativeFile}: ${error instanceof Error ? error.message : String(error)}`);
171
172
  parts.push(`${relativeFile}:missing`);
172
173
  }
173
174
  }
@@ -186,7 +187,8 @@ export class ContextService {
186
187
  const stat = await this.fileAdapter.stat(gitPath);
187
188
  parts.push(this.formatStatSignature(rel, stat));
188
189
  }
189
- catch {
190
+ catch (error) {
191
+ getLogger().debug(`[ContextService] stat failed for ${rel}: ${error instanceof Error ? error.message : String(error)}`);
190
192
  parts.push(`${rel}:missing`);
191
193
  }
192
194
  }
@@ -1,9 +1,12 @@
1
1
  import { FileAdapter } from '../../adapters/fs/file-adapter.js';
2
2
  import { LIMITS } from '../../config/limits.js';
3
+ import { getLogger } from '../../observability/logger.js';
3
4
  import { ensureInSandbox, normalizePath, safeJoin } from '../../utils/path.js';
4
- import { outlineSource } from '../ast/source-outline.js';
5
+ import { detectLang } from '../ast/skeleton-extractor.js';
6
+ import { outlineSourceAsync } from '../ast/source-outline.js';
5
7
  import { CONTEXT_AUDIT_ACTION, CONTEXT_AUDIT_PHASE } from '../audit-constants.js';
6
8
  import { recordContextAuditEvent } from '../audit.js';
9
+ import { getEffectivenessTracker } from '../effectiveness/tracker.js';
7
10
  import { extractKeywords } from '../keywords.js';
8
11
  import { assertNotAborted } from '../service-helpers.js';
9
12
  const fileAdapter = new FileAdapter();
@@ -18,7 +21,8 @@ async function readMatchedFileContent(req, file) {
18
21
  return null;
19
22
  return await fileAdapter.readFile(fullPath, 'utf-8');
20
23
  }
21
- catch {
24
+ catch (error) {
25
+ getLogger().debug(`[ContextGather] readMatchedFileContent failed for ${file}: ${error instanceof Error ? error.message : String(error)}`);
22
26
  return null;
23
27
  }
24
28
  }
@@ -59,7 +63,7 @@ export function buildContextGatherStep(deps) {
59
63
  kind: 'dependency',
60
64
  mode: content ? 'full' : 'outline',
61
65
  content: content ?? `ripgrep match at line ${snippet.line}: ${snippet.content}`,
62
- outline: content ? outlineSource(content) : undefined,
66
+ outline: content ? await outlineSourceAsync(content, detectLang(file)) : undefined,
63
67
  });
64
68
  }
65
69
  recordContextAuditEvent(CONTEXT_AUDIT_ACTION.gatherCompleted, {
@@ -75,6 +79,13 @@ export function buildContextGatherStep(deps) {
75
79
  hasKnowledgeBase: Boolean(knowledgeBase),
76
80
  hasRuntimeArtifacts: Boolean(runtimeArtifacts),
77
81
  }, { source: 'context', severity: 'low', scope: 'session', phase: CONTEXT_AUDIT_PHASE.gather });
82
+ // Record context usage for effectiveness tracking
83
+ const tracker = getEffectivenessTracker();
84
+ for (const file of astRes.relatedFiles) {
85
+ const tokens = file.content ? Math.ceil(file.content.length / 4) : 0;
86
+ const relevanceScore = file.mode === 'full' ? 80 : 40;
87
+ tracker.recordUsage(file.path, false, tokens, relevanceScore);
88
+ }
78
89
  return {
79
90
  req,
80
91
  diffScope,
@@ -20,6 +20,7 @@ export function buildContextTargetsStep(deps) {
20
20
  definitionMap: ast.definitionMap,
21
21
  symbolMap: ast.symbolMap,
22
22
  churnByFile: gitHistory?.churnByFile,
23
+ contextFiles: req.contextFiles,
23
24
  });
24
25
  assertNotAborted(req.signal);
25
26
  recordContextAuditEvent(CONTEXT_AUDIT_ACTION.targetsResolved, {
@@ -7,6 +7,8 @@ function reasonRank(reason) {
7
7
  switch (reason) {
8
8
  case 'explicit_path':
9
9
  return 100;
10
+ case 'context_file':
11
+ return 95;
10
12
  case 'symbol_definition':
11
13
  return 90;
12
14
  case 'diff_included':
@@ -140,6 +142,19 @@ function buildExplicitTargets(req) {
140
142
  evidence: 'instruction_path',
141
143
  })));
142
144
  }
145
+ function buildContextFileTargets(contextFiles) {
146
+ if (!contextFiles || contextFiles.length === 0)
147
+ return [];
148
+ return dedupeTargets(contextFiles
149
+ .map((f) => normalizePath(f).replace(/^(\.\/|\/)+/, ''))
150
+ .filter(Boolean)
151
+ .map((path) => ({
152
+ path,
153
+ reason: 'context_file',
154
+ confidence: 'high',
155
+ evidence: 'context_files_option',
156
+ })));
157
+ }
143
158
  function buildDiffTargets(includedFiles) {
144
159
  if (!includedFiles || includedFiles.length === 0)
145
160
  return [];
@@ -382,7 +397,7 @@ export class TargetResolver {
382
397
  };
383
398
  }
384
399
  async resolve(params) {
385
- const { req, includedFiles, importRelatedFiles, rgHitFiles, definitionMap, symbolMap, diffusionDepth, maxDiffusionTargets, churnByFile, } = params;
400
+ const { req, includedFiles, importRelatedFiles, rgHitFiles, definitionMap, symbolMap, diffusionDepth, maxDiffusionTargets, churnByFile, contextFiles, } = params;
386
401
  const runner = new MicroTaskRunner({
387
402
  debugLabel: 'context-targeting',
388
403
  maxRounds: 5,
@@ -390,7 +405,8 @@ export class TargetResolver {
390
405
  if (key === 'explicitTargets') {
391
406
  const primary = buildPrimaryTarget(ctx.primaryFile);
392
407
  const explicit = buildExplicitTargets(req);
393
- return dedupeTargets([...primary, ...explicit]);
408
+ const contextFileTargets = buildContextFileTargets(contextFiles);
409
+ return dedupeTargets([...primary, ...explicit, ...contextFileTargets]);
394
410
  }
395
411
  if (key === 'diffTargets') {
396
412
  const primary = buildPrimaryTarget(ctx.primaryFile);
@@ -433,34 +449,36 @@ export class TargetResolver {
433
449
  return [];
434
450
  },
435
451
  strategy: (engine) => {
452
+ const hasExplicitSignal = (data, key) => (data?.[key] || []).some((t) => t.reason === 'explicit_path' || t.reason === 'context_file');
453
+ const hasSymbolTargets = (data) => (data?.symbolTargets || []).some((t) => t.reason === 'symbol_definition');
454
+ const hasDiffTargets = (data) => (data?.diffTargets || []).some((t) => t.reason === 'diff_included');
436
455
  return engine
437
456
  .phase('Dependencies')
438
457
  .requireData(['explicitTargets', 'symbolTargets', 'diffTargets', 'defaultTargets'])
439
458
  .phase('Selection')
440
- .when((c) => (c.data?.explicitTargets || []).some((t) => t.reason === 'explicit_path'), (p) => {
459
+ .when((c) => hasExplicitSignal(c.data, 'explicitTargets'), (p) => {
441
460
  p.addAction('SET_TARGETS', {
442
461
  strategy: 'explicit',
443
462
  targets: engine.ctx.data.explicitTargets,
444
463
  });
445
464
  })
446
- .when((c) => !(c.data?.explicitTargets || []).some((t) => t.reason === 'explicit_path') &&
447
- (c.data?.symbolTargets || []).some((t) => t.reason === 'symbol_definition'), (p) => {
465
+ .when((c) => !hasExplicitSignal(c.data, 'explicitTargets') && hasSymbolTargets(c.data), (p) => {
448
466
  p.addAction('SET_TARGETS', {
449
467
  strategy: 'symbol',
450
468
  targets: engine.ctx.data.symbolTargets,
451
469
  });
452
470
  })
453
- .when((c) => !(c.data?.explicitTargets || []).some((t) => t.reason === 'explicit_path') &&
454
- !(c.data?.symbolTargets || []).some((t) => t.reason === 'symbol_definition') &&
455
- (c.data?.diffTargets || []).some((t) => t.reason === 'diff_included'), (p) => {
471
+ .when((c) => !hasExplicitSignal(c.data, 'explicitTargets') &&
472
+ !hasSymbolTargets(c.data) &&
473
+ hasDiffTargets(c.data), (p) => {
456
474
  p.addAction('SET_TARGETS', {
457
475
  strategy: 'diff',
458
476
  targets: engine.ctx.data.diffTargets,
459
477
  });
460
478
  })
461
- .unless((c) => (c.data?.explicitTargets || []).some((t) => t.reason === 'explicit_path') ||
462
- (c.data?.symbolTargets || []).some((t) => t.reason === 'symbol_definition') ||
463
- (c.data?.diffTargets || []).some((t) => t.reason === 'diff_included'), (p) => {
479
+ .unless((c) => hasExplicitSignal(c.data, 'explicitTargets') ||
480
+ hasSymbolTargets(c.data) ||
481
+ hasDiffTargets(c.data), (p) => {
464
482
  p.addAction('SET_TARGETS', {
465
483
  strategy: 'default',
466
484
  targets: engine.ctx.data.defaultTargets,
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { createHash } from 'crypto';
8
8
  import { FileAdapter } from '../../adapters/fs/file-adapter.js';
9
+ import { getLogger } from '../../observability/logger.js';
9
10
  /**
10
11
  * Two-level token cache for performance optimization.
11
12
  *
@@ -82,8 +83,9 @@ export class TokenCache {
82
83
  this.fileCache.set(filePath, entry);
83
84
  return entry;
84
85
  }
85
- catch {
86
+ catch (error) {
86
87
  // File doesn't exist or can't be accessed
88
+ getLogger().debug(`[TokenCache] file cache get failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
87
89
  this.fileCache.delete(filePath);
88
90
  this.misses++;
89
91
  return null;
@@ -108,8 +110,9 @@ export class TokenCache {
108
110
  contentHash: this.hashContent(content),
109
111
  });
110
112
  }
111
- catch {
113
+ catch (error) {
112
114
  // Ignore if file can't be accessed
115
+ getLogger().debug(`[TokenCache] file cache set failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
113
116
  }
114
117
  }
115
118
  // ==================== Invalidation ====================
@@ -4,6 +4,7 @@
4
4
  * Preserves JSON structure while truncating large arrays/objects.
5
5
  * Keeps root structure and key names visible.
6
6
  */
7
+ import { getLogger } from '../../../observability/logger.js';
7
8
  import { DEFAULT_TRUNCATION_CONFIG } from '../types.js';
8
9
  /**
9
10
  * JSON truncation strategy.
@@ -21,7 +22,8 @@ export class JsonStrategy {
21
22
  JSON.parse(output);
22
23
  return true;
23
24
  }
24
- catch {
25
+ catch (error) {
26
+ getLogger().debug(`[JsonStrategy] canHandle parse check failed: ${error instanceof Error ? error.message : String(error)}`);
25
27
  return false;
26
28
  }
27
29
  }
@@ -40,8 +42,9 @@ export class JsonStrategy {
40
42
  try {
41
43
  parsed = JSON.parse(output);
42
44
  }
43
- catch {
45
+ catch (error) {
44
46
  // Not valid JSON, fall back to simple truncation
47
+ getLogger().debug(`[JsonStrategy] JSON parse failed during truncation: ${error instanceof Error ? error.message : String(error)}`);
45
48
  return this.simpleTruncate(output, budget);
46
49
  }
47
50
  // Truncate while preserving structure
@@ -4,6 +4,7 @@
4
4
  * Analyzes output content to determine the most appropriate
5
5
  * truncation strategy.
6
6
  */
7
+ import { getLogger } from '../../observability/logger.js';
7
8
  /**
8
9
  * Detection patterns for each output type.
9
10
  */
@@ -84,8 +85,9 @@ export function detectOutputType(output) {
84
85
  try {
85
86
  JSON.parse(output);
86
87
  }
87
- catch {
88
+ catch (error) {
88
89
  // Not valid JSON, downgrade to generic
90
+ getLogger().debug(`[TypeDetector] JSON validation failed: ${error instanceof Error ? error.message : String(error)}`);
89
91
  bestType = 'generic';
90
92
  bestScore = 0;
91
93
  }
@@ -66,11 +66,11 @@ export function isWithinRoot(candidate, root) {
66
66
  const realRoot = realpathSync(resolvedRoot);
67
67
  return realCandidate === realRoot || realCandidate.startsWith(realRoot + path.sep);
68
68
  }
69
- catch {
69
+ catch (error) {
70
70
  // Candidate or root does not exist yet — fall back to lexical check.
71
71
  // This allows pre-declaring paths that will be created later, while
72
72
  // still catching obvious traversal sequences like `../../etc`.
73
- tryGetLogger()?.debug(`isWithinRoot: path not on disk, using lexical check for "${candidate}" against root "${root}"`);
73
+ tryGetLogger()?.debug(`[Paths] isWithinRoot: path not on disk, using lexical check for "${candidate}" against root "${root}": ${error instanceof Error ? error.message : String(error)}`);
74
74
  return (resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(resolvedRoot + path.sep));
75
75
  }
76
76
  }
@@ -1,2 +1,3 @@
1
+ export { DEFAULT_TOOL_AUTH } from '../config/defaults.js';
1
2
  export { getLogger } from '../observability/logger.js';
2
3
  //# sourceMappingURL=cli-authorization-provider.js.map