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
@@ -1,6 +1,7 @@
1
1
  import { execa } from 'execa';
2
2
  import { z } from 'zod';
3
3
  import { getLogger, McpConnectionManager, } from '../../core/facades/cli-authorization-non-interactive.js';
4
+ import { isRecord } from '../../core/facades/cli-utils-serialize.js';
4
5
  import { text } from '../locales/index.js';
5
6
  const DecisionSchema = z
6
7
  .object({
@@ -22,24 +23,19 @@ function findMcpServer(extensions, name) {
22
23
  return extensions.mcpServers.find((s) => s.enabled && s.name === name);
23
24
  }
24
25
  function extractDecisionPayloadFromMcpResult(result) {
25
- if (!result || typeof result !== 'object')
26
+ if (!isRecord(result))
26
27
  return result;
27
- const obj = result;
28
- if (obj.decision && typeof obj.decision === 'object')
29
- return obj.decision;
30
- if (typeof obj.outcome === 'string')
31
- return obj;
32
- const content = obj.content;
28
+ if (isRecord(result.decision))
29
+ return result.decision;
30
+ if (typeof result.outcome === 'string')
31
+ return result;
32
+ const content = result.content;
33
33
  if (!Array.isArray(content))
34
34
  return result;
35
35
  for (const item of content) {
36
- if (!item || typeof item !== 'object')
36
+ if (!isRecord(item))
37
37
  continue;
38
- const candidate = item.json ??
39
- item.text ??
40
- item.value ??
41
- item.data ??
42
- undefined;
38
+ const candidate = item.json ?? item.text ?? item.value ?? item.data ?? undefined;
43
39
  if (typeof candidate === 'string' && candidate.trim()) {
44
40
  try {
45
41
  return JSON.parse(candidate);
@@ -1,6 +1,5 @@
1
1
  import { createInterface } from 'readline/promises';
2
- import { getLogger } from '../../core/facades/cli-authorization-provider.js';
3
- import { TOOL_AUTH_CONFIG } from '../config.js';
2
+ import { DEFAULT_TOOL_AUTH, getLogger } from '../../core/facades/cli-authorization-provider.js';
4
3
  import { text } from '../locales/index.js';
5
4
  import { getPendingAuthorization, requestAuthorization } from '../ui/authorization/bus.js';
6
5
  import { loadAllowlistDecision, persistAllowlistDecision } from './allowlist.js';
@@ -27,14 +26,7 @@ const shouldAutoAllow = (request, config) => {
27
26
  const resolveConfig = (config) => {
28
27
  if (config)
29
28
  return config;
30
- return {
31
- sessionTtlMs: TOOL_AUTH_CONFIG.SESSION_TTL_MS,
32
- autoAllowRisk: TOOL_AUTH_CONFIG.AUTO_ALLOW_RISK,
33
- allowlist: {
34
- repoFile: '.salmonloop/config/authorization.json',
35
- userFile: '~/.salmonloop/config/authorization-user.json',
36
- },
37
- };
29
+ return DEFAULT_TOOL_AUTH;
38
30
  };
39
31
  export function createUiAuthorizationProvider(options) {
40
32
  const pending = new Map();
package/dist/cli/chat.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { buildSessionArtifactStateFromLoopResult, buildEffectiveConversationContext, ChatSessionManager, DEFAULT_LLM_OUTPUT_POLICY, emitLlmOutput, getDefaultSessionContextBudgetTokens, InputHistoryManager, logIgnoredError, getLogger, refreshSessionSummary, runSalmonLoop, TokenTracker, createInitialTracking, onNormalTurnComplete, runCompactionPipeline, reactiveCompact, } from '../core/facades/cli-chat.js';
2
2
  import { createSubAgentController } from '../core/facades/cli-subagent.js';
3
+ import { isRecord } from '../core/facades/cli-utils-serialize.js';
3
4
  import { createUiAuthorizationProvider } from './authorization/provider.js';
4
5
  import { resolveActiveChatFlowMode, resolveChatCheckpointStrategy } from './chat-flow.js';
5
6
  import { commands } from './commands/registry.js';
@@ -66,8 +67,9 @@ export async function startChatMode(options) {
66
67
  const answers = {};
67
68
  for (const question of input.questions) {
68
69
  if (requestOptions?.signal?.aborted) {
69
- const err = new Error(text.cli.askUserCancelled);
70
- err.code = 'ASK_USER_CANCELLED';
70
+ const err = Object.assign(new Error(text.cli.askUserCancelled), {
71
+ code: 'ASK_USER_CANCELLED',
72
+ });
71
73
  throw err;
72
74
  }
73
75
  const items = question.options.map((opt) => ({
@@ -84,8 +86,9 @@ export async function startChatMode(options) {
84
86
  multiSelect: question.multiSelect,
85
87
  });
86
88
  if (!selected || selected.length === 0) {
87
- const err = new Error(text.cli.askUserCancelled);
88
- err.code = 'ASK_USER_CANCELLED';
89
+ const err = Object.assign(new Error(text.cli.askUserCancelled), {
90
+ code: 'ASK_USER_CANCELLED',
91
+ });
89
92
  throw err;
90
93
  }
91
94
  const answer = question.multiSelect ? selected.join(', ') : (selected[0] ?? '');
@@ -299,6 +302,7 @@ export async function startChatMode(options) {
299
302
  authorizationMode: 'deferred',
300
303
  userInputProvider,
301
304
  subAgentController,
305
+ llmFactory: options.llmFactory,
302
306
  permissionMode: options.permissionMode,
303
307
  }).catch(async (error) => {
304
308
  // Level 2: Reactive Compact
@@ -307,13 +311,14 @@ export async function startChatMode(options) {
307
311
  const isContextOverflow = (() => {
308
312
  if (!error || typeof error !== 'object')
309
313
  return false;
310
- if ('llmCode' in error &&
314
+ if (isRecord(error) &&
315
+ 'llmCode' in error &&
311
316
  error.llmCode === 'LLM_CONTEXT_LENGTH_EXCEEDED') {
312
317
  return true;
313
318
  }
314
319
  const message = error instanceof Error
315
320
  ? error.message
316
- : typeof error.message === 'string'
321
+ : isRecord(error) && typeof error.message === 'string'
317
322
  ? String(error.message)
318
323
  : '';
319
324
  const lower = message.toLowerCase();
@@ -382,6 +387,7 @@ export async function startChatMode(options) {
382
387
  authorizationMode: 'deferred',
383
388
  userInputProvider,
384
389
  subAgentController,
390
+ llmFactory: options.llmFactory,
385
391
  permissionMode: options.permissionMode,
386
392
  });
387
393
  });
@@ -1,4 +1,4 @@
1
- import { EXECUTION_PHASES, getLogger } from '../../core/facades/cli-command-allowlist.js';
1
+ import { EXECUTION_PHASES, getLogger, } from '../../core/facades/cli-command-allowlist.js';
2
2
  import { clearAllowlist, clearAllowlistCache, listAllowlist, persistAllowlistDecision, removeAllowlistRule, } from '../authorization/allowlist.js';
3
3
  import { text } from '../locales/index.js';
4
4
  import { clearKnownToolNames, getKnownToolNames, getKnownToolNamesSync, validateSideEffects, } from './tool-names.js';
@@ -1,4 +1,4 @@
1
- import { createPluginRegistry, createPromptRegistry, createRuntimeLlm, setPluginRegistry, setPromptRegistry, ExtensionConfigError, getLogger, normalizePermissionMode, PluginLoader, resolveExecutionProfile, resolveExtensions, } from '../../core/facades/cli-command-chat.js';
1
+ import { createPluginRegistry, createPromptRegistry, createRuntimeLlm, createSubAgentLlmFactory, getString, setPluginRegistry, setPromptRegistry, ExtensionConfigError, getLogger, normalizePermissionMode, PluginLoader, resolveExecutionProfile, resolveExtensions, } from '../../core/facades/cli-command-chat.js';
2
2
  import { text } from '../locales/index.js';
3
3
  import { getOptionValueSourceWithGlobalFallback } from '../utils/command-option-source.js';
4
4
  import { resolveLlmOutputPolicyFromCli } from '../utils/llm-output.js';
@@ -8,26 +8,22 @@ import { resolveVerifyOption } from '../utils/verify-resolver.js';
8
8
  export async function handleChatCommand(options, command) {
9
9
  const allOptions = command.optsWithGlobals();
10
10
  const configResult = await resolveCliConfig({
11
- repo: allOptions.repo,
11
+ repo: getString(allOptions, 'repo') ?? undefined,
12
12
  cwd: process.cwd(),
13
- configPath: allOptions.config,
13
+ configPath: getString(allOptions, 'config') ?? undefined,
14
14
  enableConfigFile: allOptions.configFile !== false,
15
- auditScope: allOptions.auditScope,
15
+ auditScope: getString(allOptions, 'auditScope') ?? undefined,
16
16
  verbose: allOptions.verbose,
17
- logMode: allOptions.logMode,
17
+ logMode: getString(allOptions, 'logMode') ?? undefined,
18
18
  });
19
19
  if (!configResult.ok) {
20
20
  getLogger().error(configResult.message, true);
21
21
  process.exit(1);
22
22
  }
23
23
  const { resolvedConfig, auditScope, repoPath: runPath, verboseLevel } = configResult;
24
- const printInstruction = typeof allOptions.print === 'string'
25
- ? allOptions.print
26
- : undefined;
24
+ const printInstruction = getString(allOptions, 'print') ?? undefined;
27
25
  const continueSession = Boolean(allOptions.continue);
28
- const resumeSessionId = typeof allOptions.resume === 'string'
29
- ? allOptions.resume
30
- : undefined;
26
+ const resumeSessionId = getString(allOptions, 'resume') ?? undefined;
31
27
  if (printInstruction) {
32
28
  getLogger().error(text.cli.printCommandConflict('chat'), true);
33
29
  process.exit(1);
@@ -54,7 +50,7 @@ export async function handleChatCommand(options, command) {
54
50
  getLogger().error(`Invalid --mode "${String(rawPermissionMode)}". Expected "interactive" or "yolo".`);
55
51
  process.exit(1);
56
52
  }
57
- const llmOutputResolution = resolveLlmOutputPolicyFromCli(resolvedConfig.llmOutput, allOptions.llmOutput);
53
+ const llmOutputResolution = resolveLlmOutputPolicyFromCli(resolvedConfig.llmOutput, getString(allOptions, 'llmOutput') ?? undefined);
58
54
  if (!llmOutputResolution.ok) {
59
55
  getLogger().error(text.cli.invalidLlmOutputKind(llmOutputResolution.invalid), true);
60
56
  process.exitCode = 1;
@@ -64,6 +60,7 @@ export async function handleChatCommand(options, command) {
64
60
  const { llm } = createRuntimeLlm(resolvedConfig.llm, {
65
61
  langfuseEnabled: resolvedConfig.observability.langfuse.enabled,
66
62
  });
63
+ const llmFactory = createSubAgentLlmFactory(resolvedConfig.llm);
67
64
  const outcomeReporter = createOutcomeReporter({
68
65
  enabled: resolvedConfig.observability.langfuse.outcome,
69
66
  endpoint: resolvedConfig.observability.langfuse.endpoint,
@@ -71,7 +68,9 @@ export async function handleChatCommand(options, command) {
71
68
  langfuseApiKey: resolvedConfig.observability.langfuse.apiKey,
72
69
  });
73
70
  // Smart verification resolution with auto-detection
74
- const verifyCommand = await resolveVerifyOption(runPath, allOptions.verify, resolvedConfig.verify.command);
71
+ const verifyRaw = allOptions.verify;
72
+ const verifyCliOption = typeof verifyRaw === 'string' || typeof verifyRaw === 'boolean' ? verifyRaw : undefined;
73
+ const verifyCommand = await resolveVerifyOption(runPath, verifyCliOption, resolvedConfig.verify.command);
75
74
  // Verification is now optional - the loop will skip if undefined
76
75
  if (!verifyCommand) {
77
76
  getLogger().warn(text.verify.noCommandFound);
@@ -115,6 +114,7 @@ export async function handleChatCommand(options, command) {
115
114
  langfuseSessionId: resolvedConfig.observability.langfuse.sessionId,
116
115
  langfuseUserId: resolvedConfig.observability.langfuse.userId,
117
116
  languagePlugins,
117
+ llmFactory,
118
118
  });
119
119
  }
120
120
  catch (err) {
@@ -169,9 +169,9 @@ const permissionModeSubcommand = {
169
169
  usage: text.cli.configPermissionModeUsage,
170
170
  getSuggestions: (ctx) => modeCommand.getSuggestions?.({
171
171
  ...ctx,
172
- input: delegateInputToCommand(ctx.input, '/mode'),
172
+ input: delegateInputToCommand(ctx.input, '/permission-mode'),
173
173
  }) ?? [],
174
- execute: async (ctx) => modeCommand.execute({ ...ctx, input: delegateInputToCommand(ctx.input, '/mode') }),
174
+ execute: async (ctx) => modeCommand.execute({ ...ctx, input: delegateInputToCommand(ctx.input, '/permission-mode') }),
175
175
  };
176
176
  const outputSubcommand = {
177
177
  name: 'output',
@@ -41,8 +41,8 @@ async function persistPermissionMode(repoRoot, mode) {
41
41
  return configPath;
42
42
  }
43
43
  export const modeCommand = {
44
- name: '/mode',
45
- aliases: ['/permission-mode', '/perm-mode'],
44
+ name: '/permission-mode',
45
+ aliases: ['/mode', '/perm-mode'],
46
46
  description: text.cli.commandMode,
47
47
  order: 53,
48
48
  getSuggestions: ({ input }) => {
@@ -198,7 +198,7 @@ export const parallelCommand = {
198
198
  });
199
199
  try {
200
200
  const scheduler = new ParallelScheduler(toolstack.router, new InMemoryLockManager());
201
- const phase = state.runtime?.phase || 'PATCH';
201
+ const phase = state.runtime?.phase ?? 'PATCH';
202
202
  const runtime = {
203
203
  repoRoot: workspace.workPath,
204
204
  worktreeRoot: workspace.workPath,
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
- import { buildEffectiveConversationContext, createPluginRegistry, createPromptRegistry, getExitCode, getDefaultSessionContextBudgetTokens, getLogger, normalizePermissionMode, resolveExecutionProfile, SilentReporter, setPluginRegistry, setPromptRegistry, } from '../../../core/facades/cli-run-handler.js';
2
+ import { buildEffectiveConversationContext, createPluginRegistry, createPromptRegistry, createSubAgentLlmFactory, getExitCode, getDefaultSessionContextBudgetTokens, getLogger, normalizePermissionMode, resolveExecutionProfile, SilentReporter, setPluginRegistry, setPromptRegistry, } from '../../../core/facades/cli-run-handler.js';
3
3
  import { createStdoutWriter } from '../../headless/stdout-writer.js';
4
4
  import { text } from '../../locales/index.js';
5
5
  import { getOptionValueSourceWithGlobalFallback } from '../../utils/command-option-source.js';
@@ -201,6 +201,7 @@ export async function handleRunCommand(options, command) {
201
201
  const llmOutput = runtimeOptions.llmOutput;
202
202
  const effectiveVerify = runtimeOptions.effectiveVerify;
203
203
  const effectiveWorktreePrepare = runtimeOptions.effectiveWorktreePrepare;
204
+ const verifyPolicyOverride = runtimeOptions.verifyPolicyOverride;
204
205
  const instructionGuard = ensureInstructionOrExit({
205
206
  command,
206
207
  instruction,
@@ -303,7 +304,7 @@ export async function handleRunCommand(options, command) {
303
304
  }
304
305
  const extensionResolution = extensionsResult.extensionResolution;
305
306
  const operationalHeadlessWarnings = [];
306
- if (!effectiveVerify) {
307
+ if (!effectiveVerify && !verifyPolicyOverride) {
307
308
  if (!headlessOutput) {
308
309
  getLogger().warn(text.verify.noCommandFound);
309
310
  }
@@ -338,6 +339,7 @@ export async function handleRunCommand(options, command) {
338
339
  langfuseEnabled: resolvedConfig.observability.langfuse.enabled,
339
340
  headlessOutput,
340
341
  });
342
+ const llmFactory = createSubAgentLlmFactory(resolvedConfig.llm);
341
343
  let structuredOutputState = { ok: true, candidate: null };
342
344
  const reporter = createRunReporter({
343
345
  useGui,
@@ -398,6 +400,7 @@ export async function handleRunCommand(options, command) {
398
400
  const loopParams = buildRunLoopParams({
399
401
  instruction: instructionText,
400
402
  verify: effectiveVerify,
403
+ verifyPolicy: verifyPolicyOverride,
401
404
  repoPath: runPath,
402
405
  llm,
403
406
  languagePlugins,
@@ -435,6 +438,7 @@ export async function handleRunCommand(options, command) {
435
438
  ? { allow: allowedToolRules, deny: disallowedToolRules }
436
439
  : undefined,
437
440
  permissionMode,
441
+ llmFactory,
438
442
  });
439
443
  const buildAssistantMessage = (result) => buildRunAssistantMessage({ mode, result });
440
444
  const result = await executeRunLoop({
@@ -504,8 +508,9 @@ export async function handleRunCommand(options, command) {
504
508
  rawOutputProfile !== 'openai' &&
505
509
  activeReporterStarted &&
506
510
  activeReporter) {
507
- const error = new Error(text.cli.unexpectedError(msg));
508
- error.auditPath = lastKnownAuditPath;
511
+ const error = Object.assign(new Error(text.cli.unexpectedError(msg)), {
512
+ auditPath: lastKnownAuditPath,
513
+ });
509
514
  activeReporter.onError(error);
510
515
  }
511
516
  else if (outputFormat === 'stream-json') {
@@ -3,6 +3,7 @@ export function buildRunLoopParams(params) {
3
3
  return {
4
4
  instruction: params.instruction,
5
5
  verify: params.verify,
6
+ verifyPolicy: params.verifyPolicy,
6
7
  repoPath: params.repoPath,
7
8
  llm: params.llm,
8
9
  conversationContext: params.conversationContext,
@@ -35,6 +36,7 @@ export function buildRunLoopParams(params) {
35
36
  permissionMode: params.permissionMode,
36
37
  extensions: params.extensions,
37
38
  permissionRules: params.permissionRules,
39
+ llmFactory: params.llmFactory,
38
40
  eventPayload: params.headlessIncludeToolInput ||
39
41
  params.headlessIncludeToolOutput ||
40
42
  params.headlessIncludeAuthorizationDecisions
@@ -1,3 +1,4 @@
1
+ import { getString } from '../../../core/utils/serialize.js';
1
2
  import { resolveRepoPath } from '../../utils/resolve-cli-config.js';
2
3
  function splitToolRules(raw) {
3
4
  const parts = [];
@@ -20,39 +21,26 @@ function splitToolRules(raw) {
20
21
  }
21
22
  export function parseRunCommandOptions(command) {
22
23
  const allOptions = command.optsWithGlobals();
23
- const repoPath = resolveRepoPath({ repo: allOptions.repo, cwd: process.cwd() });
24
+ const repoPath = resolveRepoPath({
25
+ repo: getString(allOptions, 'repo') ?? undefined,
26
+ cwd: process.cwd(),
27
+ });
24
28
  const continueSession = Boolean(allOptions.continue);
25
- const resumeSessionId = typeof allOptions.resume === 'string'
26
- ? allOptions.resume
27
- : undefined;
28
- const printInstruction = typeof allOptions.print === 'string'
29
- ? allOptions.print
30
- : undefined;
31
- const explicitInstruction = typeof allOptions.instruction === 'string' ? allOptions.instruction : undefined;
32
- const jsonSchemaSpec = typeof allOptions.jsonSchema === 'string'
33
- ? allOptions.jsonSchema
34
- : undefined;
29
+ const resumeSessionId = getString(allOptions, 'resume') ?? undefined;
30
+ const printInstruction = getString(allOptions, 'print') ?? undefined;
31
+ const explicitInstruction = getString(allOptions, 'instruction') ?? undefined;
32
+ const jsonSchemaSpec = getString(allOptions, 'jsonSchema') ?? undefined;
35
33
  const rawOutputFormat = String(allOptions.outputFormat || 'text');
36
- const rawOutputProfile = typeof allOptions.outputProfile === 'string'
37
- ? String(allOptions.outputProfile)
38
- : undefined;
34
+ const rawOutputProfile = getString(allOptions, 'outputProfile') ?? undefined;
39
35
  const outputProfileForStreamJson = rawOutputProfile ?? 'native';
40
36
  const headlessIncludeToolInput = Boolean(allOptions.headlessIncludeToolInput);
41
37
  const headlessIncludeToolOutput = Boolean(allOptions.headlessIncludeToolOutput);
42
38
  const headlessIncludeAuthorizationDecisions = Boolean(allOptions.headlessIncludeAuthorizationDecisions);
43
39
  const allowOutsideCacheRoot = Boolean(allOptions.allowOutsideCacheRoot);
44
- const exportPatchPath = typeof allOptions.exportPatch === 'string'
45
- ? allOptions.exportPatch
46
- : undefined;
47
- const sweBenchInstanceId = typeof allOptions.sweBenchInstanceId === 'string'
48
- ? allOptions.sweBenchInstanceId
49
- : undefined;
50
- const sweBenchModelName = typeof allOptions.sweBenchModelName === 'string'
51
- ? allOptions.sweBenchModelName
52
- : undefined;
53
- const sweBenchPredictionsPath = typeof allOptions.sweBenchPredictions === 'string'
54
- ? allOptions.sweBenchPredictions
55
- : undefined;
40
+ const exportPatchPath = getString(allOptions, 'exportPatch') ?? undefined;
41
+ const sweBenchInstanceId = getString(allOptions, 'sweBenchInstanceId') ?? undefined;
42
+ const sweBenchModelName = getString(allOptions, 'sweBenchModelName') ?? undefined;
43
+ const sweBenchPredictionsPath = getString(allOptions, 'sweBenchPredictions') ?? undefined;
56
44
  const instruction = explicitInstruction ?? printInstruction;
57
45
  const allowedToolRules = splitToolRules(allOptions.allowedTools);
58
46
  const disallowedToolRules = splitToolRules(allOptions.disallowedTools);
@@ -41,24 +41,27 @@ export function createRuntimeLlmAndWarn(params) {
41
41
  continue;
42
42
  if (!target || typeof target !== 'object')
43
43
  continue;
44
+ const phaseTarget = target;
44
45
  const perPhaseConfig = {
45
- id: target.id,
46
- type: target.type,
47
- clientPackage: target.clientPackage,
46
+ id: phaseTarget.id,
47
+ type: phaseTarget.type,
48
+ clientPackage: phaseTarget.clientPackage,
48
49
  api: {
49
- baseUrl: target.api?.baseUrl,
50
- timeoutMs: target.api?.timeoutMs,
51
- headers: target.api?.headers,
52
- apiKey: target.api?.apiKey,
53
- apiKeySource: target.api?.apiKeySource,
50
+ baseUrl: phaseTarget.api?.baseUrl,
51
+ timeoutMs: phaseTarget.api?.timeoutMs,
52
+ headers: phaseTarget.api?.headers,
53
+ apiKey: phaseTarget.api?.apiKey,
54
+ apiKeySource: phaseTarget.api?.apiKeySource,
54
55
  },
55
56
  models: {
56
- selectedModelId: target.model?.id,
57
- selectedModelSlot: target.model?.slot || 'default',
57
+ selectedModelId: phaseTarget.model?.id,
58
+ selectedModelSlot: phaseTarget.model?.slot || 'default',
58
59
  },
59
- capabilities: target.capabilities,
60
+ capabilities: phaseTarget.capabilities,
60
61
  };
61
- const created = createRuntimeLlm(perPhaseConfig, { langfuseEnabled: params.langfuseEnabled });
62
+ const created = createRuntimeLlm(perPhaseConfig, {
63
+ langfuseEnabled: params.langfuseEnabled,
64
+ });
62
65
  warnings.push(...created.warnings);
63
66
  phaseLlms[phase] = created.llm;
64
67
  }
@@ -25,6 +25,8 @@ export async function resolveRunRuntimeOptions(params) {
25
25
  }
26
26
  const effectiveVerify = await resolveVerifyOption(params.repoPath, params.cliOptions.verify, params.resolvedConfig.verify.command, { quiet: params.headlessOutput });
27
27
  const effectiveWorktreePrepare = await resolveWorktreePrepareOption(params.repoPath, params.cliOptions.checkpointStrategy, params.cliOptions.worktreePrepare, { quiet: params.headlessOutput });
28
- return { ok: true, llmOutput, effectiveVerify, effectiveWorktreePrepare };
28
+ // When --no-verify is explicit, override verifyPolicy to 'never'
29
+ const verifyPolicyOverride = params.cliOptions.verify === false && effectiveVerify === undefined ? 'never' : undefined;
30
+ return { ok: true, llmOutput, effectiveVerify, effectiveWorktreePrepare, verifyPolicyOverride };
29
31
  }
30
32
  //# sourceMappingURL=runtime-options.js.map
@@ -5,12 +5,4 @@ export const CHAT_QUEUE_CONFIG = {
5
5
  THINKING_MIN_VISIBLE_MS: 150, // Reduced from 300ms to minimize perceived lag
6
6
  TASK_TIMEOUT_MS: 10 * 60 * 1000,
7
7
  };
8
- export const TOOL_AUTH_CONFIG = {
9
- SESSION_TTL_MS: 30 * 60 * 1000,
10
- AUTO_ALLOW_RISK: {
11
- low: true,
12
- medium: false,
13
- high: false,
14
- },
15
- };
16
8
  //# sourceMappingURL=config.js.map
@@ -1,11 +1,5 @@
1
1
  import { parseCanonicalFunctionCallItemId, } from '../../core/facades/cli-headless.js';
2
- function isRecord(value) {
3
- return Boolean(value) && typeof value === 'object';
4
- }
5
- function getString(record, key) {
6
- const value = record[key];
7
- return typeof value === 'string' ? value : null;
8
- }
2
+ import { isRecord, getString } from '../../core/utils/serialize.js';
9
3
  export class OpenAiResponsesCanonicalApplier {
10
4
  state;
11
5
  constructor(state) {
@@ -19,7 +19,7 @@ export const en = {
19
19
  chatCommandHistory: ' /history - Show iteration history',
20
20
  chatCommandQueue: ' /queue - Manage the chat queue',
21
21
  chatCommandAuth: ' /config allowlist - Manage tool allowlist',
22
- chatCommandMode: ' /mode - Set permission mode (interactive/yolo)',
22
+ chatCommandMode: ' /permission-mode - Set permission mode (interactive/yolo)',
23
23
  chatCommandConfig: ' /config - Settings (log-mode/view/output/allowlist/permission-mode)',
24
24
  chatSessionSaved: 'Session saved. Goodbye!',
25
25
  chatThinking: 'Thinking...',
@@ -108,7 +108,7 @@ export const en = {
108
108
  llmOutputUnavailable: 'LLM output configuration is unavailable in this mode.',
109
109
  llmOutputPersisted: (path) => `LLM output settings saved to ${path}`,
110
110
  llmOutputPersistFailed: (reason) => `Failed to save LLM output settings: ${reason}`,
111
- modeUsage: 'Usage: /mode <interactive|yolo>',
111
+ modeUsage: 'Usage: /permission-mode <interactive|yolo>',
112
112
  modeSuggestion: (mode) => {
113
113
  if (mode === 'interactive')
114
114
  return 'Interactive permission checks and allowlist rules';
@@ -110,6 +110,16 @@ export class StandardReporter {
110
110
  getLogger().info(text.cli.budgetSummaryTitle);
111
111
  getLogger().info(text.cli.budgetSummaryLine(s.attemptCount, s.adjustmentCount, s.alertCount, s.criticalDropCount, Math.round(s.avgUtilization * 100), Math.round(s.truncationRate * 100), Math.round(s.successRate * 100)));
112
112
  }
113
+ if (result.usage) {
114
+ const u = result.usage;
115
+ const tokenLine = `Tokens: ${u.inputTokens.toLocaleString()} in / ${u.outputTokens.toLocaleString()} out (${u.totalTokens.toLocaleString()} total)`;
116
+ if (u.estimatedCost !== undefined && u.estimatedCost > 0) {
117
+ getLogger().info(`${tokenLine} | Est. cost: $${u.estimatedCost.toFixed(2)}`);
118
+ }
119
+ else {
120
+ getLogger().info(tokenLine);
121
+ }
122
+ }
113
123
  if (this.verbose && result.logs) {
114
124
  getLogger().log('\n' + chalk.bold(text.cli.stepLogs));
115
125
  result.logs.forEach((log) => {
@@ -124,9 +134,8 @@ export class StandardReporter {
124
134
  getLogger().error(text.cli.unexpectedError(error.message), true);
125
135
  }
126
136
  initProgressBar() {
127
- // 🛡️ DCAP Defense: Check if stderr supports TTY operations to prevent clearLine crashes and unexpected termination
128
- const stream = process.stderr;
129
- if (typeof stream.clearLine !== 'function' || typeof stream.cursorTo !== 'function') {
137
+ // DCAP Defense: Check if stderr supports TTY operations to prevent clearLine crashes and unexpected termination
138
+ if (!('clearLine' in process.stderr) || !('cursorTo' in process.stderr)) {
130
139
  this.bar = {
131
140
  render: () => { },
132
141
  tick: () => { },
@@ -98,7 +98,8 @@ export class StreamJsonReporter {
98
98
  }));
99
99
  }
100
100
  onError(error) {
101
- const auditPath = typeof error.auditPath === 'string' ? error.auditPath : undefined;
101
+ const errorRecord = error;
102
+ const auditPath = typeof errorRecord.auditPath === 'string' ? errorRecord.auditPath : undefined;
102
103
  this.emit(encodeStreamFailure({
103
104
  uuid: this.uuid(),
104
105
  sessionId: this.sessionId,
@@ -135,10 +135,10 @@ export async function createCliSlashRuntime(options) {
135
135
  // @see https://agentskills.io/specification — Progressive disclosure
136
136
  const skill = await skillLoader.activateSkill(catalogEntry.id);
137
137
  const meta = (req.meta ?? {});
138
- const signal = meta?.signal;
138
+ const signal = meta.signal;
139
139
  // Prepare an isolated worktree environment for governed shell execution.
140
140
  const silentEmit = (event) => {
141
- if (event?.type === 'log' && event?.level === 'error') {
141
+ if (event.type === 'log' && event.level === 'error') {
142
142
  options.emit(event);
143
143
  return;
144
144
  }
@@ -9,7 +9,7 @@ import { prepareMessagePayload, sanitizeMessage } from '../utils/sanitizer.js';
9
9
  */
10
10
  export function useLoopEvents(mode, onStart, signal, options) {
11
11
  const store = useUIStore();
12
- const dispatch = store.dispatch;
12
+ const { dispatch } = store;
13
13
  const runStartedRef = useRef(false);
14
14
  const activeStreamIdRef = useRef(null);
15
15
  const logModeRef = useRef('normal');
@@ -31,7 +31,7 @@ export function useLoopState() {
31
31
  {
32
32
  id: Math.random().toString(36).substring(7),
33
33
  message: event.message,
34
- level: event.level,
34
+ level: event.level === 'trace' ? 'debug' : event.level,
35
35
  timestamp: event.timestamp,
36
36
  },
37
37
  ].slice(-100),
@@ -1,5 +1,6 @@
1
1
  import { promises as fs, constants } from 'fs';
2
2
  import * as path from 'path';
3
+ import { getLogger } from '../../observability/logger.js';
3
4
  import { AtomicFileWriter } from './atomic-file-writer.js';
4
5
  /**
5
6
  * Unified file system adapter for all file operations.
@@ -45,7 +46,8 @@ export class FileAdapter {
45
46
  await fs.access(filePath);
46
47
  return true;
47
48
  }
48
- catch {
49
+ catch (error) {
50
+ getLogger().debug(`[FileAdapter] exists check failed: ${error instanceof Error ? error.message : String(error)}`);
49
51
  return false;
50
52
  }
51
53
  }
@@ -662,8 +662,9 @@ export class GitAdapter {
662
662
  // Example: /tmp -> /private/tmp on macOS
663
663
  tmpReal = realpathSync(tmpResolved);
664
664
  }
665
- catch {
665
+ catch (error) {
666
666
  // Fall back to resolved path. If tmp is not realpath-resolvable, prefer denying shadow checks elsewhere.
667
+ getLogger().debug(`[GitAdapter] realpath resolve failed for tmpdir: ${error instanceof Error ? error.message : String(error)}`);
667
668
  tmpReal = tmpResolved;
668
669
  }
669
670
  return path.join(tmpReal, 's8p-wt');
@@ -674,7 +675,8 @@ export class GitAdapter {
674
675
  try {
675
676
  repoReal = realpathSync(repoResolved);
676
677
  }
677
- catch {
678
+ catch (error) {
679
+ getLogger().debug(`[GitAdapter] realpath resolve failed for repo: ${error instanceof Error ? error.message : String(error)}`);
678
680
  repoReal = repoResolved;
679
681
  }
680
682
  const parent = path.dirname(repoReal);
@@ -721,7 +723,8 @@ export class GitAdapter {
721
723
  // Prevents attacker from creating a symlink to main repo inside shadow root
722
724
  repo = realpathSync(repoResolved);
723
725
  }
724
- catch {
726
+ catch (error) {
727
+ getLogger().debug(`[GitAdapter] realpath resolve failed for shadow check: ${error instanceof Error ? error.message : String(error)}`);
725
728
  repo = repoResolved;
726
729
  }
727
730
  if (isPathWithinDirectory(expectedRoot, repo, { allowEqual: false }))
@@ -1,6 +1,7 @@
1
1
  import { realpathSync } from 'fs';
2
2
  import path from 'path';
3
3
  import { LIMITS } from '../../config/limits.js';
4
+ import { getLogger } from '../../observability/logger.js';
4
5
  import { spawnCommand } from '../../runtime/process-runner.js';
5
6
  function assertCwdSandboxed(repoRoot, cwd) {
6
7
  const resolvedRoot = path.resolve(repoRoot);
@@ -10,13 +11,15 @@ function assertCwdSandboxed(repoRoot, cwd) {
10
11
  try {
11
12
  realRoot = realpathSync(resolvedRoot);
12
13
  }
13
- catch {
14
+ catch (error) {
15
+ getLogger().debug(`[GitRunner] realpath resolve failed for root: ${error instanceof Error ? error.message : String(error)}`);
14
16
  realRoot = resolvedRoot;
15
17
  }
16
18
  try {
17
19
  realCwd = realpathSync(resolvedCwd);
18
20
  }
19
- catch {
21
+ catch (error) {
22
+ getLogger().debug(`[GitRunner] realpath resolve failed for cwd: ${error instanceof Error ? error.message : String(error)}`);
20
23
  realCwd = resolvedCwd;
21
24
  }
22
25
  const rel = path.relative(realRoot, realCwd);