salmon-loop 0.2.3 → 0.2.16

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 (234) 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 +161 -24
  4. package/dist/cli/commands/chat.js +30 -24
  5. package/dist/cli/commands/context.js +15 -3
  6. package/dist/cli/commands/flow-mode.js +63 -0
  7. package/dist/cli/commands/help-format.js +12 -0
  8. package/dist/cli/commands/registry.js +6 -7
  9. package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
  10. package/dist/cli/commands/run/config-resolution.js +30 -24
  11. package/dist/cli/commands/run/early-errors.js +23 -0
  12. package/dist/cli/commands/run/handler.js +131 -44
  13. package/dist/cli/commands/run/headless-error-writer.js +8 -0
  14. package/dist/cli/commands/run/loop-params.js +3 -0
  15. package/dist/cli/commands/run/mode.js +2 -5
  16. package/dist/cli/commands/run/parse-options.js +18 -2
  17. package/dist/cli/commands/run/persist-session.js +10 -1
  18. package/dist/cli/commands/run/preflight.js +10 -0
  19. package/dist/cli/commands/run/reporter-factory.js +4 -0
  20. package/dist/cli/commands/run/runtime-llm.js +38 -11
  21. package/dist/cli/commands/run/runtime-options.js +2 -2
  22. package/dist/cli/commands/run/validate-options.js +0 -5
  23. package/dist/cli/commands/run/verbose.js +2 -7
  24. package/dist/cli/commands/serve.js +117 -90
  25. package/dist/cli/commands/tool-names.js +78 -78
  26. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
  27. package/dist/cli/headless/json-protocol.js +37 -0
  28. package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
  29. package/dist/cli/headless/protocol-metadata.js +22 -0
  30. package/dist/cli/headless/stream-json-protocol.js +34 -1
  31. package/dist/cli/index.js +6 -4
  32. package/dist/cli/locales/en.js +32 -6
  33. package/dist/cli/program-bootstrap.js +14 -4
  34. package/dist/cli/program-commands.js +9 -1
  35. package/dist/cli/program-options.js +1 -0
  36. package/dist/cli/reporters/anthropic-stream.js +7 -1
  37. package/dist/cli/reporters/json.js +4 -0
  38. package/dist/cli/reporters/stream-json.js +17 -2
  39. package/dist/cli/run-cli.js +5 -3
  40. package/dist/cli/slash/runtime.js +30 -15
  41. package/dist/cli/ui/components/CommandInput.js +7 -3
  42. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  43. package/dist/cli/utils/command-option-source.js +13 -0
  44. package/dist/cli/utils/output-format.js +6 -0
  45. package/dist/cli/utils/resolve-cli-config.js +98 -0
  46. package/dist/cli/utils/verbose-level.js +8 -0
  47. package/dist/cli/utils/verify-resolver.js +8 -4
  48. package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
  49. package/dist/core/adapters/fs/file-adapter.js +6 -0
  50. package/dist/core/adapters/fs/filesystem.js +2 -1
  51. package/dist/core/adapters/git/git-adapter.js +78 -1
  52. package/dist/core/benchmark/patch-artifact.js +124 -0
  53. package/dist/core/benchmark/swe-bench.js +25 -0
  54. package/dist/core/config/load.js +39 -18
  55. package/dist/core/config/merge.js +27 -0
  56. package/dist/core/config/paths.js +24 -5
  57. package/dist/core/config/resolve-llm.js +12 -0
  58. package/dist/core/config/resolve.js +7 -5
  59. package/dist/core/config/resolvers/server.js +0 -6
  60. package/dist/core/config/validate.js +94 -21
  61. package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
  62. package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
  63. package/dist/core/context/keywords.js +18 -4
  64. package/dist/core/context/service-deps.js +2 -2
  65. package/dist/core/context/service.js +8 -0
  66. package/dist/core/context/steps/context-gather.js +38 -0
  67. package/dist/core/context/summarization/summarizer.js +55 -12
  68. package/dist/core/context/targeting/target-resolver.js +4 -4
  69. package/dist/core/extensions/index.js +23 -5
  70. package/dist/core/extensions/paths.js +31 -0
  71. package/dist/core/extensions/schemas.js +8 -5
  72. package/dist/core/facades/cli-chat.js +6 -2
  73. package/dist/core/facades/cli-command-chat.js +2 -1
  74. package/dist/core/facades/cli-command-tool-names.js +2 -0
  75. package/dist/core/facades/cli-context.js +1 -0
  76. package/dist/core/facades/cli-observability.js +1 -1
  77. package/dist/core/facades/cli-run-handler.js +4 -2
  78. package/dist/core/facades/cli-run-persist-session.js +1 -0
  79. package/dist/core/facades/cli-serve.js +2 -4
  80. package/dist/core/facades/cli-utils-worktree.js +1 -1
  81. package/dist/core/failure/diagnostics.js +53 -1
  82. package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
  83. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
  84. package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
  85. package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
  86. package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
  87. package/dist/core/grizzco/engine/transaction/transaction-runner.js +173 -7
  88. package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
  89. package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
  90. package/dist/core/grizzco/steps/answer.js +13 -14
  91. package/dist/core/grizzco/steps/autopilot.js +396 -0
  92. package/dist/core/grizzco/steps/cache-sharing.js +29 -0
  93. package/dist/core/grizzco/steps/explore.js +37 -21
  94. package/dist/core/grizzco/steps/generateReview.js +2 -5
  95. package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
  96. package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
  97. package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
  98. package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
  99. package/dist/core/grizzco/steps/patch.js +105 -146
  100. package/dist/core/grizzco/steps/plan.js +101 -25
  101. package/dist/core/grizzco/steps/preflight.js +5 -3
  102. package/dist/core/grizzco/steps/request-assembly.js +78 -0
  103. package/dist/core/grizzco/steps/research.js +39 -36
  104. package/dist/core/grizzco/steps/tool-runtime.js +47 -0
  105. package/dist/core/grizzco/steps/verify-shared.js +23 -0
  106. package/dist/core/grizzco/steps/verify.js +13 -21
  107. package/dist/core/intent/chat-intent.js +0 -4
  108. package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
  109. package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
  110. package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
  111. package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
  112. package/dist/core/llm/ai-sdk/request-params.js +74 -1
  113. package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
  114. package/dist/core/llm/ai-sdk.js +112 -27
  115. package/dist/core/llm/capabilities.js +12 -0
  116. package/dist/core/llm/contracts/repair.js +36 -30
  117. package/dist/core/llm/errors.js +83 -2
  118. package/dist/core/llm/message-composition.js +7 -22
  119. package/dist/core/llm/phase-router.js +29 -10
  120. package/dist/core/llm/redact.js +28 -3
  121. package/dist/core/llm/registry.js +2 -0
  122. package/dist/core/llm/request-augmentation.js +55 -0
  123. package/dist/core/llm/request-envelope.js +334 -0
  124. package/dist/core/llm/shared-request-assembly.js +35 -0
  125. package/dist/core/llm/stream-utils.js +13 -4
  126. package/dist/core/llm/utils.js +18 -29
  127. package/dist/core/memory/relevant-retrieval.js +144 -0
  128. package/dist/core/observability/logger.js +11 -2
  129. package/dist/core/patch/diff.js +1 -0
  130. package/dist/core/prompts/registry.js +39 -2
  131. package/dist/core/prompts/runtime.js +50 -12
  132. package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
  133. package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
  134. package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
  135. package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
  136. package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
  137. package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
  138. package/dist/core/prompts/templates/system/main_system.hbs +4 -16
  139. package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
  140. package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
  141. package/dist/core/prompts/templates/system/research_system.hbs +2 -0
  142. package/dist/core/protocols/a2a/agent-card.js +3 -2
  143. package/dist/core/protocols/a2a/sdk/executor.js +8 -6
  144. package/dist/core/protocols/a2a/sdk/server.js +0 -1
  145. package/dist/core/protocols/acp/formal-agent.js +221 -55
  146. package/dist/core/protocols/acp/handlers.js +5 -1
  147. package/dist/core/protocols/acp/permission-provider.js +21 -1
  148. package/dist/core/protocols/shared/execution-request.js +24 -0
  149. package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
  150. package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
  151. package/dist/core/public-capabilities/projections.js +29 -0
  152. package/dist/core/public-capabilities/registry.js +26 -0
  153. package/dist/core/public-capabilities/types.js +2 -0
  154. package/dist/core/runtime/agent-server-runtime.js +47 -43
  155. package/dist/core/runtime/execution-profile.js +67 -0
  156. package/dist/core/session/artifact-state.js +160 -0
  157. package/dist/core/session/compaction/index.js +183 -0
  158. package/dist/core/session/compaction/microcompact.js +78 -0
  159. package/dist/core/session/compaction/tracking.js +48 -0
  160. package/dist/core/session/compaction/types.js +11 -0
  161. package/dist/core/session/compression.js +12 -4
  162. package/dist/core/session/manager.js +247 -10
  163. package/dist/core/session/pruning-strategy.js +55 -9
  164. package/dist/core/session/replacement-preview-provider.js +24 -0
  165. package/dist/core/session/replacement-state.js +131 -0
  166. package/dist/core/session/resume-repair/pipeline.js +79 -0
  167. package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
  168. package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
  169. package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
  170. package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
  171. package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
  172. package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
  173. package/dist/core/session/resume-repair/types.js +2 -0
  174. package/dist/core/session/summary-sync.js +164 -13
  175. package/dist/core/session/token-tracker.js +6 -0
  176. package/dist/core/skills/audit.js +34 -0
  177. package/dist/core/skills/bridge.js +84 -7
  178. package/dist/core/skills/discovery.js +94 -0
  179. package/dist/core/skills/feature-flags.js +52 -0
  180. package/dist/core/skills/index.js +1 -1
  181. package/dist/core/skills/loader.js +195 -20
  182. package/dist/core/skills/parser.js +296 -24
  183. package/dist/core/skills/permissions.js +117 -0
  184. package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
  185. package/dist/core/skills/runtime/SkillRunner.js +240 -61
  186. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
  187. package/dist/core/strata/layers/worktree.js +70 -13
  188. package/dist/core/strata/runtime/synchronizer.js +29 -2
  189. package/dist/core/streaming/stream-assembler.js +75 -31
  190. package/dist/core/sub-agent/context-snapshot.js +156 -0
  191. package/dist/core/sub-agent/core/loop.js +1 -1
  192. package/dist/core/sub-agent/core/manager.js +119 -20
  193. package/dist/core/sub-agent/dispatch-policy.js +29 -0
  194. package/dist/core/sub-agent/prefix-consistency.js +48 -0
  195. package/dist/core/sub-agent/registry-defaults.js +4 -0
  196. package/dist/core/sub-agent/tools/task-spawn.js +79 -2
  197. package/dist/core/sub-agent/types.js +134 -5
  198. package/dist/core/tools/audit.js +13 -4
  199. package/dist/core/tools/builtin/ast-grep.js +1 -1
  200. package/dist/core/tools/builtin/ast.js +1 -1
  201. package/dist/core/tools/builtin/benchmark.js +360 -0
  202. package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
  203. package/dist/core/tools/builtin/code-search/executor.js +6 -1
  204. package/dist/core/tools/builtin/code-search/spec.js +26 -2
  205. package/dist/core/tools/builtin/fs.js +256 -23
  206. package/dist/core/tools/builtin/git.js +2 -2
  207. package/dist/core/tools/builtin/index.js +51 -2
  208. package/dist/core/tools/builtin/interaction.js +8 -1
  209. package/dist/core/tools/builtin/plan.js +37 -15
  210. package/dist/core/tools/builtin/shell.js +1 -1
  211. package/dist/core/tools/loader.js +39 -16
  212. package/dist/core/tools/mapper.js +17 -3
  213. package/dist/core/tools/parallel/scheduler.js +35 -4
  214. package/dist/core/tools/permissions/permission-rules.js +5 -10
  215. package/dist/core/tools/policy.js +6 -1
  216. package/dist/core/tools/recoverable-tool-errors.js +10 -0
  217. package/dist/core/tools/router.js +24 -6
  218. package/dist/core/tools/session.js +458 -48
  219. package/dist/core/tools/tool-visibility.js +62 -0
  220. package/dist/core/tools/types.js +9 -1
  221. package/dist/core/types/execution.js +4 -0
  222. package/dist/core/types/flow-mode.js +8 -0
  223. package/dist/core/utils/path.js +52 -0
  224. package/dist/core/verification/runner.js +4 -1
  225. package/dist/interfaces/cli/task-runner.js +4 -3
  226. package/dist/languages/typescript/index.js +4 -1
  227. package/dist/locales/en.js +87 -2
  228. package/dist/utils/eol.js +1 -1
  229. package/package.json +15 -8
  230. package/scripts/fix-es-abstract-compat.js +77 -0
  231. package/dist/core/runtime/fastify-server-bundle.js +0 -26
  232. package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
  233. package/dist/core/runtime/sidecar-paths.js +0 -47
  234. package/dist/core/runtime/sidecar-route-catalog.js +0 -103
