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
@@ -178,7 +178,8 @@ export class SessionArchiver {
178
178
  })),
179
179
  };
180
180
  }
181
- catch {
181
+ catch (error) {
182
+ getLogger().debug(`[SessionPruning] Failed to restore session from archive: ${error instanceof Error ? error.message : String(error)}`);
182
183
  return null;
183
184
  }
184
185
  }
@@ -1,6 +1,9 @@
1
1
  import path from 'path';
2
2
  import { FileAdapter } from '../adapters/fs/index.js';
3
+ import { estimateCost } from '../config/model-pricing.js';
3
4
  import { logIgnoredError } from '../observability/ignored-error.js';
5
+ import { getLogger } from '../observability/logger.js';
6
+ import { isRecord } from '../utils/serialize.js';
4
7
  /**
5
8
  * Token usage tracker for chat sessions.
6
9
  * Extracts and accumulates token statistics from LLM execution results.
@@ -21,26 +24,36 @@ export class TokenTracker {
21
24
  try {
22
25
  const auditRaw = await this.fileAdapter.readFile(result.auditPath, 'utf8');
23
26
  const audit = JSON.parse(auditRaw);
24
- const eventsRef = audit?.context?.eventsRef;
27
+ if (!isRecord(audit))
28
+ return null;
29
+ const context = isRecord(audit.context) ? audit.context : null;
30
+ const eventsRef = isRecord(context?.eventsRef) ? context.eventsRef : null;
25
31
  if (!eventsRef || typeof eventsRef.path !== 'string')
26
32
  return null;
27
33
  const eventsPath = path.isAbsolute(eventsRef.path)
28
34
  ? eventsRef.path
29
35
  : path.join(path.dirname(result.auditPath), eventsRef.path);
30
36
  const eventsRaw = await this.fileAdapter.readFile(eventsPath, 'utf8');
31
- const events = eventsRaw
32
- .split('\n')
33
- .filter((line) => line.trim().length > 0)
34
- .map((line) => JSON.parse(line));
37
+ const events = [];
38
+ for (const line of eventsRaw.split('\n')) {
39
+ if (line.trim().length === 0)
40
+ continue;
41
+ try {
42
+ events.push(JSON.parse(line));
43
+ }
44
+ catch (error) {
45
+ getLogger().debug(`[TokenTracker] Skipping malformed JSON line in events file: ${error instanceof Error ? error.message : String(error)}`);
46
+ }
47
+ }
35
48
  let inputTokens = 0;
36
49
  let outputTokens = 0;
37
50
  for (const event of events) {
38
- if (!event || typeof event !== 'object')
51
+ if (!isRecord(event))
39
52
  continue;
40
53
  if (event.action !== 'llm.usage')
41
54
  continue;
42
55
  const details = event.details;
43
- if (!details || typeof details !== 'object')
56
+ if (!isRecord(details))
44
57
  continue;
45
58
  const promptTokens = details.promptTokens;
46
59
  const completionTokens = details.completionTokens;
@@ -65,11 +78,16 @@ export class TokenTracker {
65
78
  }
66
79
  }
67
80
  /**
68
- * Accumulate tokens into session metadata
81
+ * Accumulate tokens into session metadata.
82
+ * Computes estimated cost if model pricing is available.
69
83
  */
