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
@@ -93,19 +93,16 @@ class DefaultPermissionGate {
93
93
  };
94
94
  }
95
95
  mapAuthorizationDecision(decision) {
96
+ const source = decision.source === 'auto' || decision.source === 'allowlist' ? 'policy' : decision.source;
96
97
  if (decision.outcome === 'deny') {
97
- return {
98
- kind: 'deny',
99
- reason: decision.reason ?? 'denied',
100
- source: decision.source === 'auto' || decision.source === 'allowlist'
101
- ? 'policy'
102
- : decision.source,
103
- };
98
+ return { kind: 'deny', reason: decision.reason ?? 'denied', source };
104
99
  }
105
100
  return {
106
101
  kind: 'allow',
107
102
  reason: decision.reason,
108
- source: decision.source === 'auto' || decision.source === 'allowlist' ? 'policy' : decision.source,
103
+ source,
104
+ ttlMs: decision.ttlMs,
105
+ persist: decision.persist,
109
106
  };
110
107
  }
111
108
  }
@@ -1,6 +1,7 @@
1
1
  import { randomBytes, createHash } from 'crypto';
2
2
  import path from 'path';
3
3
  import { mkdir, readFile, rename, stat, writeFile } from '../adapters/fs/node-fs.js';
4
+ import { getLogger } from '../observability/logger.js';
4
5
  const SESSION_ID_RE = /^[a-zA-Z0-9_-]{6,64}$/;
