salmon-loop 0.3.2 → 0.4.1

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 (121) hide show
  1. package/dist/cli/authorization/non-interactive.js +9 -13
  2. package/dist/cli/chat.js +12 -6
  3. package/dist/cli/commands/allowlist.js +1 -1
  4. package/dist/cli/commands/chat.js +13 -13
  5. package/dist/cli/commands/parallel.js +1 -1
  6. package/dist/cli/commands/run/handler.js +6 -3
  7. package/dist/cli/commands/run/loop-params.js +1 -0
  8. package/dist/cli/commands/run/parse-options.js +14 -26
  9. package/dist/cli/commands/run/runtime-llm.js +15 -12
  10. package/dist/cli/headless/openai-responses-canonical-applier.js +1 -7
  11. package/dist/cli/reporters/standard.js +2 -3
  12. package/dist/cli/reporters/stream-json.js +2 -1
  13. package/dist/cli/slash/runtime.js +2 -2
  14. package/dist/cli/ui/hooks/useLoopEvents.js +1 -1
  15. package/dist/cli/ui/hooks/useLoopState.js +1 -1
  16. package/dist/core/ast/parser.js +18 -9
  17. package/dist/core/config/schema.js +738 -0
  18. package/dist/core/config/validate.js +11 -922
  19. package/dist/core/context/gatherers/ast-gatherer.js +4 -12
  20. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +0 -1
  21. package/dist/core/context/gatherers/knowledge-gatherer.js +3 -0
  22. package/dist/core/context/service.js +8 -0
  23. package/dist/core/context/token/encoding-registry.js +7 -6
  24. package/dist/core/extensions/index.js +48 -3
  25. package/dist/core/extensions/load.js +3 -2
  26. package/dist/core/extensions/merge.js +5 -1
  27. package/dist/core/extensions/paths.js +6 -0
  28. package/dist/core/extensions/schemas.js +21 -0
  29. package/dist/core/facades/cli-command-chat.js +2 -0
  30. package/dist/core/facades/cli-run-handler.js +1 -0
  31. package/dist/core/facades/cli-utils-serialize.js +2 -0
  32. package/dist/core/grizzco/dsl/llm-strategy.js +3 -2
  33. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +15 -10
  34. package/dist/core/grizzco/engine/pipeline/pipeline.js +149 -240
  35. package/dist/core/grizzco/engine/transaction/attempt-failure.js +5 -4
  36. package/dist/core/grizzco/engine/transaction/authorization-summary.js +2 -1
  37. package/dist/core/grizzco/runtime/apply-back-runtime.js +2 -1
  38. package/dist/core/grizzco/services/registry.js +18 -0
  39. package/dist/core/grizzco/steps/audit.js +20 -10
  40. package/dist/core/grizzco/steps/display-report.js +4 -11
  41. package/dist/core/grizzco/steps/explore.js +9 -2
  42. package/dist/core/grizzco/steps/patch/prompt-input.js +4 -1
  43. package/dist/core/grizzco/steps/patch.js +1 -0
  44. package/dist/core/grizzco/steps/plan.js +58 -49
  45. package/dist/core/grizzco/steps/tool-runtime.js +3 -0
  46. package/dist/core/grizzco/workers/strata-sync-worker.js +2 -1
  47. package/dist/core/llm/ai-sdk/message-mapper.js +24 -18
  48. package/dist/core/llm/ai-sdk/request-params.js +1 -3
  49. package/dist/core/llm/ai-sdk/result-mapper.js +14 -8
  50. package/dist/core/llm/ai-sdk/retry-classifier.js +6 -4
  51. package/dist/core/llm/contracts/repair.js +16 -8
  52. package/dist/core/llm/errors.js +13 -10
  53. package/dist/core/llm/output-policy.js +8 -0
  54. package/dist/core/llm/redact.js +1 -3
  55. package/dist/core/llm/sub-agent-factory.js +48 -0
  56. package/dist/core/llm/tool-calling-stub.js +48 -0
  57. package/dist/core/llm/utils.js +17 -6
  58. package/dist/core/mcp/bridge/prompt-command-provider.js +4 -3
  59. package/dist/core/mcp/bridge/tool-bridge.js +5 -14
  60. package/dist/core/mcp/client/connection-manager.js +3 -2
  61. package/dist/core/mcp/host/sampling-provider.js +1 -1
  62. package/dist/core/mcp/schema/json-schema-to-zod.js +2 -1
  63. package/dist/core/memory/relevant-retrieval.js +6 -4
  64. package/dist/core/observability/authorization-decisions.js +13 -12
  65. package/dist/core/observability/error-mapping.js +2 -1
  66. package/dist/core/observability/token-usage.js +5 -4
  67. package/dist/core/plugin/loader.js +5 -4
  68. package/dist/core/prompts/registry.js +11 -29
  69. package/dist/core/protocols/a2a/sdk/server.js +2 -3
  70. package/dist/core/protocols/acp/formal-agent.js +10 -4
  71. package/dist/core/protocols/acp/stdio-server.js +6 -6
  72. package/dist/core/runtime/agent-server-runtime.js +3 -2
  73. package/dist/core/runtime/initialize.js +70 -6
  74. package/dist/core/session/compaction/index.js +4 -3
  75. package/dist/core/session/manager.js +24 -37
  76. package/dist/core/session/token-tracker.js +18 -7
  77. package/dist/core/skills/parser.js +3 -2
  78. package/dist/core/skills/runtime/MicroTaskRunner.js +1 -1
  79. package/dist/core/skills/runtime/SkillRunner.js +5 -2
  80. package/dist/core/slash/steps/slash-execute.js +7 -5
  81. package/dist/core/slash/strategy.js +1 -1
  82. package/dist/core/strata/layers/worktree.js +7 -9
  83. package/dist/core/strata/runtime/synchronizer.js +10 -9
  84. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +1 -11
  85. package/dist/core/structured-output/json-schema-validator.js +1 -13
  86. package/dist/core/sub-agent/context-snapshot.js +12 -6
  87. package/dist/core/sub-agent/controller.js +70 -1
  88. package/dist/core/sub-agent/core/loop.js +25 -3
  89. package/dist/core/sub-agent/core/manager.js +319 -116
  90. package/dist/core/sub-agent/registry-defaults.js +12 -0
  91. package/dist/core/sub-agent/registry.js +8 -0
  92. package/dist/core/sub-agent/team.js +98 -0
  93. package/dist/core/sub-agent/tools/task-await.js +109 -0
  94. package/dist/core/sub-agent/tools/task-spawn.js +49 -7
  95. package/dist/core/sub-agent/tools/team.js +92 -0
  96. package/dist/core/sub-agent/types.js +11 -2
  97. package/dist/core/tools/budget.js +4 -11
  98. package/dist/core/tools/builtin/code-search/executor.js +46 -43
  99. package/dist/core/tools/builtin/fs.js +14 -6
  100. package/dist/core/tools/builtin/index.js +41 -107
  101. package/dist/core/tools/builtin/interaction.js +13 -15
  102. package/dist/core/tools/builtin/proposal.js +11 -2
  103. package/dist/core/tools/capability/executor.js +5 -5
  104. package/dist/core/tools/headless-payload.js +1 -3
  105. package/dist/core/tools/mapper.js +8 -42
  106. package/dist/core/tools/parallel/persistence.js +17 -5
  107. package/dist/core/tools/parallel/scheduler.js +23 -21
  108. package/dist/core/tools/permissions/permission-rules.js +66 -114
  109. package/dist/core/tools/plugins/loader.js +4 -3
  110. package/dist/core/tools/router.js +24 -53
  111. package/dist/core/tools/session.js +54 -97
  112. package/dist/core/tools/streaming/ToolCallAccumulator.js +1 -3
  113. package/dist/core/tools/tool-visibility.js +2 -1
  114. package/dist/core/tools/types.js +10 -0
  115. package/dist/core/utils/error.js +79 -0
  116. package/dist/core/utils/serialize.js +63 -0
  117. package/dist/core/utils/zod.js +29 -0
  118. package/dist/core/workspace/capabilities.js +3 -2
  119. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +9 -8
  120. package/dist/locales/en.js +2 -1
  121. package/package.json +1 -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);
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) {
@@ -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';
@@ -338,6 +338,7 @@ export async function handleRunCommand(options, command) {
338
338
  langfuseEnabled: resolvedConfig.observability.langfuse.enabled,
339
339
  headlessOutput,
340
340
  });