70
- static accumulate(session, usage) {
84
+ static accumulate(session, usage, modelId) {
71
85
  session.meta.totalTokens.input += usage.inputTokens;
72
86
  session.meta.totalTokens.output += usage.outputTokens;
87
+ const cost = usage.estimatedCost ?? estimateCost(usage.inputTokens, usage.outputTokens, modelId);
88
+ if (cost !== undefined) {
89
+ session.meta.totalTokens.estimatedCost = (session.meta.totalTokens.estimatedCost ?? 0) + cost;
90
+ }
73
91
  }
74
92
  /**
75
93
  * Estimate token count based on text length
@@ -3,6 +3,7 @@ import { parse as parseYaml } from 'yaml';
3
3
  import { z } from 'zod';
4
4
  import { text } from '../../locales/index.js';
5
5
  import { tryGetLogger } from '../observability/logger.js';
6
+ import { errorMessage } from '../utils/error.js';
6
7
  /**
7
8
  * Safe logger accessor that never throws when the logger is not yet initialized.
8
9
  *
@@ -129,7 +130,7 @@ export class SkillParser {
129
130
  parsed = parseYaml(yamlRaw);
130
131
  }
131
132
  catch (error) {
132
- const reason = error instanceof Error ? error.message : String(error);
133
+ const reason = errorMessage(error);
133
134
  const msg = text.skills.yamlParseError(filePath, reason);
134
135
  safeLogger().error(msg);
135
136
  throw new Error(msg);
@@ -188,7 +189,7 @@ export class SkillParser {
188
189
  parsed = parseYaml(yamlRaw);
189
190
  }
190
191
  catch (error) {
191
- const reason = error instanceof Error ? error.message : String(error);
192
+ const reason = errorMessage(error);
192
193
  const msg = text.skills.yamlParseError(filePath, reason);
193
194
  safeLogger().error(msg);
194
195
  throw new Error(msg);
@@ -89,9 +89,9 @@ export class SkillPermissionManager {
89
89
  this.policies = [];
90
90
  }
91
91
  }
92
- catch {
92
+ catch (error) {
93
93
  const logger = tryGetLogger();
94
- logger?.warn(text.skills.permissionFileLoadError(this.filePath));
94
+ logger?.warn(`${text.skills.permissionFileLoadError(this.filePath)}: ${error instanceof Error ? error.message : String(error)}`);
95
95
  this.policies = [];
96
96
  }
97
97
  }
@@ -67,7 +67,7 @@ export class MicroTaskRunner {
67
67
  cmd,
68
68
  output: output,
69
69
  })),
70
- injectedPrompt: injectAction?.params?.prompt || '',
70
+ injectedPrompt: String(injectAction?.params?.prompt ?? ''),
71
71
  status: plan.shouldAbort ? 'FAILURE' : 'SUCCESS',
72
72
  };
73
73
  }
@@ -2,6 +2,7 @@ import * as crypto from 'crypto';
2
2
  import { MicroTaskRunner } from '../../grizzco/dsl/MicroTaskRunner.js';
3
3
  import { tryGetLogger } from '../../observability/logger.js';
4
4
  import { Phase } from '../../types/index.js';
5
+ import { isRecord } from '../../utils/serialize.js';
5
6
  import { emitSkillAuditEvent, generateSkillTraceId, hashSkillArgs } from '../audit.js';
6
7
  import { SkillParser } from '../parser.js';
7
8
  import { SkillStrategyDSL } from '../strategy.js';
@@ -227,7 +228,9 @@ export async function executeSkill(options) {
227
228
  argsHash,
228
229
  traceId,
229
230
  denyReason: result.error?.code || 'unknown',
230
- denySource: result.meta?.authorization?.source || 'policy',
231
+ denySource: isRecord(result.meta) && isRecord(result.meta.authorization)
232
+ ? result.meta.authorization.source
233
+ : 'policy',
231
234
  durationMs: Date.now() - startedAt,
232
235
  });
233
236
  }
@@ -264,7 +267,7 @@ export async function executeSkill(options) {
264
267
  cmd,
265
268
  output: String(output),
266
269
  })),
267
- injectedPrompt: String(inject?.params?.prompt ?? ''),
270
+ injectedPrompt: String((isRecord(inject?.params) ? inject.params.prompt : undefined) ?? ''),
268
271
  status,
269
272
  };
270
273
  }
@@ -2,12 +2,13 @@ function planToDecision(plan, options) {
2
2
  const action = plan.actions[0];
3
3
  if (!action)
4
4
  return { kind: 'consumed' };
5
+ const p = action.params ?? {};
5
6
  if (action.type === 'FORWARD_TEXT') {
6
- const next = String(action.params?.input ?? '');
7
+ const next = String(p.input ?? '');
7
8
  return { kind: 'forward', input: next };
8
9
  }
9
10
  if (action.type === 'UNKNOWN_SLASH') {
10
- const cmd = String(action.params?.commandName ?? '');
11
+ const cmd = String(p.commandName ?? '');
11
12
  if (options.unknownSlashPolicy === 'forward_as_text') {
12
13
  return { kind: 'forward', input: cmd };
13
14
  }
@@ -31,7 +32,8 @@ export function buildSlashExecuteStep(options, meta) {
31
32
  const decision = planToDecision(plan, { unknownSlashPolicy: options.unknownSlashPolicy });
32
33
  return { ...context, data: { ...data, __decision: decision } };
33
34
  }
34
- const commandName = String(action.params?.commandName ?? '');
35
+ const p = action.params ?? {};
36
+ const commandName = String(p.commandName ?? '');
35
37
  const spec = options.registry.find(commandName);
36
38
  if (!spec) {
37
39
  const decision = planToDecision({ ...plan, actions: [{ type: 'UNKNOWN_SLASH', params: { commandName } }] }, { unknownSlashPolicy: options.unknownSlashPolicy });
@@ -50,8 +52,8 @@ export function buildSlashExecuteStep(options, meta) {
50
52
  const req = {
51
53
  rawInput: context.input.raw,
52
54
  command: spec,
53
- argsText: String(action.params?.argsText ?? ''),
54
- tokens: Array.isArray(action.params?.tokens) ? action.params.tokens : [],
55
+ argsText: String(p.argsText ?? ''),
56
+ tokens: Array.isArray(p.tokens) ? p.tokens : [],
55
57
  meta,
56
58
  };
57
59
  const result = await handler.execute(req);
@@ -18,7 +18,7 @@ export const SlashStrategyDSL = (engine) => {
18
18
  .when((c) => c.input.isSlash && Boolean(c.resolved?.command), (p) => {
19
19
  p.setWorker('slash.execute');
20
20
  p.addAction('EXECUTE_SLASH', {
21
- commandName: engine.ctx.resolved.command.name,
21
+ commandName: engine.ctx.resolved?.command?.name ?? '',
22
22
  argsText: engine.ctx.input.argsText ?? '',
23
23
  tokens: engine.ctx.input.tokens ?? [],
24
24
  });
@@ -63,7 +63,8 @@ export class CheckpointManager {
63
63
  try {
64
64
  writeTreeProbe = await probeWriteTreeFailure(git);
65
65
  }
66
- catch {
66
+ catch (probeError) {
67
+ getLogger().debug(`[CheckpointManager] probeWriteTreeFailure failed: ${probeError instanceof Error ? probeError.message : String(probeError)}`);
67
68
  writeTreeProbe = {};
68
69
  }
69
70
  }
@@ -129,7 +130,8 @@ export class CheckpointManager {
129
130
  try {
130
131
  meta = JSON.parse(msg);
131
132
  }
132
- catch {
133
+ catch (error) {
134
+ getLogger().debug(`[CheckpointManager] Invalid snapshot metadata for ${snapshotHash}: ${error instanceof Error ? error.message : String(error)}`);
133
135
  throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
134
136
  }
135
137
  if (!meta.staged) {
@@ -268,8 +270,8 @@ export class CheckpointManager {
268
270
  try {
269
271
  await rm(tempIndexFile, { force: true });
270
272
  }
271
- catch {
272
- // Ignore cleanup errors
273
+ catch (error) {
274
+ getLogger().debug(`[CheckpointManager] Failed to cleanup temp index: ${error instanceof Error ? error.message : String(error)}`);
273
275
  }
274
276
  }
275
277
  }
@@ -292,7 +294,8 @@ export class CheckpointManager {
292
294
  try {
293
295
  meta = JSON.parse(msg);
294
296
  }
295
- catch {
297
+ catch (error) {
298
+ getLogger().debug(`[CheckpointManager] Invalid snapshot metadata for ${snapshotHash}: ${error instanceof Error ? error.message : String(error)}`);
296
299
  throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
297
300
  }
298
301
  // 3. Get Parent Commit (Original HEAD)
@@ -339,7 +342,8 @@ export class CheckpointManager {
339
342
  return { hash, timestamp, message, ref };
340
343
  });
341
344
  }
342
- catch {
345
+ catch (error) {
346
+ getLogger().debug(`[CheckpointManager] Failed to list snapshots: ${error instanceof Error ? error.message : String(error)}`);
343
347
  return [];
344
348
  }
345
349
  }
@@ -354,8 +358,8 @@ export class CheckpointManager {
354
358
  await git.exec(['update-ref', '-d', `refs/s8p/snapshots/${snapshotHash}`]);
355
359
  return;
356
360
  }
357
- catch {
358
- // If direct deletion fails (maybe hash mismatch or short hash), try to find the ref
361
+ catch (error) {
362
+ getLogger().debug(`[CheckpointManager] Direct snapshot deletion failed, trying ref search: ${error instanceof Error ? error.message : String(error)}`);
359
363
  }
360
364
  // Fallback: find the ref pointing to this hash
361
365
  // Note: This is expensive if there are many snapshots, but safe.
@@ -392,7 +396,8 @@ export class CheckpointManager {
392
396
  try {
393
397
  meta = JSON.parse(msg);
394
398
  }
395
- catch {
399
+ catch (error) {
400
+ getLogger().debug(`[CheckpointManager] Invalid snapshot metadata for ${snapshotHash}: ${error instanceof Error ? error.message : String(error)}`);
396
401
  throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
397
402
  }
398
403
  // Staged files: Diff between Parent (HEAD at time of snapshot) and Staged Tree
@@ -475,7 +480,8 @@ export class CheckpointManager {
475
480
  try {
476
481
  meta = JSON.parse(msg);
477
482
  }
478
- catch {
483
+ catch (error) {
484
+ getLogger().debug(`[CheckpointManager] Invalid backup metadata for ${backupHash}: ${error instanceof Error ? error.message : String(error)}`);
479
485
  throw new Error(`Invalid backup metadata for ${backupHash}`);
480
486
  }
481
487
  // 2. Restore Worktree and Index to the T1 state
@@ -2,6 +2,7 @@ import { randomBytes } from 'crypto';
2
2
  import { tmpdir } from 'os';
3
3
  import { join } from 'path';
4
4
  import { rm } from '../../adapters/fs/node-fs.js';
5
+ import { getLogger } from '../../observability/logger.js';
5
6
  import { normalizePath } from '../../utils/path.js';
6
7
  async function getSafeUntrackedFiles(git) {
7
8
  // --exclude-standard: Respect .gitignore
@@ -50,8 +51,8 @@ export async function createSnapshotCommitFromStagedTree(input) {
50
51
  await git.exec(['add', '--', file], { env });
51
52
  }
52
53
  }
53
- catch {
54
- // Ignore per-file add failures to keep snapshot best-effort.
54
+ catch (error) {
55
+ getLogger().debug(`[SnapshotCreate] Failed to add file ${file} to snapshot index: ${error instanceof Error ? error.message : String(error)}`);
55
56
  }
56
57
  }
57
58
  }
@@ -71,8 +72,8 @@ export async function createSnapshotCommitFromStagedTree(input) {
71
72
  try {
72
73
  await rm(tempIndexFile, { force: true });
73
74
  }
74
- catch {
75
- // Ignore cleanup errors.
75
+ catch (error) {
76
+ getLogger().debug(`[SnapshotCreate] Failed to clean up temp index file: ${error instanceof Error ? error.message : String(error)}`);
76
77
  }
77
78
  }
78
79
  }
@@ -1,5 +1,6 @@
1
1
  import { join } from 'path';
2
2
  import { stat } from '../../adapters/fs/node-fs.js';
3
+ import { getLogger } from '../../observability/logger.js';
3
4
  import { classifyGitFailureHint } from './snapshot-audit.js';
4
5
  export async function tryWriteTreeWithRetry(git, retryDelaysMs) {
5
6
  let attempts = 0;
@@ -28,7 +29,8 @@ export async function probeWriteTreeFailure(git) {
28
29
  details.indexLockPresent = true;
29
30
  details.indexLockAgeMs = Math.max(0, Math.floor(Date.now() - lockStat.mtimeMs));
30
31
  }
31
- catch {
32
+ catch (error) {
33
+ getLogger().debug(`[SnapshotWriteTree] Index lock not present or unreadable: ${error instanceof Error ? error.message : String(error)}`);
32
34
  details.indexLockPresent = false;
33
35
  }
34
36
  try {
@@ -39,7 +41,8 @@ export async function probeWriteTreeFailure(git) {
39
41
  .filter(Boolean).length;
40
42
  details.unmergedCount = count;
41
43
  }
42
- catch {
44
+ catch (error) {
45
+ getLogger().debug(`[SnapshotWriteTree] Failed to list unmerged files: ${error instanceof Error ? error.message : String(error)}`);
43
46
  details.unmergedCount = undefined;
44
47
  }
45
48
  try {
@@ -64,7 +67,8 @@ export async function probeWriteTreeFailure(git) {
64
67
  });
65
68
  }
66
69
  }
67
- catch {
70
+ catch (error) {
71
+ getLogger().debug(`[SnapshotWriteTree] Failed to probe work tree status: ${error instanceof Error ? error.message : String(error)}`);
68
72
  details.isInsideWorkTree = undefined;
69
73
  }
70
74
  return details;
@@ -269,7 +269,8 @@ export class ShadowMergeEngine {
269
269
  return false;
270
270
  return this.guardian.inspect(buffer).isBinary;
271
271
  }
272
- catch {
272
+ catch (error) {
273
+ getLogger().debug(`[ShadowMergeEngine] Binary detection failed for ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
273
274
  return false;
274
275
  }
275
276
  }
@@ -282,7 +283,8 @@ export class ShadowMergeEngine {
282
283
  try {
283
284
  return await git.show(ref, relativePath);
284
285
  }
285
- catch {
286
+ catch (error) {
287
+ getLogger().debug(`[ShadowMergeEngine] git show failed for ${relativePath}@${ref}: ${error instanceof Error ? error.message : String(error)}`);
286
288
  return null;
287
289
  }
288
290
  }
@@ -81,7 +81,8 @@ export class StrataFileSystemProvider {
81
81
  const buffer = await fs.readFile(safePath);
82
82
  return this.guardian.inspect(buffer).isBinary;
83
83
  }
84
- catch {
84
+ catch (error) {
85
+ getLogger().debug(`[StrataFileSystem] Binary detection failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
85
86
  return false;
86
87
  }
87
88
  }
@@ -3,6 +3,7 @@ import * as path from 'path';
3
3
  import * as fs from '../../adapters/fs/node-fs.js';
4
4
  import { LIMITS } from '../../config/limits.js';
5
5
  import { FileStatus } from '../../grizzco/domain/grizzco-types.js';
6
+ import { getLogger } from '../../observability/logger.js';
6
7
  /**
7
8
  * FileStateResolver
8
9
  * Responsibilities: Accurately scan workspace and index state, implementing the data foundation for Zero Index Access.
@@ -43,9 +44,8 @@ export class FileStateResolver {
43
44
  state.stagedContent = await this.git.show(':0', normalizedPath);
44
45
  state.workingContent = await fs.readFile(absolutePath);
45
46
  }
46
- catch {
47
- // Fallback: If we can't capture content, we might be in a race condition.
48
- // But proceed with what we have; strategy layer will handle missing content if needed.
47
+ catch (error) {
48
+ getLogger().debug(`[FileStateResolver] Failed to capture MM content for ${normalizedPath}: ${error instanceof Error ? error.message : String(error)}`);
49
49
  }
50
50
  }
51
51
  return state;
@@ -124,8 +124,8 @@ export class FileStateResolver {
124
124
  await fd.close();
125
125
  }
126
126
  }
127
- catch {
128
- // If file doesn't exist or can't be read, assume non-binary (safe default for new files)
127
+ catch (error) {
128
+ getLogger().debug(`[FileStateResolver] Binary detection failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
129
129
  return false;
130
130
  }
131
131
  }
@@ -137,7 +137,8 @@ export class FileStateResolver {
137
137
  const stats = await fs.lstat(filePath);
138
138
  return stats.isSymbolicLink();
139
139
  }
140
- catch {
140
+ catch (error) {
141
+ getLogger().debug(`[FileStateResolver] Symlink detection failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
141
142
  return false;
142
143
  }
143
144
  }
@@ -149,7 +150,8 @@ export class FileStateResolver {
149
150
  const stats = await fs.stat(filePath);
150
151
  return stats.size;
151
152
  }
152
- catch {
153
+ catch (error) {
154
+ getLogger().debug(`[FileStateResolver] File size check failed for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
153
155
  return 0;
154
156
  }
155
157
  }
@@ -4,6 +4,7 @@
4
4
  * Wraps existing CheckpointManager to provide StrataSystem interface
5
5
  * for L1 Git snapshot and worktree operations.
6
6
  */
7
+ import { getLogger } from '../../observability/logger.js';
7
8
  import { CheckpointManager } from '../checkpoint/manager.js';
8
9
  /**
9
10
  * ImmutableGitLayer Implementation
@@ -34,7 +35,8 @@ export class ImmutableGitLayerImpl {
34
35
  const content = await this.checkpointManager.getSnapshotFileContent(process.cwd(), 'HEAD', path);
35
36
  return Buffer.from(content);
36
37
  }
37
- catch {
38
+ catch (error) {
39
+ getLogger().debug(`[ImmutableGitLayer] Failed to get file from snapshot: ${error instanceof Error ? error.message : String(error)}`);
38
40
  return null;
39
41
  }
40
42
  }
@@ -129,7 +129,8 @@ async function removeLockByToken(lockPath, expectedToken) {
129
129
  try {
130
130
  await rename(lockPath, swapPath);
131
131
  }
132
- catch {
132
+ catch (error) {
133
+ getLogger().debug(`[ReadonlyLock] Failed to rename lock file for atomic swap: ${error instanceof Error ? error.message : String(error)}`);
133
134
  return false;
134
135
  }
135
136
  try {
@@ -141,18 +142,19 @@ async function removeLockByToken(lockPath, expectedToken) {
141
142
  try {
142
143
  await rename(swapPath, lockPath);
143
144
  }
144
- catch {
145
- // Best-effort rollback when concurrent updates happen.
145
+ catch (rollbackError) {
146
+ getLogger().debug(`[ReadonlyLock] Failed to rollback lock swap (token mismatch): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
146
147
  await unlink(swapPath).catch(() => null);
147
148
  }
148
149
  return false;
149
150
  }
150
- catch {
151
+ catch (error) {
152
+ getLogger().debug(`[ReadonlyLock] Failed to verify lock token after swap: ${error instanceof Error ? error.message : String(error)}`);
151
153
  try {
152
154
  await rename(swapPath, lockPath);
153
155
  }
154
- catch {
155
- // Best-effort rollback when concurrent updates happen.
156
+ catch (rollbackError) {
157
+ getLogger().debug(`[ReadonlyLock] Failed to rollback lock swap (verification error): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
156
158
  await unlink(swapPath).catch(() => null);
157
159
  }
158
160
  return false;
@@ -36,7 +36,8 @@ async function pointsToExpectedDependency(sourcePath, targetPath) {
36
36
  ]);
37
37
  return arePathsEquivalent(resolvedSourcePath, resolvedTargetPath);
38
38
  }
39
- catch {
39
+ catch (error) {
40
+ getLogger().debug(`[ShadowDriver] Failed to verify dependency path equivalence: ${error instanceof Error ? error.message : String(error)}`);
40
41
  return false;
41
42
  }
42
43
  }
@@ -5,7 +5,9 @@ import { text } from '../../../locales/index.js';
5
5
  import { access, readdir, realpath, rm } from '../../adapters/fs/node-fs.js';
6
6
  import { GitAdapter } from '../../adapters/git/git-adapter.js';
7
7
  import { getLogger } from '../../observability/logger.js';
8
+ import { errorMessage } from '../../utils/error.js';
8
9
  import { isPathWithinDirectory, normalizePath } from '../../utils/path.js';
10
+ import { isRecord } from '../../utils/serialize.js';
9
11
  import { detectDependencyPaths } from './shadow-driver/strategy.js';
10
12
  function resolveEnvironmentMode(options) {
11
13
  return options.environmentMode === 'parity' ? 'parity' : 'strict';
@@ -46,7 +48,8 @@ async function tryRealpath(value) {
46
48
  try {
47
49
  return await realpath(value);
48
50
  }
49
- catch {
51
+ catch (error) {
52
+ getLogger().debug(`[Worktree] Failed to resolve realpath for ${value}: ${error instanceof Error ? error.message : String(error)}`);
50
53
  return null;
51
54
  }
52
55
  }
@@ -73,14 +76,14 @@ async function removeProjectedWorktreeEntries(workPath) {
73
76
  worktreeRealPath = await realpath(workPath);
74
77
  }
75
78
  catch (error) {
76
- throw new Error(`Failed to resolve worktree path before git cleanup (${workPath}): ${error instanceof Error ? error.message : String(error)}`);
79
+ throw new Error(`Failed to resolve worktree path before git cleanup (${workPath}): ${errorMessage(error)}`);
77
80
  }
78
81
  let entries = [];
79
82
  try {
80
83
  entries = (await readdir(workPath, { withFileTypes: true }));
81
84
  }
82
85
  catch (error) {
83
- throw new Error(`Failed to enumerate worktree entries before git cleanup (${workPath}): ${error instanceof Error ? error.message : String(error)}`);
86
+ throw new Error(`Failed to enumerate worktree entries before git cleanup (${workPath}): ${errorMessage(error)}`);
84
87
  }
85
88
  for (const entry of entries) {
86
89
  const name = entry?.name;
@@ -116,7 +119,7 @@ async function pruneWorktreeDependencyRoots(baseRepoPath, worktreePath) {
116
119
  getLogger().debug(`Pruned disposable dependency root before worktree cleanup: ${dependencyRoot}`);
117
120
  }
118
121
  catch (error) {
119
- getLogger().debug(`Failed to prune dependency root before worktree cleanup (${dependencyRoot}): ${error instanceof Error ? error.message : String(error)}`);
122
+ getLogger().debug(`Failed to prune dependency root before worktree cleanup (${dependencyRoot}): ${errorMessage(error)}`);
120
123
  }
121
124
  }
122
125
  }
@@ -243,7 +246,7 @@ export class WorkspaceManager {
243
246
  return true;
244
247
  }
245
248
  catch (error) {
246
- if (error && typeof error === 'object' && error.code === 'ENOENT') {
249
+ if (isRecord(error) && error.code === 'ENOENT') {
247
250
  return false;
248
251
  }
249
252
  return true;
@@ -268,11 +271,7 @@ export class WorkspaceManager {
268
271
  }
269
272
  }
270
273
  catch (error) {
271
- const msg = error instanceof Error
272
- ? error instanceof Error
273
- ? error.message
274
- : String(error)
275
- : String(error);
274
+ const msg = errorMessage(error);
276
275
  onEvent?.({
277
276
  type: 'action.fallback',
278
277
  tool: 'git',
@@ -20,7 +20,8 @@ async function pathExists(target) {
20
20
  await stat(target);
21
21
  return true;
22
22
  }
23
- catch {
23
+ catch (error) {
24
+ getLogger().debug(`[RuntimeEnvironment] pathExists check failed for ${target}: ${error instanceof Error ? error.message : String(error)}`);
24
25
  return false;
25
26
  }
26
27
  }