5
6
  export function assertValidSessionId(sessionId) {
6
7
  if (!SESSION_ID_RE.test(sessionId)) {
@@ -31,8 +32,8 @@ async function resolveGitDir(repoRoot) {
31
32
  if (st.isDirectory())
32
33
  return dotGit;
33
34
  }
34
- catch {
35
- // ignore
35
+ catch (error) {
36
+ getLogger().debug(`[PlanStorage] .git stat failed: ${error instanceof Error ? error.message : String(error)}`);
36
37
  }
37
38
  try {
38
39
  const raw = await readFile(dotGit, 'utf-8');
@@ -43,7 +44,8 @@ async function resolveGitDir(repoRoot) {
43
44
  const gitdir = m[1].trim();
44
45
  return path.isAbsolute(gitdir) ? gitdir : path.resolve(repoRoot, gitdir);
45
46
  }
46
- catch {
47
+ catch (error) {
48
+ getLogger().debug(`[PlanStorage] .git read failed: ${error instanceof Error ? error.message : String(error)}`);
47
49
  return null;
48
50
  }
49
51
  }
@@ -61,7 +63,8 @@ export async function ensureSalmonloopIgnored(repoRoot) {
61
63
  try {
62
64
  existing = await readFile(excludePath, 'utf-8');
63
65
  }
64
- catch {
66
+ catch (error) {
67
+ getLogger().debug(`[PlanStorage] Failed to read git exclude file: ${error instanceof Error ? error.message : String(error)}`);
65
68
  existing = '';
66
69
  }
67
70
  if (existing.split('\n').some((l) => l.trim() === '.salmonloop/'))
@@ -1,7 +1,9 @@
1
1
  import { join } from 'path';
2
+ import { pythonPlugin } from '../../languages/python/index.js';
2
3
  import { typescriptPlugin, tsxPlugin, javascriptPlugin } from '../../languages/typescript/index.js';
3
4
  import { readdir } from '../adapters/fs/node-fs.js';
4
5
  import { getLogger } from '../observability/logger.js';
6
+ import { errorMessage } from '../utils/error.js';
5
7
  import { validateQueryPack } from './validator.js';
6
8
  // Import built-in plugins (Phase 1: explicit import)
7
9
  export class PluginLoader {
@@ -18,10 +20,11 @@ export class PluginLoader {
18
20
  try {
19
21
  // Phase 1: Manually register TypeScript/JavaScript plugins
20
22
  getLogger().debug('Loading built-in plugins...');
23
+ this.registerWithValidation(registry, pythonPlugin);
21
24
  this.registerWithValidation(registry, typescriptPlugin);
22
25
  this.registerWithValidation(registry, tsxPlugin);
23
26
  this.registerWithValidation(registry, javascriptPlugin);
24
- getLogger().debug(`Plugins loaded: ${typescriptPlugin.meta.name}, ${tsxPlugin.meta.name}, ${javascriptPlugin.meta.name}`);
27
+ getLogger().debug(`Plugins loaded: ${pythonPlugin.meta.name}, ${typescriptPlugin.meta.name}, ${tsxPlugin.meta.name}, ${javascriptPlugin.meta.name}`);
25
28
  // Phase 2: Load user plugins from .salmonloop/languages/
26
29
  if (repoPath) {
27
30
  await this.loadUserPlugins(registry, repoPath);
@@ -31,11 +34,11 @@ export class PluginLoader {
31
34
  catch (error) {
32
35
  // In test environment, we want to know why it failed
33
36
  if (process.env.NODE_ENV === 'test') {
34
- const errorMsg = error instanceof Error ? error.message : String(error);
37
+ const errorMsg = errorMessage(error);
35
38
  getLogger().error(`CRITICAL: Failed to load plugins: ${errorMsg}`);
36
39
  throw error;
37
40
  }
38
- getLogger().error(`Failed to load plugins: ${error instanceof Error ? error.message : String(error)}`);
41
+ getLogger().error(`Failed to load plugins: ${errorMessage(error)}`);
39
42
  }
40
43
  }
41
44
  /**
@@ -70,7 +73,7 @@ export class PluginLoader {
70
73
  }
71
74
  catch (err) {
72
75
  if (err && typeof err === 'object' && 'code' in err && err.code !== 'ENOENT') {
73
- getLogger().warn(`Failed to load user plugin from ${dirName}: ${err instanceof Error ? (err instanceof Error ? err.message : String(err)) : String(err)}`);
76
+ getLogger().warn(`Failed to load user plugin from ${dirName}: ${errorMessage(err)}`);
74
77
  }
75
78
  }
76
79
  }
@@ -78,7 +81,7 @@ export class PluginLoader {
78
81
  catch (err) {
79
82
  // Ignore if directory doesn't exist
80
83
  if (err && typeof err === 'object' && 'code' in err && err.code !== 'ENOENT') {
81
- getLogger().debug(`Error scanning for user plugins: ${err instanceof Error ? err.message : String(err)}`);
84
+ getLogger().debug(`Error scanning for user plugins: ${errorMessage(err)}`);
82
85
  }
83
86
  }
84
87
  }
@@ -2,6 +2,7 @@ import { fileURLToPath } from 'node:url';
2
2
  import Handlebars from 'handlebars';
3
3
  import { z } from 'zod';
4
4
  import { readFile } from '../adapters/fs/node-fs.js';
5
+ import { unwrapZodSchema } from '../utils/zod.js';
5
6
  const TEMPLATE_URLS = {
6
7
  'system/_tool_defs.hbs': new URL('./templates/system/_tool_defs.hbs', import.meta.url),
7
8
  'system/main_system.hbs': new URL('./templates/system/main_system.hbs', import.meta.url),
@@ -60,8 +61,8 @@ export class PromptRegistry {
60
61
  if (!url) {
61
62
  throw new Error(`Unknown prompt template path: ${relativePath}`);
62
63
  }
63
- const bunAny = globalThis;
64
- const bun = bunAny.Bun;
64
+ const bunGlobal = globalThis;
65
+ const bun = bunGlobal.Bun;
65
66
  if (bun?.file) {
66
67
  return bun.file(url).text();
67
68
  }
@@ -113,34 +114,15 @@ export class PromptRegistry {
113
114
  if (!zodSchema) {
114
115
  return { type: 'object', description: 'Schema details unavailable' };
115
116
  }
116
- const unwrapForJsonSchema = (schema) => {
117
- let current = schema;
118
- for (let depth = 0; depth < 20; depth++) {
119
- const ZodEffects = z.ZodEffects;
120
- if (ZodEffects && current instanceof ZodEffects) {
121
- current = current._def.schema;
122
- continue;
123
- }
124
- if (current instanceof z.ZodPipe) {
125
- current = current._def.out;
126
- continue;
127
- }
128
- if (current instanceof z.ZodOptional ||
129
- current instanceof z.ZodNullable ||
130
- current instanceof z.ZodDefault) {
131
- current = current._def.innerType;
132
- continue;
133
- }
134
- break;
135
- }
136
- return current;
137
- };
138
117
  try {
139
- const unwrapped = unwrapForJsonSchema(zodSchema);
140
- const schema = z.toJSONSchema(unwrapped);
141
- if (schema && typeof schema === 'object') {
142
- const { $schema: _ignored, ...rest } = schema;
143
- return rest;
118
+ const unwrapped = unwrapZodSchema(zodSchema);
119
+ const zWithJson = z;
120
+ if (zWithJson.toJSONSchema) {
121
+ const schema = zWithJson.toJSONSchema(unwrapped);
122
+ if (schema && typeof schema === 'object') {
123
+ const { $schema: _ignored, ...rest } = schema;
124
+ return rest;
125
+ }
144
126
  }
145
127
  }
146
128
  catch (_e) {
@@ -174,7 +156,7 @@ export class PromptRegistry {
174
156
  return this.render('explore_system', { tools: this.getToolsForTemplate() });
175
157
  }
176
158
  renderAutopilotSystem() {
177
- return this.render('autopilot_system', {});
159
+ return this.render('autopilot_system', { tools: this.getToolsForTemplate() });
178
160
  }
179
161
  renderAnswerSystem() {
180
162
  return this.render('answer_system', {});
@@ -1,3 +1,4 @@
1
+ import { getLogger } from '../observability/logger.js';
1
2
  import { resolvePhaseVisibleTools } from '../tools/tool-visibility.js';
2
3
  import { Phase } from '../types/runtime.js';
3
4
  import { getPromptRegistry } from './registry.js';
@@ -16,7 +17,8 @@ function extractTargetFiles(plan) {
16
17
  const files = parsed.files.filter((f) => typeof f === 'string');
17
18
  return files.length > 0 ? files.join(', ') : undefined;
18
19
  }
19
- catch {
20
+ catch (error) {
21
+ getLogger().debug(`[PromptRuntime] Failed to extract target files from plan JSON: ${error instanceof Error ? error.message : String(error)}`);
20
22
  return undefined;
21
23
  }
22
24
  }
@@ -1,11 +1,35 @@
1
1
  You are a coding assistant running in "autopilot" mode.
2
- Drive the task forward autonomously and answer in the same language as the user.
3
- Use the repository context available in the current turn when present.
4
- If no repository action is possible yet, explain the next best action succinctly.
5
- Prefer taking a reasonable repository action over asking the user for confirmation.
2
+ Your job is to complete the given task by using the available tools to inspect and modify the repository.
3
+ You MUST use tools to accomplish the task do not just describe what needs to be done, actually do it.
4
+
5
+ ## Problem-Solving Strategy
6
+
7
+ 1. **Understand**: Read the problem statement carefully. Identify what is failing and what the expected behavior should be.
8
+ 2. **Locate**: Find the source file to fix AND all test files that will be affected. Use `code.search` to search for the rule name, function name, or error message across the entire codebase. Pay special attention to CLI integration tests, snapshot tests, and end-to-end tests that assert on the behavior you are changing. Read ALL affected test files before writing any code.
9
+ 3. **Diagnose**: Understand the root cause before writing any code. Read surrounding context, not just the error site.
10
+ 4. **Fix**: Write the minimal change that fixes the issue. Update ALL affected test files to match the new behavior. Do not refactor unrelated code.
11
+ 5. **Verify**: Run the FULL test suite with `shell.exec` (e.g. `pytest`). If tests fail, analyze the output and iterate. Do NOT run only targeted tests — behavioral changes cascade to other tests.
12
+
13
+ ## Efficiency Rules
14
+ - Do NOT reproduce the bug. The problem statement already describes it. Go straight from understanding to fixing.
15
+ - Do NOT run multiple exploratory shell commands. Read the source code, understand the logic, write the fix.
16
+ - If you understand the root cause after reading the source, write the fix immediately. Do not verify the bug first.
17
+ - IMPORTANT: The Locate step is not optional. Spending time finding all affected tests upfront saves rounds later.
18
+
19
+ ## Rules
20
+ - After writing code, you MUST run tests with `shell.exec` to verify your changes. This is not optional — a fix without verification is incomplete.
21
+ - If tests fail, read the failure output carefully, diagnose the issue, and fix it. Do not stop until tests pass. A failing test means your fix is incomplete — find all affected tests and update them.
22
+ - Read test files before modifying code — tests define the expected behavior.
23
+ - Do not modify test files unless the test itself is incorrect. If your fix changes behavior correctly, update the test's expected output to match.
24
+ - Before writing any code, use `code.search` to find ALL tests that reference the changed behavior. Search for: the rule name (e.g. "L031"), the function name, error messages, and expected test output. CLI integration tests and snapshot tests often contain hardcoded expected output that must be updated when behavior changes.
25
+ - Make the smallest possible change. Avoid unrelated refactoring or style changes.
26
+ - Drive the task forward autonomously and answer in the same language as the user.
27
+
6
28
  Treat simple repo-relative paths like "smoke.txt", "README.md", or "src/index.ts" as valid paths from the repository root.
7
29
  Do not ask the user to validate a path that is already a valid relative path or can be inferred directly from the instruction.
8
30
  Use `interaction.ask_user` only when the task is genuinely ambiguous and you cannot safely infer the next action.
9
31
  When calling `interaction.ask_user`, the arguments MUST be valid JSON with exactly this shape: {"questions":[{"header":"Short header","question":"One clear question","options":[{"label":"Option 1","description":"Why pick it"},{"label":"Option 2","description":"Why pick it"}],"multiSelect":false}]}.
10
32
  Do not call `interaction.ask_user` with free-form strings, a single question field, string-only options, or empty options arrays.
11
33
  For simple file creation or edits, call `fs.write_file` directly with {"file":"relative/path.ext","content":"..."} instead of asking the user for the path again.
34
+
35
+ {{> tool_defs}}
@@ -1,4 +1,5 @@
1
1
  import { InMemoryTaskStore } from '@a2a-js/sdk/server';
2
+ import { getLogger } from '../../../observability/logger.js';
2
3
  import { buildCanonicalExecutionRequest, buildInstructionFromParts, } from '../../shared/execution-request.js';
3
4
  import { parseA2ASkillFlowMode } from '../../shared/flow-mode-mapping.js';
4
5
  export function createA2AInteractionExecutor(deps) {
@@ -346,7 +347,8 @@ export function createA2AInteractionExecutor(deps) {
346
347
  data: JSON.parse(artifact.content),
347
348
  };
348
349
  }
349
- catch {
350
+ catch (error) {
351
+ getLogger().debug(`[A2AExecutor] Failed to parse artifact content as JSON: ${error instanceof Error ? error.message : String(error)}`);
350
352
  // Fall through to text
351
353
  }
352
354
  }
@@ -406,9 +406,9 @@ const normalizeA2AExtensionHeadersByVersion = (req, res, next) => {
406
406
  });
407
407
  next();
408
408
  };
409
- function isObjectRecord(value) {
410
- return typeof value === 'object' && value !== null && !Array.isArray(value);
411
- }
409
+ import { getLogger } from '../../../observability/logger.js';
410
+ import { isRecord } from '../../../utils/serialize.js';
411
+ const isObjectRecord = isRecord;
412
412
  function looksLikeTask(result) {
413
413
  return typeof result.id === 'string' && 'status' in result;
414
414
  }
@@ -826,7 +826,8 @@ function normalizeSseJsonRpcChunkByMethod(chunk, method) {
826
826
  const payload = JSON.parse(line.slice('data: '.length));
827
827
  return `data: ${JSON.stringify(normalizeJsonRpcPayloadByMethod(method, payload))}`;
828
828
  }
829
- catch {
829
+ catch (error) {
830
+ getLogger().debug(`[A2AServer] Failed to parse SSE JSON-RPC chunk: ${error instanceof Error ? error.message : String(error)}`);
830
831
  return line;
831
832
  }
832
833
  })
@@ -1,3 +1,4 @@
1
+ import { getLogger } from '../../observability/logger.js';
1
2
  function sleep(ms) {
2
3
  if (ms <= 0)
3
4
  return Promise.resolve();
@@ -72,8 +73,8 @@ async function safeRelease(terminal) {
72
73
  try {
73
74
  await terminal.release();
74
75
  }
75
- catch {
76
- // Ignore release errors.
76
+ catch (error) {
77
+ getLogger().debug(`[AcpCommandRunner] Failed to release terminal: ${error instanceof Error ? error.message : String(error)}`);
77
78
  }
78
79
  }
79
80
  export function createAcpCommandRunner(params) {
@@ -134,8 +135,8 @@ export function createAcpCommandRunner(params) {
134
135
  try {
135
136
  await terminal.kill();
136
137
  }
137
- catch {
138
- // Ignore kill errors.
138
+ catch (error) {
139
+ getLogger().debug(`[AcpCommandRunner] Failed to kill terminal on abort: ${error instanceof Error ? error.message : String(error)}`);
139
140
  }
140
141
  break;
141
142
  }
@@ -144,8 +145,8 @@ export function createAcpCommandRunner(params) {
144
145
  try {
145
146
  await terminal.kill();
146
147
  }
147
- catch {
148
- // Ignore kill errors.
148
+ catch (error) {
149
+ getLogger().debug(`[AcpCommandRunner] Failed to kill terminal on timeout: ${error instanceof Error ? error.message : String(error)}`);
149
150
  }
150
151
  break;
151
152
  }
@@ -1,6 +1,7 @@
1
1
  import { mkdir, open, readFile, rename, stat, unlink, writeFile, } from '../../adapters/fs/node-fs.js';
2
2
  import { defaultPathAdapter } from '../../adapters/path/path-adapter.js';
3
3
  import { recordAuditEvent } from '../../observability/audit-trail.js';
4
+ import { getLogger } from '../../observability/logger.js';
4
5
  import { hashRepoPath, isPermissionPolicyValue, parseTimestamp, } from './acp-types.js';
5
6
  export function createAcpSessionPersistence(options) {
6
7
  const deletedSessionIds = new Map();
@@ -219,7 +220,8 @@ export function createAcpSessionPersistence(options) {
219
220
  phase: 'PREFLIGHT',
220
221
  });
221
222
  }
222
- catch {
223
+ catch (error) {
224
+ getLogger().debug(`[AcpSessionPersistence] Failed to parse stale lock file: ${error instanceof Error ? error.message : String(error)}`);
223
225
  try {
224
226
  const lockStat = await stat(lockPath);
225
227
  const ageMs = Date.now() - lockStat.mtimeMs;
@@ -231,8 +233,8 @@ export function createAcpSessionPersistence(options) {
231
233
  }, { source: 'acp', severity: 'medium', scope: 'session', phase: 'PREFLIGHT' });
232
234
  }
233
235
  }
234
- catch {
235
- // ignore
236
+ catch (innerError) {
237
+ getLogger().debug(`[AcpSessionPersistence] Failed to stat lock file for age check: ${innerError instanceof Error ? innerError.message : String(innerError)}`);
236
238
  }
237
239
  }
238
240
  };
@@ -246,7 +248,8 @@ export function createAcpSessionPersistence(options) {
246
248
  await lockHandle.writeFile(JSON.stringify({ pid: process.pid, createdAtMs: Date.now() }), 'utf8');
247
249
  break;
248
250
  }
249
- catch {
251
+ catch (error) {
252
+ getLogger().debug(`[AcpSessionPersistence] Lock acquire attempt failed: ${error instanceof Error ? error.message : String(error)}`);
250
253
  await tryClearStaleLock();
251
254
  const delayMs = Math.min(250, 20 * (attempt + 1));
252
255
  await new Promise((resolve) => setTimeout(resolve, delayMs));
@@ -271,8 +274,8 @@ export function createAcpSessionPersistence(options) {
271
274
  const existingRaw = await readFile(options.path, 'utf8');
272
275
  existing = normalizePersistedSessionStore(JSON.parse(existingRaw));
273
276
  }
274
- catch {
275
- // ignore read failure; writing fresh payload is acceptable
277
+ catch (error) {
278
+ getLogger().debug(`[AcpSessionPersistence] Failed to read existing session store for merge: ${error instanceof Error ? error.message : String(error)}`);
276
279
  }
277
280
  const merged = new Map();
278
281
  const mergedDeletedSessions = pruneDeletedSessionRecords([
@@ -311,14 +314,14 @@ export function createAcpSessionPersistence(options) {
311
314
  try {
312
315
  await lockHandle.close();
313
316
  }
314
- catch {
315
- // ignore
317
+ catch (error) {
318
+ getLogger().debug(`[AcpSessionPersistence] Failed to close lock handle: ${error instanceof Error ? error.message : String(error)}`);
316
319
  }
317
320
  try {
318
321
  await unlink(lockPath);
319
322
  }
320
- catch {
321
- // ignore
323
+ catch (error) {
324
+ getLogger().debug(`[AcpSessionPersistence] Failed to unlink lock file: ${error instanceof Error ? error.message : String(error)}`);
322
325
  }
323
326
  }
324
327
  }
@@ -3,10 +3,12 @@ import { text } from '../../../locales/index.js';
3
3
  import { defaultPathAdapter } from '../../adapters/path/path-adapter.js';
4
4
  import { inferTurnStopReasonFromFailure } from '../../interaction/turn-stop-reason.js';
5
5
  import { recordAuditEvent } from '../../observability/audit-trail.js';
6
+ import { getLogger } from '../../observability/logger.js';
6
7
  import { toAcpPublicModes } from '../../public-capabilities/projections.js';
7
8
  import { buildPublicCapabilityRegistry } from '../../public-capabilities/registry.js';
8
9
  import { parseSlashInput } from '../../slash/parser.js';
9
10
  import { Phase } from '../../types/runtime.js';
11
+ import { isRecord } from '../../utils/serialize.js';
10
12
  import { buildCanonicalExecutionRequest } from '../shared/execution-request.js';
11
13
  import { parseAcpFlowMode } from '../shared/flow-mode-mapping.js';
12
14
  import { probeCheckpoint, probeCheckpointForNewSession, } from './acp-checkpoint-probe.js';
@@ -18,7 +20,7 @@ import { createAcpSessionStore, isTerminalTaskEvent } from './handlers.js';
18
20
  import { createAcpToolAuthorizationProvider } from './permission-provider.js';
19
21
  import { mapToolKind } from './tool-kind-mapping.js';
20
22
  function formatInputRequiredMessage(inputRequired) {
21
- if (!inputRequired || !Array.isArray(inputRequired.questions))
23
+ if (!inputRequired || !isRecord(inputRequired) || !Array.isArray(inputRequired.questions))
22
24
  return null;
23
25
  const questions = inputRequired.questions;
24
26
  if (questions.length === 0)
@@ -525,6 +527,7 @@ function acpMcpServersToExtensions(mcpServers) {
525
527
  mcpServers: resolvedServers,
526
528
  toolPlugins: [],
527
529
  skillDiscovery: { paths: [], scope: 'repo' },
530
+ agentProfiles: [],
528
531
  };
529
532
  }
530
533
  function validateAcpMcpServers(mcpServers) {
@@ -569,12 +572,13 @@ function isPersistableSession(session) {
569
572
  async function awaitTerminalEvent(params) {
570
573
  if (!params.eventBus)
571
574
  return null;
572
- const history = params.eventBus.list(params.taskId);
575
+ const { eventBus } = params;
576
+ const history = eventBus.list(params.taskId);
573
577
  const terminal = history.find(isTerminalTaskEvent);
574
578
  if (terminal)
575
579
  return terminal;
576
580
  return await new Promise((resolve) => {
577
- const unsubscribe = params.eventBus.subscribe((event) => {
581
+ const unsubscribe = eventBus.subscribe((event) => {
578
582
  if (event.taskId !== params.taskId)
579
583
  return;
580
584
  if (!isTerminalTaskEvent(event))
@@ -638,8 +642,8 @@ export function createAcpFormalAgent(deps) {
638
642
  try {
639
643
  await emitSessionUpdate(sessionId, update);
640
644
  }
641
- catch {
642
- // Best-effort: do not fail the request due to notification delivery issues.
645
+ catch (error) {
646
+ getLogger().debug(`[AcpFormalAgent] Best-effort session info update failed: ${error instanceof Error ? error.message : String(error)}`);
643
647
  }
644
648
  }
645
649
  async function emitRuntimePlanUpdateIfNeeded(params) {
@@ -1092,7 +1096,10 @@ export function createAcpFormalAgent(deps) {
1092
1096
  ...current,
1093
1097
  history: [
1094
1098
  ...current.history,
1095
- { role: 'assistant', content: [buildTextContentBlock(responseText)] },
1099
+ {
1100
+ role: 'assistant',
1101
+ content: [buildTextContentBlock(responseText)],
1102
+ },
1096
1103
  ],
1097
1104
  }));
1098
1105
  await sessionPersistence.persist();
@@ -1,4 +1,5 @@
1
1
  import { text } from '../../../locales/index.js';
2
+ import { getLogger } from '../../observability/logger.js';
2
3
  import { mapToolKind } from './tool-kind-mapping.js';
3
4
  function buildPermissionOptions() {
4
5
  return [
@@ -45,8 +46,8 @@ export function createAcpToolAuthorizationProvider(params) {
45
46
  };
46
47
  await params.conn.sessionUpdate({ sessionId: params.sessionId, update });
47
48
  }
48
- catch {
49
- // Best-effort: do not block authorization if the client can't accept updates.
49
+ catch (error) {
50
+ getLogger().debug(`[AcpPermissionProvider] Best-effort in_progress update failed: ${error instanceof Error ? error.message : String(error)}`);
50
51
  }
51
52
  };
52
53
  return {
@@ -1,6 +1,8 @@
1
1
  import { Readable, Writable } from 'node:stream';
2
2
  import { AgentSideConnection } from '@agentclientprotocol/sdk';
3
3
  import { tryGetLogger } from '../../observability/logger.js';
4
+ import { errorMessage } from '../../utils/error.js';
5
+ import { isRecord } from '../../utils/serialize.js';
4
6
  const INVALID_REQUEST = {
5
7
  jsonrpc: '2.0',
6
8
  id: null,
@@ -11,9 +13,7 @@ const PARSE_ERROR = {
11
13
  id: null,
12
14
  error: { code: -32700, message: 'Parse error' },
13
15
  };
14
- function isJsonObject(value) {
15
- return typeof value === 'object' && value !== null && !Array.isArray(value);
16
- }
16
+ const isJsonObject = isRecord;
17
17
  function hasOwn(value, key) {
18
18
  return Object.prototype.hasOwnProperty.call(value, key);
19
19
  }
@@ -50,9 +50,9 @@ function createNdjsonWriter(output) {
50
50
  .catch(() => undefined)
51
51
  .then(() => writer.write(data))
52
52
  .catch((error) => {
53
- const detail = error instanceof Error ? error.message : String(error);
53
+ const detail = errorMessage(error);
54
54
  safeWarn(`ACP stdio failed to write NDJSON line. reason="${detail}"`);
55
- lastError = error instanceof Error ? new Error(detail) : new Error(detail);
55
+ lastError = new Error(detail);
56
56
  });
57
57
  await tail;
58
58
  },
@@ -83,7 +83,7 @@ async function processStdioLine(line, ndjson, controller) {
83
83
  controller.enqueue(parsed);
84
84
  }
85
85
  catch (error) {
86
- const detail = error instanceof Error ? error.message : String(error);
86
+ const detail = errorMessage(error);
87
87
  safeWarn(`ACP stdio failed to parse JSON line. reason="${detail}"`);
88
88
  await ndjson.write(PARSE_ERROR);
89
89
  }