salmon-loop 0.2.13 → 0.3.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 (224) hide show
  1. package/dist/cli/argv/headless-detection.js +27 -0
  2. package/dist/cli/chat-flow.js +11 -0
  3. package/dist/cli/chat.js +160 -24
  4. package/dist/cli/commands/chat.js +14 -7
  5. package/dist/cli/commands/flow-mode.js +63 -0
  6. package/dist/cli/commands/registry.js +2 -0
  7. package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
  8. package/dist/cli/commands/run/early-errors.js +23 -0
  9. package/dist/cli/commands/run/handler.js +115 -27
  10. package/dist/cli/commands/run/headless-error-writer.js +8 -0
  11. package/dist/cli/commands/run/loop-params.js +2 -0
  12. package/dist/cli/commands/run/mode.js +2 -5
  13. package/dist/cli/commands/run/parse-options.js +16 -0
  14. package/dist/cli/commands/run/persist-session.js +10 -1
  15. package/dist/cli/commands/run/preflight.js +10 -0
  16. package/dist/cli/commands/run/reporter-factory.js +4 -0
  17. package/dist/cli/commands/run/runtime-llm.js +38 -11
  18. package/dist/cli/commands/run/runtime-options.js +2 -2
  19. package/dist/cli/commands/serve.js +97 -77
  20. package/dist/cli/commands/tool-names.js +78 -78
  21. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
  22. package/dist/cli/headless/json-protocol.js +37 -0
  23. package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
  24. package/dist/cli/headless/protocol-metadata.js +22 -0
  25. package/dist/cli/headless/stream-json-protocol.js +34 -1
  26. package/dist/cli/index.js +6 -4
  27. package/dist/cli/locales/en.js +30 -6
  28. package/dist/cli/program-bootstrap.js +10 -5
  29. package/dist/cli/program-commands.js +5 -1
  30. package/dist/cli/reporters/anthropic-stream.js +7 -1
  31. package/dist/cli/reporters/json.js +4 -0
  32. package/dist/cli/reporters/stream-json.js +17 -2
  33. package/dist/cli/run-cli.js +5 -3
  34. package/dist/cli/slash/runtime.js +27 -12
  35. package/dist/cli/ui/components/CommandInput.js +7 -3
  36. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  37. package/dist/cli/utils/command-option-source.js +13 -0
  38. package/dist/cli/utils/verify-resolver.js +8 -4
  39. package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
  40. package/dist/core/adapters/fs/file-adapter.js +6 -0
  41. package/dist/core/adapters/fs/filesystem.js +2 -1
  42. package/dist/core/adapters/git/git-adapter.js +78 -1
  43. package/dist/core/backends/salmon-loop/task-executor.js +1 -0
  44. package/dist/core/benchmark/patch-artifact.js +124 -0
  45. package/dist/core/benchmark/swe-bench.js +25 -0
  46. package/dist/core/config/load.js +18 -11
  47. package/dist/core/config/resolve-llm.js +12 -0
  48. package/dist/core/config/resolvers/server.js +0 -6
  49. package/dist/core/config/validate.js +73 -21
  50. package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
  51. package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
  52. package/dist/core/context/keywords.js +18 -4
  53. package/dist/core/context/service-deps.js +2 -2
  54. package/dist/core/context/service.js +8 -0
  55. package/dist/core/context/steps/context-gather.js +38 -0
  56. package/dist/core/context/summarization/summarizer.js +55 -12
  57. package/dist/core/context/targeting/target-resolver.js +4 -4
  58. package/dist/core/extensions/index.js +23 -5
  59. package/dist/core/extensions/merge.js +14 -0
  60. package/dist/core/extensions/paths.js +31 -0
  61. package/dist/core/extensions/schemas.js +8 -5
  62. package/dist/core/facades/cli-chat.js +6 -2
  63. package/dist/core/facades/cli-command-chat.js +1 -0
  64. package/dist/core/facades/cli-command-tool-names.js +2 -0
  65. package/dist/core/facades/cli-observability.js +1 -1
  66. package/dist/core/facades/cli-program-bootstrap.js +1 -0
  67. package/dist/core/facades/cli-run-handler.js +4 -2
  68. package/dist/core/facades/cli-run-persist-session.js +1 -0
  69. package/dist/core/facades/cli-serve.js +4 -4
  70. package/dist/core/facades/cli-utils-worktree.js +1 -1
  71. package/dist/core/failure/diagnostics.js +53 -1
  72. package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
  73. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
  74. package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
  75. package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
  76. package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
  77. package/dist/core/grizzco/engine/transaction/transaction-runner.js +165 -7
  78. package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
  79. package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
  80. package/dist/core/grizzco/steps/answer.js +13 -14
  81. package/dist/core/grizzco/steps/autopilot.js +396 -0
  82. package/dist/core/grizzco/steps/cache-sharing.js +29 -0
  83. package/dist/core/grizzco/steps/explore.js +37 -21
  84. package/dist/core/grizzco/steps/generateReview.js +2 -5
  85. package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
  86. package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
  87. package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
  88. package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
  89. package/dist/core/grizzco/steps/patch.js +105 -146
  90. package/dist/core/grizzco/steps/plan.js +101 -25
  91. package/dist/core/grizzco/steps/preflight.js +5 -6
  92. package/dist/core/grizzco/steps/request-assembly.js +78 -0
  93. package/dist/core/grizzco/steps/research.js +39 -36
  94. package/dist/core/grizzco/steps/tool-runtime.js +47 -0
  95. package/dist/core/grizzco/steps/verify-shared.js +23 -0
  96. package/dist/core/grizzco/steps/verify.js +13 -21
  97. package/dist/core/interaction/orchestration/facade.js +1 -1
  98. package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
  99. package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
  100. package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
  101. package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
  102. package/dist/core/llm/ai-sdk/request-params.js +113 -1
  103. package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
  104. package/dist/core/llm/ai-sdk.js +112 -27
  105. package/dist/core/llm/capabilities.js +12 -0
  106. package/dist/core/llm/contracts/repair.js +36 -30
  107. package/dist/core/llm/errors.js +83 -2
  108. package/dist/core/llm/message-composition.js +7 -22
  109. package/dist/core/llm/phase-router.js +29 -10
  110. package/dist/core/llm/redact.js +28 -3
  111. package/dist/core/llm/registry.js +2 -0
  112. package/dist/core/llm/request-augmentation.js +55 -0
  113. package/dist/core/llm/request-envelope.js +334 -0
  114. package/dist/core/llm/shared-request-assembly.js +35 -0
  115. package/dist/core/llm/stream-utils.js +13 -4
  116. package/dist/core/llm/utils.js +18 -29
  117. package/dist/core/memory/relevant-retrieval.js +144 -0
  118. package/dist/core/observability/logger.js +11 -2
  119. package/dist/core/patch/diff.js +1 -0
  120. package/dist/core/prompts/registry.js +39 -2
  121. package/dist/core/prompts/runtime.js +50 -12
  122. package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
  123. package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
  124. package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
  125. package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
  126. package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
  127. package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
  128. package/dist/core/prompts/templates/system/main_system.hbs +4 -16
  129. package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
  130. package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
  131. package/dist/core/prompts/templates/system/research_system.hbs +2 -0
  132. package/dist/core/protocols/a2a/agent-card.js +5 -3
  133. package/dist/core/protocols/a2a/sdk/executor.js +2 -1
  134. package/dist/core/protocols/a2a/sdk/server.js +0 -1
  135. package/dist/core/protocols/acp/formal-agent.js +300 -58
  136. package/dist/core/protocols/acp/handlers.js +5 -1
  137. package/dist/core/protocols/acp/permission-provider.js +1 -1
  138. package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
  139. package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
  140. package/dist/core/public-capabilities/projections.js +29 -0
  141. package/dist/core/public-capabilities/registry.js +26 -0
  142. package/dist/core/public-capabilities/types.js +2 -0
  143. package/dist/core/runtime/agent-server-runtime.js +47 -43
  144. package/dist/core/runtime/execution-profile.js +67 -0
  145. package/dist/core/session/artifact-state.js +160 -0
  146. package/dist/core/session/compaction/index.js +183 -0
  147. package/dist/core/session/compaction/microcompact.js +78 -0
  148. package/dist/core/session/compaction/tracking.js +48 -0
  149. package/dist/core/session/compaction/types.js +11 -0
  150. package/dist/core/session/compression.js +8 -0
  151. package/dist/core/session/manager.js +244 -8
  152. package/dist/core/session/pruning-strategy.js +55 -9
  153. package/dist/core/session/replacement-preview-provider.js +24 -0
  154. package/dist/core/session/replacement-state.js +131 -0
  155. package/dist/core/session/resume-repair/pipeline.js +79 -0
  156. package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
  157. package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
  158. package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
  159. package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
  160. package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
  161. package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
  162. package/dist/core/session/resume-repair/types.js +2 -0
  163. package/dist/core/session/summary-sync.js +164 -13
  164. package/dist/core/session/token-tracker.js +6 -0
  165. package/dist/core/skills/audit.js +34 -0
  166. package/dist/core/skills/bridge.js +84 -7
  167. package/dist/core/skills/discovery.js +94 -0
  168. package/dist/core/skills/feature-flags.js +52 -0
  169. package/dist/core/skills/index.js +1 -1
  170. package/dist/core/skills/loader.js +195 -20
  171. package/dist/core/skills/parser.js +296 -24
  172. package/dist/core/skills/permissions.js +117 -0
  173. package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
  174. package/dist/core/skills/runtime/SkillRunner.js +240 -61
  175. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
  176. package/dist/core/strata/layers/worktree.js +67 -10
  177. package/dist/core/strata/runtime/synchronizer.js +29 -2
  178. package/dist/core/streaming/stream-assembler.js +75 -31
  179. package/dist/core/sub-agent/context-snapshot.js +156 -0
  180. package/dist/core/sub-agent/core/loop.js +1 -1
  181. package/dist/core/sub-agent/core/manager.js +119 -20
  182. package/dist/core/sub-agent/dispatch-policy.js +29 -0
  183. package/dist/core/sub-agent/prefix-consistency.js +48 -0
  184. package/dist/core/sub-agent/registry-defaults.js +4 -0
  185. package/dist/core/sub-agent/tools/task-spawn.js +79 -2
  186. package/dist/core/sub-agent/types.js +134 -5
  187. package/dist/core/tools/audit.js +13 -4
  188. package/dist/core/tools/builtin/ast-grep.js +1 -1
  189. package/dist/core/tools/builtin/ast.js +1 -1
  190. package/dist/core/tools/builtin/benchmark.js +360 -0
  191. package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
  192. package/dist/core/tools/builtin/code-search/executor.js +6 -1
  193. package/dist/core/tools/builtin/code-search/spec.js +26 -2
  194. package/dist/core/tools/builtin/fs.js +256 -23
  195. package/dist/core/tools/builtin/git.js +2 -2
  196. package/dist/core/tools/builtin/index.js +51 -2
  197. package/dist/core/tools/builtin/interaction.js +8 -1
  198. package/dist/core/tools/builtin/plan.js +37 -15
  199. package/dist/core/tools/builtin/shell.js +1 -1
  200. package/dist/core/tools/loader.js +39 -16
  201. package/dist/core/tools/mapper.js +17 -3
  202. package/dist/core/tools/mcp/client.js +2 -1
  203. package/dist/core/tools/parallel/scheduler.js +35 -4
  204. package/dist/core/tools/permissions/permission-rules.js +5 -10
  205. package/dist/core/tools/policy.js +6 -1
  206. package/dist/core/tools/recoverable-tool-errors.js +10 -0
  207. package/dist/core/tools/router.js +24 -6
  208. package/dist/core/tools/session.js +458 -48
  209. package/dist/core/tools/tool-visibility.js +62 -0
  210. package/dist/core/tools/types.js +9 -1
  211. package/dist/core/types/execution.js +4 -0
  212. package/dist/core/types/flow-mode.js +8 -0
  213. package/dist/core/utils/path.js +52 -0
  214. package/dist/core/verification/runner.js +4 -1
  215. package/dist/core/version.js +17 -0
  216. package/dist/languages/typescript/index.js +4 -1
  217. package/dist/locales/en.js +35 -2
  218. package/dist/utils/eol.js +1 -1
  219. package/package.json +14 -7
  220. package/scripts/fix-es-abstract-compat.js +77 -0
  221. package/dist/core/runtime/fastify-server-bundle.js +0 -26
  222. package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
  223. package/dist/core/runtime/sidecar-paths.js +0 -47
  224. package/dist/core/runtime/sidecar-route-catalog.js +0 -103
