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
@@ -1,25 +1,10 @@
1
- function toSafeConversationMessage(msg) {
2
- if (!msg || typeof msg !== 'object')
3
- return null;
4
- if (msg.role !== 'user' && msg.role !== 'assistant')
5
- return null;
6
- if (typeof msg.content !== 'string')
7
- return null;
8
- const content = msg.content.trimEnd();
9
- if (!content)
10
- return null;
11
- return { role: msg.role, content };
12
- }
1
+ import { buildSharedRequestEnvelope } from './shared-request-assembly.js';
13
2
  export function composeChatMessages(params) {
14
- const out = [{ role: 'system', content: String(params.system ?? '') }];
15
- if (Array.isArray(params.conversationContext)) {
16
- for (const msg of params.conversationContext) {
17
- const safe = toSafeConversationMessage(msg);
18
- if (safe)
19
- out.push(safe);
20
- }
21
- }
22
- out.push({ role: 'user', content: String(params.user ?? '') });
23
- return out;
3
+ return buildSharedRequestEnvelope({
4
+ defaultNamespace: 'chat',
5
+ systemPrompt: params.system,
6
+ userPrompt: params.user,
7
+ conversationContext: params.conversationContext,
8
+ }).baseMessages;
24
9
  }
25
10
  //# sourceMappingURL=message-composition.js.map
@@ -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
  };