341
+ const llmFactory = createSubAgentLlmFactory(resolvedConfig.llm);
341
342
  let structuredOutputState = { ok: true, candidate: null };
342
343
  const reporter = createRunReporter({
343
344
  useGui,
@@ -435,6 +436,7 @@ export async function handleRunCommand(options, command) {
435
436
  ? { allow: allowedToolRules, deny: disallowedToolRules }
436
437
  : undefined,
437
438
  permissionMode,
439
+ llmFactory,
438
440
  });
439
441
  const buildAssistantMessage = (result) => buildRunAssistantMessage({ mode, result });
440
442
  const result = await executeRunLoop({
@@ -504,8 +506,9 @@ export async function handleRunCommand(options, command) {
504
506
  rawOutputProfile !== 'openai' &&
505
507
  activeReporterStarted &&
506
508
  activeReporter) {
507
- const error = new Error(text.cli.unexpectedError(msg));
508
- error.auditPath = lastKnownAuditPath;
509
+ const error = Object.assign(new Error(text.cli.unexpectedError(msg)), {
510
+ auditPath: lastKnownAuditPath,
511
+ });
509
512
  activeReporter.onError(error);
510
513
  }
511
514
  else if (outputFormat === 'stream-json') {
@@ -35,6 +35,7 @@ export function buildRunLoopParams(params) {
35
35
  permissionMode: params.permissionMode,
36
36
  extensions: params.extensions,
37
37
  permissionRules: params.permissionRules,
38
+ llmFactory: params.llmFactory,
38
39
  eventPayload: params.headlessIncludeToolInput ||
39
40
  params.headlessIncludeToolOutput ||
40
41
  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
  }
@@ -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) {
@@ -124,9 +124,8 @@ export class StandardReporter {
124
124
  getLogger().error(text.cli.unexpectedError(error.message), true);
125
125
  }
126
126
  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') {
127
+ // DCAP Defense: Check if stderr supports TTY operations to prevent clearLine crashes and unexpected termination
128
+ if (!('clearLine' in process.stderr) || !('cursorTo' in process.stderr)) {
130
129
  this.bar = {
131
130
  render: () => { },
132
131
  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),
@@ -24,39 +24,48 @@ export class AstParser {
24
24
  static state = InitState.Idle;
25
25
  static initPromise = null;
26
26
  /**
27
- * Get the Parser class from web-tree-sitter, handling different API versions
27
+ * Get the Parser class from web-tree-sitter, handling different API versions.
28
+ * Uses `as unknown as` for CJS/ESM interop — the runtime module shape varies
29
+ * across bundler configurations.
28
30
  */
29
31
  static getParserClass() {
32
+ const mod = TreeSitter;
30
33
  try {
31
- return TreeSitter.Parser || TreeSitter.default?.Parser || TreeSitter;
34
+ return (mod.Parser ??
35
+ mod.default?.Parser ??
36
+ TreeSitter);
32
37
  }
33
38
  catch (_error) {
34
39
  getLogger().degrade(text.ast.degradedApi);
35
- return TreeSitter.default || TreeSitter;
40
+ return (mod.default ?? TreeSitter);
36
41
  }
37
42
  }
38
43
  /**
39
- * Get the Language class from web-tree-sitter, handling different API versions
44
+ * Get the Language class from web-tree-sitter, handling different API versions.
45
+ * Uses `as unknown as` for CJS/ESM interop.
40
46
  */
41
47
  static getLanguageClass() {
48
+ const mod = TreeSitter;
42
49
  try {
43
- return TreeSitter.Language || TreeSitter.default?.Language;
50
+ return (mod.Language ?? mod.default?.Language);
44
51
  }
45
52
  catch (_error) {
46
53
  getLogger().degrade(text.ast.degradedApi);
47
- return TreeSitter.default?.Language;
54
+ return mod.default?.Language;
48
55
  }
49
56
  }
50
57
  /**
51
- * Get the Query class from web-tree-sitter, handling different API versions
58
+ * Get the Query class from web-tree-sitter, handling different API versions.
59
+ * Uses `as unknown as` for CJS/ESM interop.
52
60
  */
53
61
  static getQueryClass() {
62
+ const mod = TreeSitter;
54
63
  try {
55
- return TreeSitter.Query || TreeSitter.default?.Query;
64
+ return (mod.Query ?? mod.default?.Query);
56
65
  }
57
66
  catch (_error) {
58
67
  getLogger().degrade(text.ast.degradedApi);
59
- return TreeSitter.default?.Query;
68
+ return mod.default?.Query;
60
69
  }
61
70
  }
62
71
  static languages = new Map();