@@ -12,16 +12,9 @@ export function createPhaseRoutingLlm(params) {
12
12
  chat(messages, options) {
13
13
  return resolve(options?.phase).chat(messages, options);
14
14
  },
15
- getCapabilities() {
16
- const base = defaultLlm.getCapabilities?.() ?? {};
17
- const hasToolCalling = Object.values(phaseLlms).some((llm) => llm?.getCapabilities?.().toolCalling);
18
- const hasStreaming = Object.values(phaseLlms).some((llm) => llm?.getCapabilities?.().streaming);
19
- const hasJsonMode = Object.values(phaseLlms).some((llm) => llm?.getCapabilities?.().responseFormatJsonObject);
20
- return {
21
- toolCalling: base.toolCalling || hasToolCalling,
22
- streaming: base.streaming || hasStreaming,
23
- responseFormatJsonObject: base.responseFormatJsonObject || hasJsonMode,
24
- };
15
+ getCapabilities(options) {
16
+ const selected = resolve(options?.phase);
17
+ return selected.getCapabilities?.(options) ?? {};
25
18
  },
26
19
  createPlan(context, instruction, lastError, signal) {
27
20
  return resolve(Phase.PLAN).createPlan(context, instruction, lastError, signal);
@@ -33,11 +26,37 @@ export function createPhaseRoutingLlm(params) {
33
26
  if (hasAnyStreaming) {
34
27
  routed.chatStream = async function* (messages, options) {
35
28
  const selected = resolve(options?.phase);
29
+ const capabilities = selected.getCapabilities?.(options) ?? {};
30
+ if (capabilities.streaming === false) {
31
+ const fallback = await selected.chat(messages, options);
32
+ if (fallback.reasoning_content) {
33
+ yield {
34
+ role: 'assistant',
35
+ source: 'synthesized',
36
+ reasoningDelta: fallback.reasoning_content,
37
+ };
38
+ }
39
+ if (fallback.content) {
40
+ yield { role: 'assistant', source: 'synthesized', contentDelta: fallback.content };
41
+ }
42
+ if (Array.isArray(fallback.tool_calls) && fallback.tool_calls.length > 0) {
43
+ yield { role: 'assistant', source: 'synthesized', tool_calls: fallback.tool_calls };
44
+ }
45
+ yield { role: 'assistant', source: 'synthesized', done: true, finishReason: 'stop' };
46
+ return;
47
+ }
36
48
  if (selected.chatStream) {
37
49
  yield* selected.chatStream(messages, options);
38
50
  return;
39
51
  }
40
52
  const fallback = await selected.chat(messages, options);
53
+ if (fallback.reasoning_content) {
54
+ yield {
55
+ role: 'assistant',
56
+ source: 'synthesized',
57
+ reasoningDelta: fallback.reasoning_content,
58
+ };
59
+ }
41
60
  if (fallback.content) {
42
61
  yield { role: 'assistant', source: 'synthesized', contentDelta: fallback.content };
43
62
  }
@@ -1,4 +1,22 @@
1
1
  const SECRET_KEY_REGEX = /(api[-_]?key|authorization|token|secret|password|cookie)/i;
2
+ const STRING_SECRET_PATTERNS = [
3
+ {
4
+ pattern: /(authorization\s*:\s*bearer\s+)[^\s'",`]+/gi,
5
+ replacement: '$1[REDACTED]',
6
+ },
7
+ {
8
+ pattern: /\bbearer\s+[a-z0-9._~+/=-]{16,}\b/gi,
9
+ replacement: 'Bearer [REDACTED]',
10
+ },
11
+ {
12
+ pattern: /\bsk-[a-z0-9_-]{16,}\b/gi,
13
+ replacement: '[REDACTED]',
14
+ },
15
+ {
16
+ pattern: /\b(api[-_]?key|token|secret|password|cookie)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s'",`]+)/gi,
17
+ replacement: '$1=[REDACTED]',
18
+ },
19
+ ];
2
20
  function isRecord(value) {
3
21
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
4
22
  }
@@ -7,9 +25,16 @@ function truncate(value, max = 500) {
7
25
  return value;
8
26
  return value.slice(0, max) + '...';
9
27
  }
28
+ function redactString(value) {
29
+ let redacted = value;
30
+ for (const { pattern, replacement } of STRING_SECRET_PATTERNS) {
31
+ redacted = redacted.replace(pattern, replacement);
32
+ }
33
+ return truncate(redacted, 500);
34
+ }
10
35
  export function redactValue(value) {
11
36
  if (typeof value === 'string')
12
- return truncate(value, 500);
37
+ return redactString(value);
13
38
  if (typeof value === 'number' || typeof value === 'boolean' || value === null)
14
39
  return value;
15
40
  if (Array.isArray(value))
@@ -29,9 +54,9 @@ export function redactValue(value) {
29
54
  return '[Unserializable]';
30
55
  }
31
56
  export function redactJsonString(raw) {
32
- return truncate(raw, 500);
57
+ return redactString(raw);
33
58
  }
34
59
  export function redactErrorMessage(raw) {
35
- return truncate(raw, 500);
60
+ return redactString(raw);
36
61
  }
37
62
  //# sourceMappingURL=redact.js.map
@@ -39,6 +39,7 @@ export function createDefaultLlmRegistry() {
39
39
  headers: resolved.api.headers,
40
40
  timeoutMs: resolved.api.timeoutMs,
41
41
  langfuseEnabled: options?.langfuseEnabled,
42
+ capabilities: resolved.capabilities,
42
43
  }),
43
44
  backend: 'ai-sdk',
44
45
  warnings,
@@ -73,6 +74,7 @@ export function createDefaultOpenAiFallback(resolved, options) {
73
74
  headers: resolved.api.headers,
74
75
  timeoutMs: resolved.api.timeoutMs,
75
76
  langfuseEnabled: options?.langfuseEnabled,
77
+ capabilities: resolved.capabilities,
76
78
  }),
77
79
  backend: 'ai-sdk',
78
80
  warnings,
@@ -0,0 +1,55 @@
1
+ const MEMORY_HEADER = '[Relevant memory]';
2
+ function defaultCountTokens(text) {
3
+ return Math.ceil(text.length / 4);
4
+ }
5
+ function normalizeEntry(entry) {
6
+ return {
7
+ path: String(entry.path),
8
+ title: String(entry.title),
9
+ summary: String(entry.summary),
10
+ tags: entry.tags,
11
+ };
12
+ }
13
+ function formatRelevantMemoryEntry(entry) {
14
+ return `- ${entry.path} | ${entry.title}\n ${entry.summary}`;
15
+ }
16
+ export function augmentPromptWithRelevantMemory(args) {
17
+ const basePrompt = String(args.basePrompt ?? '').trimEnd();
18
+ const selectedEntries = Array.isArray(args.selectedEntries)
19
+ ? args.selectedEntries.map(normalizeEntry)
20
+ : [];
21
+ if (selectedEntries.length === 0) {
22
+ return { prompt: basePrompt, injectedEntries: [] };
23
+ }
24
+ const countTokens = args.countTokens ?? defaultCountTokens;
25
+ const budgetTokens = typeof args.budgetTokens === 'number'
26
+ ? Math.max(0, Math.floor(args.budgetTokens))
27
+ : Number.MAX_SAFE_INTEGER;
28
+ const headerTokens = Math.max(0, Math.floor(countTokens(MEMORY_HEADER)));
29
+ if (headerTokens > budgetTokens) {
30
+ return { prompt: basePrompt, injectedEntries: [] };
31
+ }
32
+ let remainingBudget = budgetTokens - headerTokens;
33
+ const injectedEntries = [];
34
+ const renderedEntries = [];
35
+ for (const entry of selectedEntries) {
36
+ const rendered = formatRelevantMemoryEntry(entry);
37
+ const entryTokens = Math.max(0, Math.floor(countTokens(rendered)));
38
+ if (entryTokens > remainingBudget) {
39
+ break;
40
+ }
41
+ injectedEntries.push(entry);
42
+ renderedEntries.push(rendered);
43
+ remainingBudget -= entryTokens;
44
+ }
45
+ if (renderedEntries.length === 0) {
46
+ return { prompt: basePrompt, injectedEntries: [] };
47
+ }
48
+ const memoryBlock = [MEMORY_HEADER, ...renderedEntries].join('\n');
49
+ return {
50
+ prompt: basePrompt ? `${basePrompt}\n\n${memoryBlock}` : memoryBlock,
51
+ injectedEntries,
52
+ memoryBlock,
53
+ };
54
+ }
55
+ //# sourceMappingURL=request-augmentation.js.map
@@ -0,0 +1,334 @@
1
+ import { createHash } from 'crypto';
2
+ import { getPromptCachingManager } from '../context/cache/prompt-caching.js';
3
+ function isArtifactHandle(value) {
4
+ if (!value || typeof value !== 'object')
5
+ return false;
6
+ const candidate = value;
7
+ return (typeof candidate.handle === 'string' &&
8
+ typeof candidate.mimeType === 'string' &&
9
+ typeof candidate.sha256 === 'string' &&
10
+ typeof candidate.size === 'number');
11
+ }
12
+ function mergeArtifactHandles(existing, incoming, limit = 4) {
13
+ const merged = [];
14
+ const seen = new Set();
15
+ for (const artifact of [...(existing ?? []), ...(incoming ?? [])]) {
16
+ if (!artifact || seen.has(artifact.handle))
17
+ continue;
18
+ seen.add(artifact.handle);
19
+ merged.push(artifact);
20
+ }
21
+ if (merged.length === 0)
22
+ return undefined;
23
+ return merged.slice(-limit);
24
+ }
25
+ function mergeReadArtifactRefs(existing, incoming, limit = 6) {
26
+ const merged = [];
27
+ const seen = new Set();
28
+ for (const item of [...(existing ?? []), ...(incoming ?? [])]) {
29
+ if (!item?.path || !item.artifact?.handle)
30
+ continue;
31
+ const key = `${item.path}::${item.artifact.handle}`;
32
+ if (seen.has(key))
33
+ continue;
34
+ seen.add(key);
35
+ merged.push(item);
36
+ }
37
+ if (merged.length === 0)
38
+ return undefined;
39
+ return merged.slice(-limit);
40
+ }
41
+ function mergePreviewArtifactRefs(existing, incoming, limit = 6) {
42
+ const merged = [];
43
+ const seen = new Set();
44
+ for (const item of [...(existing ?? []), ...(incoming ?? [])]) {
45
+ if (!item?.label || !item.artifact?.handle)
46
+ continue;
47
+ const key = `${item.label}::${item.artifact.handle}`;
48
+ if (seen.has(key))
49
+ continue;
50
+ seen.add(key);
51
+ merged.push(item);
52
+ }
53
+ if (merged.length === 0)
54
+ return undefined;
55
+ return merged.slice(-limit);
56
+ }
57
+ export function resolveRequestArtifactHints(params) {
58
+ const direct = params.artifactHints;
59
+ const auditEntries = params.toolCallingAudit ?? [];
60
+ const auditPatchArtifacts = [];
61
+ const auditAuditArtifacts = [];
62
+ const auditReadArtifacts = [];
63
+ const auditPreviewArtifacts = [];
64
+ for (const entry of auditEntries) {
65
+ if (entry?.toolResultStatus === 'ok' && entry.toolName === 'agent_dispatch') {
66
+ if (isArtifactHandle(entry.toolResultPatchArtifact)) {
67
+ auditPatchArtifacts.push(entry.toolResultPatchArtifact);
68
+ }
69
+ if (isArtifactHandle(entry.toolResultAuditArtifact)) {
70
+ auditAuditArtifacts.push(entry.toolResultAuditArtifact);
71
+ }
72
+ }
73
+ if (typeof entry.toolResultReadArtifactPath === 'string' &&
74
+ isArtifactHandle(entry.toolResultReadArtifact)) {
75
+ auditReadArtifacts.push({
76
+ path: entry.toolResultReadArtifactPath,
77
+ artifact: entry.toolResultReadArtifact,
78
+ });
79
+ }
80
+ if (typeof entry.toolResultPreviewLabel === 'string' &&
81
+ isArtifactHandle(entry.toolResultPreviewArtifact)) {
82
+ auditPreviewArtifacts.push({
83
+ label: entry.toolResultPreviewLabel,
84
+ artifact: entry.toolResultPreviewArtifact,
85
+ });
86
+ }
87
+ }
88
+ const resolved = {
89
+ verifyArtifact: direct?.verifyArtifact,
90
+ subAgentPatchArtifacts: mergeArtifactHandles(direct?.subAgentPatchArtifacts, auditPatchArtifacts),
91
+ subAgentAuditArtifacts: mergeArtifactHandles(direct?.subAgentAuditArtifacts, auditAuditArtifacts),
92
+ recentReadArtifacts: mergeReadArtifactRefs(direct?.recentReadArtifacts, auditReadArtifacts),
93
+ toolResultPreviewArtifacts: mergePreviewArtifactRefs(params.previewProvider?.getPreviewHints(), auditPreviewArtifacts),
94
+ };
95
+ if (!resolved.verifyArtifact &&
96
+ !resolved.subAgentPatchArtifacts?.length &&
97
+ !resolved.subAgentAuditArtifacts?.length &&
98
+ !resolved.recentReadArtifacts?.length &&
99
+ !resolved.toolResultPreviewArtifacts?.length) {
100
+ return undefined;
101
+ }
102
+ return resolved;
103
+ }
104
+ function toSafeMessage(message) {
105
+ if (!message || typeof message !== 'object')
106
+ return null;
107
+ if (message.role !== 'system' && message.role !== 'user' && message.role !== 'assistant') {
108
+ return null;
109
+ }
110
+ if (typeof message.content !== 'string')
111
+ return null;
112
+ const content = message.content.trimEnd();
113
+ if (!content)
114
+ return null;
115
+ return {
116
+ role: message.role,
117
+ content,
118
+ };
119
+ }
120
+ function estimateTokens(text) {
121
+ return Math.ceil(text.length / 4);
122
+ }
123
+ function resolvePromptCacheMode(mode) {
124
+ return mode === 'strict_full_prompt' ? 'strict_full_prompt' : 'cache_safe_only';
125
+ }
126
+ function serializeAttachmentForFingerprint(item) {
127
+ return [
128
+ item.key,
129
+ item.kind,
130
+ item.label ?? '',
131
+ item.content ?? '',
132
+ item.artifactHandle ?? '',
133
+ item.mimeType ?? '',
134
+ typeof item.size === 'number' ? String(item.size) : '',
135
+ ].join('\u001f');
136
+ }
137
+ function createFingerprint(parts) {
138
+ if (parts.length === 0)
139
+ return undefined;
140
+ const hash = createHash('sha256');
141
+ for (const [index, part] of parts.entries()) {
142
+ hash.update(`part:${index}:${part.length}\n`);
143
+ hash.update(part);
144
+ hash.update('\n');
145
+ }
146
+ return hash.digest('hex');
147
+ }
148
+ function toArtifactAttachment(args) {
149
+ return {
150
+ key: args.key,
151
+ kind: 'artifact',
152
+ label: args.label,
153
+ content: '',
154
+ artifactHandle: args.artifact.handle,
155
+ mimeType: args.artifact.mimeType,
156
+ size: args.artifact.size,
157
+ };
158
+ }
159
+ export function buildArtifactHintAttachments(hints) {
160
+ if (!hints)
161
+ return [];
162
+ const attachments = [];
163
+ if (hints.verifyArtifact) {
164
+ attachments.push(toArtifactAttachment({
165
+ key: 'previous-verify-output',
166
+ label: 'Previous verify output',
167
+ artifact: hints.verifyArtifact,
168
+ }));
169
+ }
170
+ for (const [index, artifact] of (hints.subAgentPatchArtifacts ?? []).entries()) {
171
+ attachments.push(toArtifactAttachment({
172
+ key: `previous-subagent-patch-${index}`,
173
+ label: `Previous sub-agent patch artifact ${index + 1}`,
174
+ artifact,
175
+ }));
176
+ }
177
+ for (const [index, artifact] of (hints.subAgentAuditArtifacts ?? []).entries()) {
178
+ attachments.push(toArtifactAttachment({
179
+ key: `previous-subagent-audit-${index}`,
180
+ label: `Previous sub-agent audit artifact ${index + 1}`,
181
+ artifact,
182
+ }));
183
+ }
184
+ for (const [index, item] of (hints.recentReadArtifacts ?? []).entries()) {
185
+ attachments.push(toArtifactAttachment({
186
+ key: `recent-read-${index}`,
187
+ label: `Recent file read: ${item.path}`,
188
+ artifact: item.artifact,
189
+ }));
190
+ }
191
+ for (const [index, item] of (hints.toolResultPreviewArtifacts ?? []).entries()) {
192
+ attachments.push(toArtifactAttachment({
193
+ key: `tool-result-preview-${index}`,
194
+ label: item.label,
195
+ artifact: item.artifact,
196
+ }));
197
+ }
198
+ return attachments;
199
+ }
200
+ function buildPromptCachingHints(surface) {
201
+ const policy = {
202
+ mode: surface.mode,
203
+ eligibility: surface.cacheEligibility,
204
+ namespace: surface.namespace,
205
+ contextHash: surface.contextHash,
206
+ cacheSafeFingerprint: surface.cacheSafeFingerprint,
207
+ lateInjectionFingerprint: surface.lateInjectionFingerprint,
208
+ };
209
+ if (surface.cacheEligibility !== 'eligible' ||
210
+ !surface.contextHash ||
211
+ !surface.cacheSafeFingerprint) {
212
+ return {
213
+ openAICachePolicy: policy,
214
+ };
215
+ }
216
+ const components = [surface.contextHash, `stable:${surface.cacheSafeFingerprint}`];
217
+ if (surface.mode === 'strict_full_prompt' && surface.lateInjectionFingerprint) {
218
+ components.push(`late:${surface.lateInjectionFingerprint}`);
219
+ }
220
+ const manager = getPromptCachingManager();
221
+ return {
222
+ openAICacheHint: manager.generateOpenAICacheHint(surface.namespace ?? 'request-envelope', components),
223
+ openAICachePolicy: policy,
224
+ };
225
+ }
226
+ export function buildRequestEnvelope(params) {
227
+ const systemSections = (Array.isArray(params.system) ? params.system : [params.system]).map((item) => String(item ?? '').trimEnd());
228
+ const attachments = Array.isArray(params.attachments)
229
+ ? params.attachments
230
+ .filter((item) => item && (typeof item.content === 'string' || typeof item.artifactHandle === 'string'))
231
+ .map((item) => ({
232
+ ...item,
233
+ content: typeof item.content === 'string' ? item.content.trimEnd() : '',
234
+ }))
235
+ .filter((item) => item.content.length > 0 || typeof item.artifactHandle === 'string')
236
+ : [];
237
+ const userMetaMessages = [];
238
+ const conversationMessages = [];
239
+ if (Array.isArray(params.conversationContext)) {
240
+ for (const message of params.conversationContext) {
241
+ const safe = toSafeMessage(message);
242
+ if (!safe)
243
+ continue;
244
+ if (safe.role === 'system') {
245
+ userMetaMessages.push(safe);
246
+ continue;
247
+ }
248
+ conversationMessages.push(safe);
249
+ }
250
+ }
251
+ const cacheMode = resolvePromptCacheMode(params.cacheSafeSurface?.mode);
252
+ const cacheSafeAttachments = attachments.filter((item) => item.cacheSafe);
253
+ const lateInjectionAttachments = attachments.filter((item) => !item.cacheSafe);
254
+ const cacheSafeFingerprint = createFingerprint([
255
+ ...systemSections.map((section, index) => `system:${index}\u001f${section}`),
256
+ ...cacheSafeAttachments.map((item, index) => `attachment:${index}\u001f${serializeAttachmentForFingerprint(item)}`),
257
+ ]);
258
+ const lateInjectionFingerprint = createFingerprint([
259
+ `userPrompt\u001f${String(params.user ?? '').trimEnd()}`,
260
+ ...userMetaMessages.map((message, index) => `meta:${index}\u001f${message.content}`),
261
+ ...conversationMessages.map((message, index) => `conversation:${index}\u001f${message.role}\u001f${message.content}`),
262
+ ...lateInjectionAttachments.map((item, index) => `late-attachment:${index}\u001f${serializeAttachmentForFingerprint(item)}`),
263
+ ]);
264
+ const cacheSafeText = [...systemSections, ...cacheSafeAttachments.map((item) => item.content)]
265
+ .filter(Boolean)
266
+ .join('\n\n');
267
+ const manager = getPromptCachingManager();
268
+ const cacheEligibility = !params.cacheSafeSurface?.contextHash
269
+ ? 'missing_context_hash'
270
+ : !cacheSafeText.trim()
271
+ ? 'empty_cache_safe_surface'
272
+ : !manager.shouldCache(estimateTokens(cacheSafeText))
273
+ ? 'below_min_tokens'
274
+ : 'eligible';
275
+ const cacheSafeSurface = {
276
+ systemSections,
277
+ attachments: cacheSafeAttachments,
278
+ contextHash: params.cacheSafeSurface?.contextHash,
279
+ namespace: params.cacheSafeSurface?.namespace,
280
+ mode: cacheMode,
281
+ cacheEligibility,
282
+ cacheSafeFingerprint,
283
+ lateInjectionFingerprint,
284
+ };
285
+ return {
286
+ systemSections,
287
+ userPrompt: String(params.user ?? ''),
288
+ userMetaMessages,
289
+ conversationMessages,
290
+ attachments,
291
+ providerHints: {
292
+ ...buildPromptCachingHints(cacheSafeSurface),
293
+ ...(params.providerHints ?? {}),
294
+ },
295
+ cacheSafeSurface,
296
+ };
297
+ }
298
+ export function materializeRequestEnvelope(envelope) {
299
+ const artifactSection = (() => {
300
+ const artifactAttachments = envelope.attachments.filter((item) => item.kind === 'artifact' && typeof item.artifactHandle === 'string' && item.artifactHandle);
301
+ if (artifactAttachments.length === 0)
302
+ return '';
303
+ const lines = [
304
+ '# Available Artifacts',
305
+ 'Use `artifact.read` with these handles when you need the full artifact contents.',
306
+ ];
307
+ for (const item of artifactAttachments) {
308
+ const suffix = [
309
+ item.mimeType ? `mime=${item.mimeType}` : undefined,
310
+ typeof item.size === 'number' ? `size=${item.size}` : undefined,
311
+ ]
312
+ .filter(Boolean)
313
+ .join(', ');
314
+ lines.push(`- ${item.label ?? item.key}: ${item.artifactHandle}${suffix ? ` (${suffix})` : ''}`);
315
+ }
316
+ return lines.join('\n');
317
+ })();
318
+ const out = [
319
+ {
320
+ role: 'system',
321
+ content: envelope.systemSections.filter(Boolean).join('\n\n'),
322
+ },
323
+ ];
324
+ out.push(...envelope.userMetaMessages);
325
+ out.push(...envelope.conversationMessages);
326
+ out.push({
327
+ role: 'user',
328
+ content: artifactSection.trim().length > 0
329
+ ? `${String(envelope.userPrompt ?? '')}\n\n${artifactSection}`
330
+ : String(envelope.userPrompt ?? ''),
331
+ });
332
+ return out;
333
+ }
334
+ //# sourceMappingURL=request-envelope.js.map
@@ -0,0 +1,35 @@
1
+ import { buildArtifactHintAttachments, buildRequestEnvelope, materializeRequestEnvelope, resolveRequestArtifactHints, } from './request-envelope.js';
2
+ export function buildSharedRequestEnvelope(args) {
3
+ const cacheSurface = {
4
+ namespace: args.defaultNamespace,
5
+ contextHash: args.contextHash,
6
+ };
7
+ const resolvedArtifactHints = resolveRequestArtifactHints({
8
+ artifactHints: args.artifactHints,
9
+ toolCallingAudit: args.toolCallingAudit,
10
+ previewProvider: args.previewProvider,
11
+ });
12
+ const envelope = buildRequestEnvelope({
13
+ system: args.systemPrompt,
14
+ user: args.userPrompt,
15
+ conversationContext: args.conversationContext,
16
+ attachments: [
17
+ ...(args.attachments ?? []),
18
+ ...buildArtifactHintAttachments(resolvedArtifactHints),
19
+ ],
20
+ providerHints: args.providerHints,
21
+ cacheSafeSurface: {
22
+ contextHash: cacheSurface.contextHash,
23
+ namespace: cacheSurface.namespace,
24
+ mode: 'cache_safe_only',
25
+ },
26
+ });
27
+ const baseMessages = materializeRequestEnvelope(envelope);
28
+ return {
29
+ cacheSurface,
30
+ resolvedArtifactHints,
31
+ envelope,
32
+ baseMessages,
33
+ };
34
+ }
35
+ //# sourceMappingURL=shared-request-assembly.js.map
@@ -33,12 +33,20 @@ export function mapAiSdkStreamPartToChunk(part) {
33
33
  return null;
34
34
  }
35
35
  switch (part.type) {
36
- case 'text-delta':
37
- case 'reasoning-delta':
38
- if (typeof part.text === 'string' && part.text) {
39
- return { role: 'assistant', source: 'provider', contentDelta: part.text };
36
+ case 'text-delta': {
37
+ const text = typeof part.text === 'string' ? part.text : part.delta;
38
+ if (typeof text === 'string' && text) {
39
+ return { role: 'assistant', source: 'provider', contentDelta: text };
40
40
  }
41
41
  return null;
42
+ }
43
+ case 'reasoning-delta': {
44
+ const text = typeof part.text === 'string' ? part.text : part.delta;
45
+ if (typeof text === 'string' && text) {
46
+ return { role: 'assistant', source: 'provider', reasoningDelta: text };
47
+ }
48
+ return null;
49
+ }
42
50
  case 'tool-call':
43
51
  return {
44
52
  role: 'assistant',
@@ -51,6 +59,7 @@ export function mapAiSdkStreamPartToChunk(part) {
51
59
  name: part.toolName || 'unknown',
52
60
  arguments: JSON.stringify(normalizeToolInput(part.input ?? {})),
53
61
  },
62
+ ...(part.providerMetadata ? { providerMetadata: part.providerMetadata } : {}),
54
63
  },
55
64
  ],
56
65
  };
@@ -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