@@ -10,32 +10,22 @@ export function formatContextForPrompt(context, options = {}) {
10
10
  }
11
11
  return formatContextForXmlPrompt(context);
12
12
  }
13
- export function extractJson(content) {
14
- // 1. Try to find JSON block
15
- const jsonBlockMatch = content.match(/```json\s*\n([\s\S]*?)\n```/);
16
- if (jsonBlockMatch) {
17
- try {
18
- return JSON.parse(jsonBlockMatch[1]);
19
- }
20
- catch (__e) {
21
- // Fallback to raw content if block is invalid
22
- }
13
+ export function parsePlanFromLLMContent(content) {
14
+ const trimmed = String(content ?? '').trim();
15
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
16
+ throw new Error(text.llm.planInvalidJson);
23
17
  }
24
- // 2. Try to find anything that looks like a JSON object
25
- const jsonMatch = content.match(/\{[\s\S]*\}/);
26
- if (jsonMatch) {
27
- try {
28
- return JSON.parse(jsonMatch[0]);
29
- }
30
- catch (__e) {
31
- // Fallback
32
- }
18
+ let parsed;
19
+ try {
20
+ parsed = JSON.parse(trimmed);
33
21
  }
34
- // 3. Final fallback: try parsing the whole content
35
- return JSON.parse(content);
36
- }
37
- export function parsePlanFromLLMContent(content) {
38
- const plan = extractJson(content);
22
+ catch {
23
+ throw new Error(text.llm.planInvalidJson);
24
+ }
25
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
26
+ throw new Error(text.llm.planInvalidJson);
27
+ }
28
+ const plan = parsed;
39
29
  if (!plan.goal || !Array.isArray(plan.files) || !Array.isArray(plan.changes) || !plan.verify) {
40
30
  throw new Error(text.llm.planInvalid);
41
31
  }
@@ -46,10 +36,9 @@ export function extractUnifiedDiffFromLLMContent(content) {
46
36
  throw wrapPatchEmpty();
47
37
  }
48
38
  const looksLikeUnifiedDiff = (text) => {
49
- return /^\s*(diff --git |--- a\/)/m.test(text);
39
+ return /^\s*diff --git /m.test(text);
50
40
  };
51
- // 1) Prefer fenced code blocks and always pick the LAST diff-like block (LLM may generate multiple attempts).
52
- // Accept both git-style (`diff --git`) and minimal unified diffs (`--- a/...` + `+++ b/...`).
41
+ // 1) Prefer fenced code blocks and always pick the LAST canonical diff block (LLM may generate multiple attempts).
53
42
  const fencedBlocks = [];
54
43
  const fenceRegex = /```(?:diff)?\s*\n([\s\S]*?)\n```/gi;
55
44
  let fenceMatch = null;
@@ -62,10 +51,10 @@ export function extractUnifiedDiffFromLLMContent(content) {
62
51
  if (fencedBlocks.length > 0) {
63
52
  return fencedBlocks[fencedBlocks.length - 1].trim();
64
53
  }
65
- // 2) Raw diff without markdown: keep the first diff-like section.
54
+ // 2) Raw diff without markdown: keep the first canonical diff section.
66
55
  // In "pure diff" mode, LLMs typically return only the patch, so selecting the first marker
67
56
  // avoids accidentally dropping the leading `diff --git` header.
68
- const rawStart = content.search(/^\s*(diff --git |--- a\/)/m);
57
+ const rawStart = content.search(/^\s*diff --git /m);
69
58
  if (rawStart !== -1)
70
59
  return content.slice(rawStart).trim();
71
60
  // Final fallback: original simple cleanup
@@ -0,0 +1,144 @@
1
+ import { extractKeywords } from '../context/keywords.js';
2
+ const DEFAULT_MAX_ITEMS = 3;
3
+ const DEFAULT_MAX_SUMMARY_CHARS = 220;
4
+ function trimToUndefined(value) {
5
+ if (typeof value !== 'string')
6
+ return undefined;
7
+ const trimmed = value.trim();
8
+ return trimmed ? trimmed : undefined;
9
+ }
10
+ function clampText(value, maxChars) {
11
+ if (value.length <= maxChars)
12
+ return value;
13
+ return `${value.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
14
+ }
15
+ function normalizeCandidate(candidate, maxSummaryChars) {
16
+ const path = trimToUndefined(candidate.path);
17
+ const title = trimToUndefined(candidate.title);
18
+ const summary = trimToUndefined(candidate.summary);
19
+ if (!path || !title || !summary)
20
+ return undefined;
21
+ const tags = Array.isArray(candidate.tags)
22
+ ? candidate.tags
23
+ .map((tag) => trimToUndefined(tag)?.toLowerCase())
24
+ .filter((tag) => Boolean(tag))
25
+ : undefined;
26
+ return {
27
+ path,
28
+ title,
29
+ summary: clampText(summary, maxSummaryChars),
30
+ tags,
31
+ };
32
+ }
33
+ function buildAlreadySurfacedHaystack(values) {
34
+ return (values ?? []).join('\n').toLowerCase();
35
+ }
36
+ function hasToolConflict(candidate, activeToolNames) {
37
+ const toolTags = (candidate.tags ?? []).filter((tag) => tag.startsWith('tool:'));
38
+ if (toolTags.length === 0)
39
+ return false;
40
+ const active = new Set(activeToolNames.map((name) => name.trim().toLowerCase()).filter(Boolean));
41
+ if (active.size === 0)
42
+ return false;
43
+ return toolTags.some((tag) => active.has(tag.slice('tool:'.length)));
44
+ }
45
+ function computeCandidateScore(candidate, keywords) {
46
+ if (keywords.length === 0)
47
+ return 0;
48
+ const pathLower = candidate.path.toLowerCase();
49
+ const titleLower = candidate.title.toLowerCase();
50
+ const summaryLower = candidate.summary.toLowerCase();
51
+ const tagsLower = candidate.tags ?? [];
52
+ let score = 0;
53
+ for (const keyword of keywords) {
54
+ const normalized = keyword.trim().toLowerCase();
55
+ if (!normalized)
56
+ continue;
57
+ if (pathLower.includes(normalized))
58
+ score += 6;
59
+ if (titleLower.includes(normalized))
60
+ score += 5;
61
+ if (summaryLower.includes(normalized))
62
+ score += 4;
63
+ if (tagsLower.some((tag) => tag.includes(normalized)))
64
+ score += 3;
65
+ }
66
+ return score;
67
+ }
68
+ export function buildRelevantMemoryCandidates(context) {
69
+ const candidates = [];
70
+ const knowledge = context.knowledgeBase;
71
+ const metadata = context.projectMetadata;
72
+ if (Array.isArray(knowledge?.project_rules) && knowledge.project_rules.length > 0) {
73
+ candidates.push({
74
+ path: '.salmonloop/knowledge/project_rules',
75
+ title: 'Project rules',
76
+ summary: knowledge.project_rules.join('; '),
77
+ tags: ['rules', 'project'],
78
+ });
79
+ }
80
+ if (trimToUndefined(knowledge?.user_preferences)) {
81
+ candidates.push({
82
+ path: '.salmonloop/knowledge/user_preferences',
83
+ title: 'User preferences',
84
+ summary: knowledge.user_preferences,
85
+ tags: ['preferences', 'user'],
86
+ });
87
+ }
88
+ for (const [index, decision] of (knowledge?.architectural_decisions ?? []).entries()) {
89
+ const summary = trimToUndefined(decision?.decision);
90
+ if (!summary)
91
+ continue;
92
+ candidates.push({
93
+ path: `.salmonloop/knowledge/architectural_decisions/${index + 1}`,
94
+ title: `Architectural decision ${index + 1}`,
95
+ summary,
96
+ tags: ['architecture', ...(decision.related_files ?? []).map((file) => file.toLowerCase())],
97
+ });
98
+ }
99
+ if (trimToUndefined(metadata?.aiInstructions)) {
100
+ candidates.push({
101
+ path: '.salmonloop/project/ai-instructions',
102
+ title: 'Project AI instructions',
103
+ summary: metadata.aiInstructions,
104
+ tags: ['instructions', 'project'],
105
+ });
106
+ }
107
+ return candidates;
108
+ }
109
+ export function selectRelevantMemory(args) {
110
+ const maxItems = Math.max(1, Math.floor(args.maxItems ?? DEFAULT_MAX_ITEMS));
111
+ const maxSummaryChars = Math.max(48, Math.floor(args.maxSummaryChars ?? DEFAULT_MAX_SUMMARY_CHARS));
112
+ const haystack = buildAlreadySurfacedHaystack(args.alreadySurfacedText);
113
+ const activeToolNames = args.activeToolNames ?? [];
114
+ const keywords = extractKeywords(args.instruction ?? '');
115
+ const deduped = new Map();
116
+ for (const rawCandidate of args.candidates) {
117
+ const candidate = normalizeCandidate(rawCandidate, maxSummaryChars);
118
+ if (!candidate)
119
+ continue;
120
+ if (deduped.has(candidate.path))
121
+ continue;
122
+ if (haystack.includes(candidate.path.toLowerCase()) ||
123
+ haystack.includes(candidate.title.toLowerCase()) ||
124
+ haystack.includes(candidate.summary.toLowerCase())) {
125
+ continue;
126
+ }
127
+ if (hasToolConflict(candidate, activeToolNames))
128
+ continue;
129
+ deduped.set(candidate.path, candidate);
130
+ }
131
+ const scored = [...deduped.values()].map((candidate) => ({
132
+ candidate,
133
+ score: computeCandidateScore(candidate, keywords),
134
+ }));
135
+ const ranked = scored
136
+ .filter((entry) => entry.score > 0 || keywords.length === 0)
137
+ .sort((left, right) => {
138
+ if (left.score !== right.score)
139
+ return right.score - left.score;
140
+ return left.candidate.path.localeCompare(right.candidate.path);
141
+ });
142
+ return ranked.slice(0, maxItems).map((entry) => entry.candidate);
143
+ }
144
+ //# sourceMappingURL=relevant-retrieval.js.map
@@ -3,6 +3,17 @@ import { FileAdapter } from '../adapters/fs/index.js';
3
3
  import { sanitizeObject, sanitizeErrorMessage } from '../utils/sanitizer.js';
4
4
  import { recordAuditEvent } from './audit-trail.js';
5
5
  import { mapErrorForDisplay } from './error-mapping.js';
6
+ /**
7
+ * Reporter for machine-readable modes where stdout/stderr are protocol channels.
8
+ */
9
+ export class SilentReporter {
10
+ log(_level, _message, _metadata) {
11
+ // Intentionally empty.
12
+ }
13
+ clear() {
14
+ // Intentionally empty.
15
+ }
16
+ }
6
17
  /**
7
18
  * Standard Console Reporter
8
19
  */
@@ -418,8 +429,6 @@ export class Logger {
418
429
  const auditMeta = typeof meta === 'string' ? { source: meta } : meta;
419
430
  recordAuditEvent(action, sanitizedDetails, auditMeta);
420
431
  this.writeToLog('audit', formatted);
421
- if (!this.silent)
422
- this.reporter.log('audit', formatted);
423
432
  }
424
433
  sanitizeLogMessage(message) {
425
434
  let result = '';
@@ -31,6 +31,7 @@ const cleanPath = (path) => {
31
31
  'src',
32
32
  'lib',
33
33
  'app',
34
+ 'scripts',
34
35
  'tests',
35
36
  'test',
36
37
  'packages',
@@ -10,9 +10,14 @@ const TEMPLATE_URLS = {
10
10
  'system/plan_system.hbs': new URL('./templates/system/plan_system.hbs', import.meta.url),
11
11
  'system/reflection.hbs': new URL('./templates/system/reflection.hbs', import.meta.url),
12
12
  'system/patch_system.hbs': new URL('./templates/system/patch_system.hbs', import.meta.url),
13
+ 'system/autopilot_system.hbs': new URL('./templates/system/autopilot_system.hbs', import.meta.url),
14
+ 'system/answer_system.hbs': new URL('./templates/system/answer_system.hbs', import.meta.url),
15
+ 'system/research_system.hbs': new URL('./templates/system/research_system.hbs', import.meta.url),
13
16
  'phases/explore_user.hbs': new URL('./templates/phases/explore_user.hbs', import.meta.url),
14
17
  'phases/plan_user.hbs': new URL('./templates/phases/plan_user.hbs', import.meta.url),
15
18
  'phases/patch_user.hbs': new URL('./templates/phases/patch_user.hbs', import.meta.url),
19
+ 'phases/research_user.hbs': new URL('./templates/phases/research_user.hbs', import.meta.url),
20
+ 'phases/review_user.hbs': new URL('./templates/phases/review_user.hbs', import.meta.url),
16
21
  };
17
22
  export class PromptRegistry {
18
23
  templates = new Map();
@@ -30,10 +35,15 @@ export class PromptRegistry {
30
35
  await this.loadTemplate('explore_system', 'system/explore_system.hbs');
31
36
  await this.loadTemplate('plan_system', 'system/plan_system.hbs');
32
37
  await this.loadTemplate('patch_system', 'system/patch_system.hbs');
38
+ await this.loadTemplate('autopilot_system', 'system/autopilot_system.hbs');
39
+ await this.loadTemplate('answer_system', 'system/answer_system.hbs');
40
+ await this.loadTemplate('research_system', 'system/research_system.hbs');
33
41
  await this.loadTemplate('reflection', 'system/reflection.hbs');
34
42
  await this.loadTemplate('explore', 'phases/explore_user.hbs');
35
43
  await this.loadTemplate('plan', 'phases/plan_user.hbs');
36
44
  await this.loadTemplate('patch', 'phases/patch_user.hbs');
45
+ await this.loadTemplate('research', 'phases/research_user.hbs');
46
+ await this.loadTemplate('review', 'phases/review_user.hbs');
37
47
  })();
38
48
  return this.initPromise;
39
49
  }
@@ -73,8 +83,8 @@ export class PromptRegistry {
73
83
  /**
74
84
  * Get serializable tool definitions for template rendering
75
85
  */
76
- getToolsForTemplate() {
77
- return this.tools.map((spec) => ({
86
+ serializeToolsForTemplate(tools) {
87
+ return tools.map((spec) => ({
78
88
  name: spec.name,
79
89
  source: spec.source,
80
90
  intent: spec.intent,
@@ -90,6 +100,12 @@ export class PromptRegistry {
90
100
  examples: spec.examples,
91
101
  }));
92
102
  }
103
+ /**
104
+ * Get serializable tool definitions for template rendering
105
+ */
106
+ getToolsForTemplate(tools = this.tools) {
107
+ return this.serializeToolsForTemplate(tools);
108
+ }
93
109
  /**
94
110
  * Convert Zod schema to a JSON Schema representation
95
111
  */
@@ -142,15 +158,30 @@ export class PromptRegistry {
142
158
  renderPlanSystemWithRuntime(runtime) {
143
159
  return this.render('plan_system', { tools: this.getToolsForTemplate(), runtime });
144
160
  }
161
+ renderPlanSystemWithTools(tools, runtime) {
162
+ return this.render('plan_system', { tools: this.getToolsForTemplate(tools), runtime });
163
+ }
145
164
  renderPatchSystem() {
146
165
  return this.render('patch_system', { tools: this.getToolsForTemplate() });
147
166
  }
148
167
  renderPatchSystemWithRuntime(runtime) {
149
168
  return this.render('patch_system', { tools: this.getToolsForTemplate(), runtime });
150
169
  }
170
+ renderPatchSystemWithTools(tools, runtime) {
171
+ return this.render('patch_system', { tools: this.getToolsForTemplate(tools), runtime });
172
+ }
151
173
  renderExploreSystem() {
152
174
  return this.render('explore_system', { tools: this.getToolsForTemplate() });
153
175
  }
176
+ renderAutopilotSystem() {
177
+ return this.render('autopilot_system', {});
178
+ }
179
+ renderAnswerSystem() {
180
+ return this.render('answer_system', {});
181
+ }
182
+ renderResearchSystem() {
183
+ return this.render('research_system', {});
184
+ }
154
185
  renderExploreSystemWithRuntime(runtime) {
155
186
  return this.render('explore_system', { tools: this.getToolsForTemplate(), runtime });
156
187
  }
@@ -163,6 +194,12 @@ export class PromptRegistry {
163
194
  renderPatch(vars) {
164
195
  return this.render('patch', vars);
165
196
  }
197
+ renderResearch(vars) {
198
+ return this.render('research', vars);
199
+ }
200
+ renderReview(vars) {
201
+ return this.render('review', vars);
202
+ }
166
203
  renderReflection(vars) {
167
204
  return this.render('reflection', vars);
168
205
  }
@@ -1,4 +1,13 @@
1
+ import { resolvePhaseVisibleTools } from '../tools/tool-visibility.js';
2
+ import { Phase } from '../types/runtime.js';
1
3
  import { getPromptRegistry } from './registry.js';
4
+ function resolveToolSpecs(toolRegistry) {
5
+ if (!toolRegistry)
6
+ return [];
7
+ if (Array.isArray(toolRegistry))
8
+ return toolRegistry;
9
+ return toolRegistry.listAll();
10
+ }
2
11
  function extractTargetFiles(plan) {
3
12
  try {
4
13
  const parsed = JSON.parse(plan);
@@ -20,14 +29,26 @@ export async function getExplorePrompt(context, instruction, lastError) {
20
29
  lastError,
21
30
  });
22
31
  }
23
- export async function getExploreSystemPrompt(toolRegistry, runtime) {
32
+ export async function getExploreSystemPrompt(runtime) {
24
33
  const promptRegistry = getPromptRegistry();
25
34
  await promptRegistry.init();
26
- if (toolRegistry) {
27
- promptRegistry.setTools(toolRegistry.listAll());
28
- }
29
35
  return promptRegistry.renderExploreSystemWithRuntime(runtime);
30
36
  }
37
+ export async function getAutopilotSystemPrompt() {
38
+ const promptRegistry = getPromptRegistry();
39
+ await promptRegistry.init();
40
+ return promptRegistry.renderAutopilotSystem();
41
+ }
42
+ export async function getAnswerSystemPrompt() {
43
+ const promptRegistry = getPromptRegistry();
44
+ await promptRegistry.init();
45
+ return promptRegistry.renderAnswerSystem();
46
+ }
47
+ export async function getResearchSystemPrompt() {
48
+ const promptRegistry = getPromptRegistry();
49
+ await promptRegistry.init();
50
+ return promptRegistry.renderResearchSystem();
51
+ }
31
52
  export async function getPlanPrompt(context, instruction, maxFilesChanged, lastError) {
32
53
  const promptRegistry = getPromptRegistry();
33
54
  await promptRegistry.init();
@@ -50,20 +71,37 @@ export async function getPatchPrompt(plan, context, maxFilesChanged, maxDiffLine
50
71
  lastError,
51
72
  });
52
73
  }
74
+ export async function getResearchPrompt(context, instruction) {
75
+ const promptRegistry = getPromptRegistry();
76
+ await promptRegistry.init();
77
+ return promptRegistry.renderResearch({
78
+ context,
79
+ instruction,
80
+ });
81
+ }
82
+ export async function getReviewPrompt(contextJson) {
83
+ const promptRegistry = getPromptRegistry();
84
+ await promptRegistry.init();
85
+ return promptRegistry.renderReview({ contextJson });
86
+ }
53
87
  export async function getPlanSystemPrompt(toolRegistry, runtime) {
54
88
  const promptRegistry = getPromptRegistry();
55
89
  await promptRegistry.init();
56
- if (toolRegistry) {
57
- promptRegistry.setTools(toolRegistry.listAll());
58
- }
59
- return promptRegistry.renderPlanSystemWithRuntime(runtime);
90
+ const promptVisibleTools = resolvePhaseVisibleTools({
91
+ phase: Phase.PLAN,
92
+ tools: resolveToolSpecs(toolRegistry),
93
+ runtime,
94
+ });
95
+ return promptRegistry.renderPlanSystemWithTools(promptVisibleTools, runtime);
60
96
  }
61
97
  export async function getPatchSystemPrompt(toolRegistry, runtime) {
62
98
  const promptRegistry = getPromptRegistry();
63
99
  await promptRegistry.init();
64
- if (toolRegistry) {
65
- promptRegistry.setTools(toolRegistry.listAll());
66
- }
67
- return promptRegistry.renderPatchSystemWithRuntime(runtime);
100
+ const promptVisibleTools = resolvePhaseVisibleTools({
101
+ phase: Phase.PATCH,
102
+ tools: resolveToolSpecs(toolRegistry),
103
+ runtime,
104
+ });
105
+ return promptRegistry.renderPatchSystemWithTools(promptVisibleTools, runtime);
68
106
  }
69
107
  //# sourceMappingURL=runtime.js.map
@@ -34,7 +34,7 @@ The previous attempt failed with the following error. Please fix the issue descr
34
34
  1. **Format**: Is it standard git unified diff (starting with `diff --git`)?
35
35
  2. **Paths**: Do `--- a/path` and `+++ b/path` exactly match target files?
36
36
  3. **Scope**: Does it only modify what's requested in the instruction?
37
- 4. **Context**: Are there 8-12 lines of EXACT surrounding context for each hunk?
37
+ 4. **Context**: Are there enough exact context lines for each hunk to apply cleanly?
38
38
  5. **Cleanliness**: Is there ANY text other than the diff itself? (If yes, remove it).
39
39
 
40
40
  # Requirements
@@ -43,13 +43,10 @@ The previous attempt failed with the following error. Please fix the issue descr
43
43
  - **If you need to fix an error, regenerate the ENTIRE diff correctly in one block.**
44
44
  - **CRITICAL**: Use EXACT relative paths from the repository root in diff headers.
45
45
  - Example: `--- a/src/index.js` and `+++ b/src/index.js`
46
- - **Context Matching**: Provide 8-12 lines of surrounding code.
47
- - Indentation and whitespace must match EXACTLY.
48
- - **DO NOT include `index <old>..<new>` lines** (the host may strip them for safety).
46
+ - **Context Matching**: Provide exact context lines; indentation and whitespace must match EXACTLY.
49
47
  - Constraints:
50
48
  - Maximum {{maxFilesChanged}} files.
51
49
  - Maximum {{maxDiffLines}} total diff lines.
52
- - No file creation, deletion, or renaming.
53
50
  - No unrelated refactoring or formatting.
54
51
  - **DO NOT translate or modify comments unless explicitly instructed.**
55
52
 
@@ -0,0 +1,11 @@
1
+ You are running in deep research mode.
2
+ Use available tools to gather external information as needed.
3
+ Return JSON with keys: researchNotes, researchFindings, sources, researchText.
4
+ Each researchFinding should include: summary, confidence (0-1), uncertainty (string).
5
+ Each source should include: toolName, summary, ok, timestamp (epoch ms).
6
+
7
+ Instruction:
8
+ {{{instruction}}}
9
+
10
+ Context:
11
+ {{{context}}}
@@ -0,0 +1,3 @@
1
+ Please review the following context and provide suggestions for improvement:
2
+
3
+ {{{contextJson}}}
@@ -0,0 +1,5 @@
1
+ You are a coding assistant in "answer" mode.
2
+ You may use read-only tools to inspect the repository when helpful.
3
+ Never write files, never apply patches, and never run shell commands.
4
+ If repository inspection is not required, answer directly without tools.
5
+ Answer in the same language as the user.
@@ -0,0 +1,11 @@
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.
6
+ Treat simple repo-relative paths like "smoke.txt", "README.md", or "src/index.ts" as valid paths from the repository root.
7
+ 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
+ Use `interaction.ask_user` only when the task is genuinely ambiguous and you cannot safely infer the next action.
9
+ 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
+ Do not call `interaction.ask_user` with free-form strings, a single question field, string-only options, or empty options arrays.
11
+ 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.
@@ -1,26 +1,17 @@
1
- {{> main_system phaseInstruction="You are a code investigator. Your goal is to locate and read the files relevant to the user's request.
1
+ You are in EXPLORE. Find the files relevant to the user's request and read them before planning. Do not modify files.
2
2
 
3
- **Capabilities:**
4
- - You can list directories (`fs.list`), search code (`code.search`), and read files (`fs.read`).
5
- - You can ask the user a short clarification question (`interaction.ask_user`) when the request is ambiguous.
6
- - You CANNOT modify files. Your role is purely investigative.
3
+ Use only these exact tool names in EXPLORE: `fs.list`, `code.search`, `fs.read`, `interaction.ask_user`.
7
4
 
8
- **Workflow:**
9
- 1. Analyze the user's request to identify key terms and potential file locations.
10
- 1. If the request does not clearly specify which files or area to focus on, ask ONE clarification question using `interaction.ask_user` with 2-4 options (for example: Focus area: `src/`, `tests/`, `docs/`, `config`).
11
- 2. Use `fs.list` or `code.search` to locate relevant files.
12
- 3. Use `fs.read` to examine the content of promising files.
13
- 4. Continue exploring until you are confident you have identified the files that need to be modified.
14
- 5. When you have found the relevant context, reply with a concise summary of your findings to complete the phase.
5
+ Default sequence:
6
+ - If the user gives a file path, call `fs.read` first using `{"file":"path/to/file"}`.
7
+ - Otherwise, use `code.search` with `{"pattern":"keyword"}` or `fs.list` with `{"path":"."}` to find a plausible file, then call `fs.read` on it as soon as possible.
8
+ - Use `interaction.ask_user` only if the request is too ambiguous to choose a reasonable first file or search target.
15
9
 
16
- **Important:**
17
- - **MANDATORY: YOU MUST READ AT LEAST ONE FILE.** Any exploration that concludes without at least one successful `fs.read` call is a violation of the execution contract and will be rejected.
18
- - **DO NOT GUESS file contents.** You must always use `fs.read` to see the actual content of a file before claiming it is relevant.
19
- - If you find a file that looks relevant, **YOU MUST READ IT**. The content will be automatically added to the context for the next phase.
20
- - Tool calls must use valid JSON arguments that match the tool input schema.
21
- - `fs.list` requires `{\"path\":\"relative/dir/from/repo/root\"}` (example: `{\"path\":\".\"}`); optionally add `includeHidden`, `maxEntries`.
22
- - `fs.read` requires `{\"file\":\"relative/path/from/repo/root\"}` (example: `{\"file\":\"README.md\"}`).
23
- - `code.search` requires `{\"pattern\":\"...\"}`; optionally add `glob`, `cwd`, `maxMatches`, `isRegex`.
24
- - Be efficient. Do not read the entire codebase.
25
- - If the user provides specific file paths, read them first.
26
- "}}
10
+ Rules:
11
+ - Do not finish EXPLORE until at least one `fs.read` call succeeds.
12
+ - Do not mention a file as relevant unless you have read it with `fs.read`.
13
+ - Do not guess file contents.
14
+ - Use valid JSON arguments only.
15
+ - Be efficient: prefer short list/search/read loops over broad exploration.
16
+
17
+ When you have enough read-backed context, reply with a concise summary of the files you read and why they matter.
@@ -1,18 +1,6 @@
1
- You are SalmonLoop. {{phaseInstruction}}
1
+ You are SalmonLoop.
2
2
 
3
- {{#if runtime.plan}}
4
- ## Runtime Plan (Local, gitignored)
5
- Session ID: {{runtime.plan.sessionId}}
6
- Path hint: {{runtime.plan.planPathHint}}
7
-
8
- Guidelines:
9
- - Use `plan.read` to obtain `baseHash`.
10
- - Use `plan.update` with this `sessionId` to update plan steps by stable `sl:id`.
11
- - Prefer appending tasks under `work_root` and tracking progress via `sl:status` and checkboxes.
12
- - Keep phase outputs strictly compliant (PLAN must still be pure JSON, PATCH must still be a unified diff). Tool calls are allowed, but your final content must remain valid.
3
+ Primary text is authoritative. Do not guess missing file contents. If required content is missing, use the available read-only tools to fetch it before finalizing.
4
+ {{#if phaseInstruction}}
5
+ {{phaseInstruction}}
13
6
  {{/if}}
14
-
15
- ## Knowledge Retention
16
- You have access to a cross-session knowledge base. When you learn important project-specific rules, long-term architectural decisions, or user preferences, use the `update_knowledge` tool to persist them. This helps you stay consistent in future sessions.
17
-
18
- {{> tool_defs}}
@@ -1,10 +1,41 @@
1
- {{> main_system phaseInstruction="Use tool calls to inspect the repository when needed. Output only a valid unified diff when patching.
1
+ You are PATCH, a phase-native diff compiler. Produce only a canonical unified diff document.
2
2
 
3
- CRITICAL CHARACTER FIDELITY RULE:
4
- You MUST replicate context lines (lines starting with ' ') EXACTLY as they appear in the provided snippets.
5
- - DO NOT remove or add hyphens.
6
- - DO NOT normalize casing.
7
- - DO NOT fix typos in context lines.
8
- Failure to maintain 100% character fidelity will cause git-apply to fail.
3
+ Output contract:
4
+ - The very first line must be `diff --git a/... b/...`.
5
+ - The document may contain multiple file sections.
6
+ - Each file section must use `--- a/...` and `+++ b/...`.
7
+ - For file add/delete, use `--- /dev/null` or `+++ /dev/null` as appropriate.
8
+ - An `index` line is optional and not required.
9
9
 
10
- If you used a sub-agent, prefer referencing its proposal artifacts (s8p handles) in your reasoning rather than copying large diff text. Only read artifacts via 'artifact.read' when necessary for correctness."}}
10
+ Tooling rules:
11
+ - Default tool set is read-only and minimal: `fs.read`, `code.search`.
12
+ - Use tools only when necessary to read plan-relevant files.
13
+ - Any further context lookups must remain read-only and target plan-relevant files.
14
+ - Never call or mention write tools.
15
+
16
+ {{> tool_defs}}
17
+
18
+ Fidelity rule:
19
+ - Context lines (lines starting with a single space) must match the source text exactly, byte-for-byte.
20
+
21
+ Strict bans (do not output any of these):
22
+ - Prose, explanations, or commentary.
23
+ - Markdown fences or code blocks.
24
+ - JSON or other structured wrappers.
25
+ - Multiple alternatives or options.
26
+ - Tool calls, tool schemas, or tool instructions.
27
+
28
+ LEGAL EXAMPLE (verbatim):
29
+ diff --git a/README.md b/README.md
30
+ --- a/README.md
31
+ +++ b/README.md
32
+ @@ -1 +1 @@
33
+ -Hello
34
+ +Hello world
35
+
36
+ ILLEGAL PATTERNS (each is invalid):
37
+ Here is the diff:
38
+ Fenced diff blocks that start with triple backticks.
39
+ {"diff":"diff --git a/x b/x"}
40
+ Option A: diff --git a/x b/x
41
+ Tool calls, tool schemas, or tool instructions.