@hassangameryt/freeflow 0.5.0 → 0.6.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 (284) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +148 -154
  5. package/capabilities/cognitive-routing/SKILL.md +229 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +6 -13
  9. package/hooks/freeflow-runtime-context.mjs +35 -91
  10. package/hooks/tests/check-runtime-context-hook.sh +56 -39
  11. package/package.json +24 -16
  12. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  13. package/pi-extension/dist/cognitive-routing/config.js +236 -0
  14. package/pi-extension/dist/cognitive-routing/controller.js +862 -0
  15. package/pi-extension/dist/cognitive-routing/history.js +439 -0
  16. package/pi-extension/dist/cognitive-routing/host.js +126 -0
  17. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  18. package/pi-extension/dist/cognitive-routing/tool.js +211 -0
  19. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  20. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  21. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  22. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  23. package/pi-extension/dist/context-virtualization/state.js +57 -0
  24. package/pi-extension/dist/context-virtualization/types.js +10 -0
  25. package/pi-extension/dist/conversation-history/passages.js +66 -0
  26. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  27. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  28. package/pi-extension/dist/conversation-history/search.js +56 -0
  29. package/pi-extension/dist/conversation-history/types.js +1 -0
  30. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  31. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  32. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  33. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  34. package/pi-extension/dist/freeflow-context/types.js +12 -0
  35. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  36. package/pi-extension/dist/index.js +516 -98
  37. package/pi-extension/dist/runtime/runtime-context.js +318 -271
  38. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  39. package/pi-extension/dist/settings/settings-tui.js +78 -0
  40. package/pi-extension/dist/settings/settings-ui.js +593 -563
  41. package/pi-extension/dist/ui/text-component.js +57 -0
  42. package/pi-extension/src/cognitive-routing/commands.ts +119 -0
  43. package/pi-extension/src/cognitive-routing/config.ts +307 -0
  44. package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
  45. package/pi-extension/src/cognitive-routing/history.ts +638 -0
  46. package/pi-extension/src/cognitive-routing/host.ts +172 -0
  47. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  48. package/pi-extension/src/cognitive-routing/tool.ts +272 -0
  49. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  50. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  51. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  52. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  53. package/pi-extension/src/context-virtualization/state.ts +79 -0
  54. package/pi-extension/src/context-virtualization/types.ts +92 -0
  55. package/pi-extension/src/conversation-history/passages.ts +81 -0
  56. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  57. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  58. package/pi-extension/src/conversation-history/search.ts +79 -0
  59. package/pi-extension/src/conversation-history/types.ts +31 -0
  60. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  61. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  62. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  63. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  64. package/pi-extension/src/freeflow-context/types.ts +70 -0
  65. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  66. package/pi-extension/src/index.ts +553 -101
  67. package/pi-extension/src/runtime/runtime-context.ts +339 -281
  68. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  69. package/pi-extension/src/settings/settings-tui.ts +93 -0
  70. package/pi-extension/src/settings/settings-ui.ts +700 -613
  71. package/pi-extension/src/ui/text-component.ts +66 -0
  72. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  73. package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
  74. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
  75. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  76. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  77. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  78. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
  79. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  80. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  81. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  82. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  83. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  84. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  85. package/pi-extension/tests/integration/pi-extension.test.js +927 -2048
  86. package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
  87. package/runtime/prompts/cognitive-routing.md +9 -0
  88. package/runtime/prompts/context-virtualization.md +3 -0
  89. package/runtime/prompts/conversation-history.md +3 -0
  90. package/runtime/prompts/core.md +13 -0
  91. package/runtime/prompts/interaction-contract.md +7 -0
  92. package/runtime/prompts/skills.md +65 -0
  93. package/skills/action-selection/SKILL.md +91 -0
  94. package/skills/bypass/SKILL.md +3 -2
  95. package/skills/commit-work/SKILL.md +1 -1
  96. package/skills/commit-work/references/staging-decisions.md +1 -1
  97. package/skills/decision-gate/SKILL.md +1 -1
  98. package/skills/design-for-depth/SKILL.md +14 -33
  99. package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
  100. package/skills/design-for-depth/references/interface-design-loop.md +1 -1
  101. package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
  102. package/skills/diagnose-failure/SKILL.md +20 -14
  103. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
  104. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
  105. package/skills/discuss/SKILL.md +34 -22
  106. package/skills/discuss/references/discussion-continuity.md +71 -0
  107. package/skills/evaluate-skill/SKILL.md +5 -5
  108. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
  109. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  110. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  111. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  112. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  113. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  114. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  115. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  116. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  117. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  118. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  119. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  120. package/skills/execute-work/SKILL.md +18 -15
  121. package/skills/execute-work/references/code-practices.md +1 -1
  122. package/skills/execute-work/references/execute-work-edges.md +42 -0
  123. package/skills/finish-branch/SKILL.md +2 -2
  124. package/skills/finish-branch/references/integration-options.md +1 -1
  125. package/skills/handoff/SKILL.md +3 -3
  126. package/skills/launch-work/SKILL.md +1 -1
  127. package/skills/launch-work/references/launch-readiness.md +2 -2
  128. package/skills/migration-work/SKILL.md +2 -2
  129. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  130. package/skills/mode-contract/SKILL.md +4 -4
  131. package/skills/release-work/SKILL.md +1 -1
  132. package/skills/release-work/references/release-evidence.md +1 -1
  133. package/skills/review-artifact/SKILL.md +28 -26
  134. package/skills/review-work/SKILL.md +28 -26
  135. package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
  136. package/skills/review-work/references/security-risk-lens.md +1 -1
  137. package/skills/setup-freeflow/SKILL.md +3 -1
  138. package/skills/setup-freeflow/references/activation-contract.md +2 -2
  139. package/skills/setup-freeflow/references/host-setup.md +2 -2
  140. package/skills/simplify-code/SKILL.md +1 -1
  141. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  142. package/skills/tdd/SKILL.md +18 -38
  143. package/skills/tdd/references/test-design.md +2 -2
  144. package/skills/track-work/SKILL.md +362 -58
  145. package/skills/track-work/scripts/lib/codec.mjs +570 -0
  146. package/skills/track-work/scripts/lib/commands.mjs +568 -0
  147. package/skills/track-work/scripts/lib/contract.mjs +303 -0
  148. package/skills/track-work/scripts/lib/model.mjs +506 -0
  149. package/skills/track-work/scripts/lib/operations.mjs +965 -0
  150. package/skills/track-work/scripts/lib/record-store.mjs +264 -0
  151. package/skills/track-work/scripts/lib/result.mjs +37 -0
  152. package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
  153. package/skills/track-work/scripts/lib/schema.mjs +277 -0
  154. package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
  155. package/skills/track-work/scripts/lib/views.mjs +443 -0
  156. package/skills/track-work/scripts/lib/workspace.mjs +134 -0
  157. package/skills/track-work/scripts/working-record.mjs +122 -0
  158. package/skills/verify-work/SKILL.md +14 -2
  159. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  160. package/skills/verify-work/references/integration-evidence.md +1 -1
  161. package/skills/verify-work/references/performance-evidence.md +1 -1
  162. package/skills/workflow/SKILL.md +105 -53
  163. package/skills/workflow/references/domain-skill-composition.md +22 -42
  164. package/skills/write-plan/SKILL.md +1 -1
  165. package/skills/write-skill/SKILL.md +3 -2
  166. package/skills/write-skill/references/progressive-disclosure.md +17 -3
  167. package/capabilities/output-router/SKILL.md +0 -206
  168. package/capabilities/output-router/references/safety-policy.md +0 -40
  169. package/capabilities/output-router/references/setup.md +0 -110
  170. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  171. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  172. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  173. package/pi-extension/dist/output-router/renderers.js +0 -1023
  174. package/pi-extension/dist/output-router/router-tools.js +0 -554
  175. package/pi-extension/dist/output-router/schemas.js +0 -383
  176. package/pi-extension/dist/output-router/status.js +0 -744
  177. package/pi-extension/dist/output-router/utils.js +0 -502
  178. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  179. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  180. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  181. package/pi-extension/src/output-router/renderers.ts +0 -1108
  182. package/pi-extension/src/output-router/router-tools.ts +0 -579
  183. package/pi-extension/src/output-router/schemas.ts +0 -387
  184. package/pi-extension/src/output-router/status.ts +0 -791
  185. package/pi-extension/src/output-router/utils.ts +0 -554
  186. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  187. package/pi-extension/tests/output-router/search.test.js +0 -722
  188. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  189. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  190. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  191. package/router/dist/benchmarks/benchmarks.js +0 -979
  192. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  193. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  194. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  195. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  196. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  197. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  198. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  199. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  200. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  201. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  202. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  203. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  204. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  205. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  206. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  207. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  208. package/router/dist/config/config.d.ts +0 -103
  209. package/router/dist/config/config.js +0 -632
  210. package/router/dist/config/router-contract.d.ts +0 -10
  211. package/router/dist/config/router-contract.js +0 -54
  212. package/router/dist/config/schema.d.ts +0 -27
  213. package/router/dist/config/schema.js +0 -648
  214. package/router/dist/config/types.d.ts +0 -573
  215. package/router/dist/config/types.js +0 -41
  216. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  217. package/router/dist/evidence/bounded-evidence.js +0 -323
  218. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  219. package/router/dist/evidence/evidence-range-selector.js +0 -232
  220. package/router/dist/evidence/evidence-search.d.ts +0 -27
  221. package/router/dist/evidence/evidence-search.js +0 -490
  222. package/router/dist/evidence/evidence.d.ts +0 -38
  223. package/router/dist/evidence/evidence.js +0 -200
  224. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  225. package/router/dist/evidence/failure-contracts.js +0 -166
  226. package/router/dist/evidence/line-ranges.d.ts +0 -24
  227. package/router/dist/evidence/line-ranges.js +0 -22
  228. package/router/dist/experiments/local-index.d.ts +0 -64
  229. package/router/dist/experiments/local-index.js +0 -581
  230. package/router/dist/index.d.ts +0 -30
  231. package/router/dist/index.js +0 -32
  232. package/router/dist/local/local-traversal.d.ts +0 -18
  233. package/router/dist/local/local-traversal.js +0 -334
  234. package/router/dist/processing/engine.d.ts +0 -132
  235. package/router/dist/processing/engine.js +0 -533
  236. package/router/dist/processing/reducers.d.ts +0 -280
  237. package/router/dist/processing/reducers.js +0 -1673
  238. package/router/dist/processing/renderers.d.ts +0 -31
  239. package/router/dist/processing/renderers.js +0 -195
  240. package/router/dist/processing/scripts.d.ts +0 -91
  241. package/router/dist/processing/scripts.js +0 -386
  242. package/router/dist/repo/repo-traversal.d.ts +0 -18
  243. package/router/dist/repo/repo-traversal.js +0 -245
  244. package/router/dist/routing/observed-reducers.d.ts +0 -41
  245. package/router/dist/routing/observed-reducers.js +0 -661
  246. package/router/dist/routing/observed-routing.d.ts +0 -26
  247. package/router/dist/routing/observed-routing.js +0 -378
  248. package/router/dist/routing/parsers.d.ts +0 -16
  249. package/router/dist/routing/parsers.js +0 -521
  250. package/router/dist/routing/run-filters.d.ts +0 -60
  251. package/router/dist/routing/run-filters.js +0 -238
  252. package/router/dist/routing/run-reducers.d.ts +0 -49
  253. package/router/dist/routing/run-reducers.js +0 -122
  254. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  255. package/router/dist/sandbox/adapter-roots.js +0 -66
  256. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  257. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  258. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  259. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  260. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  261. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  262. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  263. package/router/dist/sandbox/script-sandbox.js +0 -337
  264. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  265. package/router/dist/setup/script-transform-adapters.js +0 -278
  266. package/router/dist/tools/batch.d.ts +0 -33
  267. package/router/dist/tools/batch.js +0 -586
  268. package/router/dist/tools/run.d.ts +0 -62
  269. package/router/dist/tools/run.js +0 -1304
  270. package/router/dist/tools/search.d.ts +0 -66
  271. package/router/dist/tools/search.js +0 -1364
  272. package/router/dist/transform/engine.d.ts +0 -168
  273. package/router/dist/transform/engine.js +0 -2072
  274. package/router/dist/vault/vault-index.d.ts +0 -125
  275. package/router/dist/vault/vault-index.js +0 -416
  276. package/router/dist/vault/vault.d.ts +0 -138
  277. package/router/dist/vault/vault.js +0 -630
  278. package/runtime/interaction-contract.md +0 -16
  279. package/skills/discuss/references/checkpoints.md +0 -66
  280. package/skills/execute-work/references/execution-loop.md +0 -77
  281. package/skills/track-work/references/working-record-schema.md +0 -378
  282. package/skills/workflow/references/workflow-loop.md +0 -92
  283. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
  284. /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
@@ -1,1304 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import {
3
- commandOutputFingerprints,
4
- createVault,
5
- findExactDuplicateCommandOutput,
6
- storeCommandOutput,
7
- storeMetadataOutput,
8
- storeTextOutput,
9
- } from "../vault/vault.js";
10
- import { DEFAULT_ROUTER_THRESHOLDS, DEFAULT_STORAGE_POLICY } from "../config/config.js";
11
- import { executeSandboxedScriptOperation, freeflowTransform, validateTransformInput } from "../transform/engine.js";
12
- import { assembleTextEvidence, byteLength, countLines } from "../evidence/evidence.js";
13
- import { parseCommandOutput } from "../routing/parsers.js";
14
- import { applyRunOutputFilters, hasRunOutputFilters, normalizeRunOutputFilters } from "../routing/run-filters.js";
15
- import { parserWithReducer, reducerImportantLines, selectRunReducerRoute } from "../routing/run-reducers.js";
16
- export async function freeflowRun(options, runner) {
17
- const preserve = options.preserve ?? "important";
18
- const producerValidation = normalizeRunProducer(options);
19
- if (!producerValidation.ok) {
20
- return runProducerValidationFailureResult({
21
- preserve,
22
- message: producerValidation.message,
23
- path: producerValidation.path,
24
- });
25
- }
26
- const runProducer = producerValidation.producer;
27
- const command = runProducer.command;
28
- const validationProducer =
29
- runProducer.kind === "script"
30
- ? { kind: "script", name: `script:${runProducer.script.language}` }
31
- : runProducer.producer;
32
- const filterValidation = normalizeRunOutputFilters(options.filters);
33
- if (!filterValidation.ok) {
34
- return commandFilterValidationFailureResult({
35
- command,
36
- producer: validationProducer,
37
- preserve,
38
- message: filterValidation.message,
39
- path: filterValidation.path,
40
- });
41
- }
42
- const scriptFilterValidation = normalizeRunScriptFilter(options.scriptFilter);
43
- if (!scriptFilterValidation.ok) {
44
- return commandScriptFilterValidationFailureResult({
45
- command,
46
- producer: validationProducer,
47
- preserve,
48
- message: scriptFilterValidation.message,
49
- path: scriptFilterValidation.path,
50
- });
51
- }
52
- const filters = hasRunOutputFilters(filterValidation.filters) ? filterValidation.filters : undefined;
53
- const scriptFilter = scriptFilterValidation.scriptFilter;
54
- const thresholds = { ...DEFAULT_ROUTER_THRESHOLDS, ...options.thresholds };
55
- const vaultOptions = {};
56
- if (options.vaultRoot !== undefined) {
57
- vaultOptions.root = options.vaultRoot;
58
- }
59
- if (options.vaultRetention !== undefined) {
60
- vaultOptions.retention = options.vaultRetention;
61
- }
62
- const vault = createVault(vaultOptions);
63
- let runResult;
64
- let producer =
65
- runProducer.kind === "command"
66
- ? runProducer.producer
67
- : { kind: "script", name: `script:${runProducer.script.language}` };
68
- let scriptProducer;
69
- if (runProducer.kind === "command") {
70
- try {
71
- const runRequest = { command: runProducer.command };
72
- if (options.cwd !== undefined) {
73
- runRequest.cwd = options.cwd;
74
- }
75
- if (options.timeoutMs !== undefined) {
76
- runRequest.timeoutMs = options.timeoutMs;
77
- }
78
- runResult = await runner.run(runRequest);
79
- } catch (error) {
80
- const message = error instanceof Error ? error.message : String(error);
81
- return {
82
- toolStatus: "error",
83
- decisionId: decisionId("run-error", commandText(command), message),
84
- preserve,
85
- outputId: "",
86
- execution: { status: "failed", exitCode: null },
87
- producer,
88
- persistence: { status: "not_persisted", recoverability: "none" },
89
- routing: {
90
- status: "failed",
91
- route: "run",
92
- reason: `Adapter-provided runner failed before command output could be captured: ${message}`,
93
- },
94
- recovery: {
95
- how: "No command output was captured. Retry through the host-approved runner or use the host-native shell tool if direct raw behavior is required.",
96
- },
97
- };
98
- }
99
- } else {
100
- const scriptExecutionOptions = {
101
- operation: runScriptProducerOperation(runProducer.script),
102
- scriptSandboxAdapters: options.scriptSandboxAdapters ?? [],
103
- };
104
- const limits = scriptProducerLimits(runProducer.script, options.timeoutMs);
105
- if (limits !== undefined) {
106
- Object.assign(scriptExecutionOptions, { limits });
107
- }
108
- if (options.scriptTransform !== undefined) {
109
- Object.assign(scriptExecutionOptions, { scriptTransform: options.scriptTransform });
110
- }
111
- const scriptExecution = await executeSandboxedScriptOperation(scriptExecutionOptions);
112
- scriptProducer = runScriptProducerMetadata(runProducer.script, scriptExecution);
113
- producer = scriptProducerDescriptor(runProducer.script, scriptExecution);
114
- if (!scriptExecution.ok) {
115
- return scriptProducerExecutionFailureResult({
116
- command,
117
- preserve,
118
- producer,
119
- scriptProducer,
120
- execution: scriptExecution,
121
- });
122
- }
123
- runResult = scriptExecutionResult(scriptExecution);
124
- }
125
- const combined = runResult.combined ?? combineOutputSections(runResult.stdout, runResult.stderr);
126
- let record;
127
- let duplicate;
128
- const fingerprints = commandOutputFingerprints({
129
- command,
130
- stdout: runResult.stdout,
131
- stderr: runResult.stderr,
132
- combined,
133
- executionStatus: runResult.executionStatus,
134
- exitCode: runResult.exitCode,
135
- ...(options.cwd !== undefined ? { cwd: options.cwd } : {}),
136
- });
137
- try {
138
- duplicate = await findExactDuplicateCommandOutput(vault, {
139
- sessionId: options.sessionId,
140
- fingerprints,
141
- });
142
- } catch {
143
- duplicate = undefined;
144
- }
145
- const storagePolicy = options.storagePolicy ?? DEFAULT_STORAGE_POLICY;
146
- const storageParser = parseCommandOutput({
147
- command,
148
- executionStatus: runResult.executionStatus,
149
- exitCode: runResult.exitCode,
150
- stdout: runResult.stdout,
151
- stderr: runResult.stderr,
152
- combined,
153
- ...(options.goal !== undefined ? { goal: options.goal } : {}),
154
- });
155
- const reducerRoute = selectRunReducerRoute({
156
- command,
157
- ...(options.goal !== undefined ? { goal: options.goal } : {}),
158
- executionStatus: runResult.executionStatus,
159
- stdout: runResult.stdout,
160
- stderr: runResult.stderr,
161
- combined,
162
- preserve,
163
- thresholds,
164
- hasFilters: filters !== undefined,
165
- hasScriptFilter: scriptFilter !== undefined,
166
- });
167
- const storageInput = {
168
- policy: storagePolicy,
169
- command,
170
- producerKind: runProducer.kind,
171
- preserve,
172
- executionStatus: runResult.executionStatus,
173
- outputBytes: byteLength(combined),
174
- outputLines: countLines(combined),
175
- thresholds,
176
- parserName: storageParser.parser.name,
177
- hasFilters: filters !== undefined,
178
- hasScriptFilter: scriptFilter !== undefined,
179
- hasReducer: reducerRoute.status === "selected",
180
- };
181
- if (options.goal !== undefined) {
182
- Object.assign(storageInput, { goal: options.goal });
183
- }
184
- if (duplicate !== undefined) {
185
- Object.assign(storageInput, { duplicate });
186
- }
187
- const storageDecision = decideCommandStorage(storageInput);
188
- const execution = {
189
- status: runResult.executionStatus,
190
- exitCode: runResult.exitCode,
191
- };
192
- if (runResult.durationMs !== undefined) {
193
- Object.assign(execution, { durationMs: runResult.durationMs });
194
- }
195
- try {
196
- const storeOptions = {
197
- sessionId: options.sessionId,
198
- command,
199
- stdout: runResult.stdout,
200
- stderr: runResult.stderr,
201
- combined,
202
- executionStatus: runResult.executionStatus,
203
- exitCode: runResult.exitCode,
204
- decisionIds: [decisionId("run", commandText(command), options.sessionId)],
205
- producer,
206
- };
207
- if (options.cwd !== undefined) {
208
- Object.assign(storeOptions, { cwd: options.cwd });
209
- }
210
- if (runResult.durationMs !== undefined) {
211
- Object.assign(storeOptions, { durationMs: runResult.durationMs });
212
- }
213
- const metadataStoreOptions = {
214
- vault,
215
- sessionId: options.sessionId,
216
- command,
217
- runResult,
218
- combined,
219
- fingerprints,
220
- storageDecision,
221
- parserName: storageParser.parser.name,
222
- producer,
223
- };
224
- if (options.cwd !== undefined) {
225
- Object.assign(metadataStoreOptions, { cwd: options.cwd });
226
- }
227
- record =
228
- storageDecision.mode === "exact"
229
- ? await storeCommandOutput(vault, storeOptions)
230
- : await storeCommandMetadataOutput(metadataStoreOptions);
231
- const routeOptions = {
232
- outputId: record.outputId,
233
- command,
234
- executionStatus: runResult.executionStatus,
235
- exitCode: runResult.exitCode,
236
- stdout: runResult.stdout,
237
- stderr: runResult.stderr,
238
- combined,
239
- preserve,
240
- thresholds,
241
- storage: storageDecision,
242
- };
243
- if (filters !== undefined) {
244
- routeOptions.filters = filters;
245
- }
246
- if (options.goal !== undefined) {
247
- routeOptions.goal = options.goal;
248
- }
249
- if (reducerRoute.status === "selected") {
250
- routeOptions.reducer = reducerRoute;
251
- }
252
- let routing;
253
- const duplicateForRouting =
254
- duplicate && preserve !== "full" && filters === undefined && scriptFilter === undefined ? duplicate : undefined;
255
- if (duplicateForRouting) {
256
- routing = routeDuplicateCommandOutput({
257
- outputId: record.outputId,
258
- duplicate: duplicateForRouting,
259
- storage: storageDecision,
260
- command,
261
- executionStatus: runResult.executionStatus,
262
- exitCode: runResult.exitCode,
263
- });
264
- } else if (scriptFilter) {
265
- const scriptRouteOptions = {
266
- routeOptions,
267
- rawRecord: record,
268
- sessionId: options.sessionId,
269
- scriptFilter,
270
- scriptSandboxAdapters: options.scriptSandboxAdapters ?? [],
271
- };
272
- if (options.vaultRoot !== undefined) {
273
- Object.assign(scriptRouteOptions, { vaultRoot: options.vaultRoot });
274
- }
275
- if (options.vaultRetention !== undefined) {
276
- Object.assign(scriptRouteOptions, { vaultRetention: options.vaultRetention });
277
- }
278
- if (options.scriptTransform !== undefined) {
279
- Object.assign(scriptRouteOptions, { scriptTransform: options.scriptTransform });
280
- }
281
- routing = await routeScriptFilteredOutput(scriptRouteOptions);
282
- } else {
283
- if (
284
- routeOptions.reducer?.status === "selected" &&
285
- record.kind === "command" &&
286
- storageDecision.mode === "exact"
287
- ) {
288
- routeOptions.reducerRecord = await storeReducerOutput({
289
- vault,
290
- sessionId: options.sessionId,
291
- rawRecord: record,
292
- reducer: routeOptions.reducer,
293
- });
294
- }
295
- routing = routeCommandOutput(routeOptions);
296
- }
297
- return {
298
- toolStatus: "ok",
299
- decisionId: routing.decisionId,
300
- outputId: record.outputId,
301
- recordId: record.recordId,
302
- preserve,
303
- execution,
304
- producer: record.producer,
305
- persistence: record.persistence,
306
- ...(routing.lineage !== undefined
307
- ? { lineage: routing.lineage }
308
- : record.lineage !== undefined
309
- ? { lineage: record.lineage }
310
- : {}),
311
- ...(routing.failure !== undefined ? { failure: routing.failure } : {}),
312
- ...(routing.transformExecution !== undefined ? { transformExecution: routing.transformExecution } : {}),
313
- ...(routing.evidence !== undefined ? { evidence: routing.evidence } : {}),
314
- routing: {
315
- status: routing.routingStatus,
316
- route: "run",
317
- reason: routing.reason,
318
- },
319
- summary: routing.summary,
320
- importantLines: routing.importantLines,
321
- parser: routing.parser,
322
- ...(routing.filters !== undefined ? { filters: routing.filters } : {}),
323
- ...(scriptProducer !== undefined ? { scriptProducer } : {}),
324
- ...(routing.scriptFilter !== undefined ? { scriptFilter: routing.scriptFilter } : {}),
325
- ...(routing.reducer !== undefined ? { reducer: routing.reducer } : {}),
326
- recovery: routing.recovery ?? commandRecoveryHint(record.outputId, storageDecision),
327
- };
328
- } catch (error) {
329
- const message = error instanceof Error ? error.message : String(error);
330
- return commandRoutingFailureResult({
331
- command,
332
- outputId: record?.outputId ?? "",
333
- record,
334
- preserve,
335
- execution,
336
- stdout: runResult.stdout,
337
- stderr: runResult.stderr,
338
- combined,
339
- errorMessage: message,
340
- storage: storageDecision,
341
- producer,
342
- ...(scriptProducer !== undefined ? { scriptProducer } : {}),
343
- });
344
- }
345
- }
346
- function routeDuplicateCommandOutput(options) {
347
- const currentVaultText =
348
- options.storage.mode === "duplicate-metadata"
349
- ? `Current run stored metadata-only as outputId=${options.outputId}; exact raw output remains recoverable from prior outputId=${options.duplicate.outputId}.`
350
- : options.outputId === options.duplicate.outputId
351
- ? `Current run resolved to the same outputId=${options.outputId}.`
352
- : `Current raw output was vaulted as outputId=${options.outputId}.`;
353
- const statusText = `executionStatus=${options.executionStatus} exitCode=${options.exitCode}`;
354
- return {
355
- decisionId: decisionId("run-route", options.outputId, "exact-duplicate", options.duplicate.outputId),
356
- routingStatus: "partial",
357
- reason: `Exact duplicate run output matched previous outputId=${options.duplicate.outputId} by exact output hash and producer/cwd/status fingerprint (${statusText}); returning a compact duplicate note instead of re-injecting repeated output. ${currentVaultText}`,
358
- summary: `Run output is an exact duplicate of previous outputId=${options.duplicate.outputId}. ${currentVaultText}`,
359
- importantLines: [],
360
- parser: {
361
- name: "duplicate-output",
362
- confidence: 1,
363
- fidelity: "exact",
364
- compressed: true,
365
- counts: { duplicateOutputs: 1 },
366
- },
367
- };
368
- }
369
- function commandRecoveryHint(outputId, storage) {
370
- if (storage.mode === "exact") {
371
- return {
372
- how: `Use freeflow_search with source.kind=vault and outputId=${outputId} to recover exact run output.`,
373
- outputId,
374
- };
375
- }
376
- if (storage.mode === "duplicate-metadata" && storage.exactRecoveryOutputId) {
377
- return {
378
- how: `Current run record is metadata-only outputId=${outputId}; exact duplicate raw output is recoverable with freeflow_search source.kind=vault outputId=${storage.exactRecoveryOutputId}.`,
379
- outputId: storage.exactRecoveryOutputId,
380
- };
381
- }
382
- return {
383
- how: `Current run record is metadata-only outputId=${outputId}; exact raw output was not vaulted by storagePolicy=${storage.policy}. Rerun with preserve=full or a verification/diagnosis goal if exact recovery is required.`,
384
- };
385
- }
386
- function commandStorageReason(storage) {
387
- if (!storage || storage.mode === "exact") {
388
- return "raw output was vaulted";
389
- }
390
- if (storage.mode === "duplicate-metadata" && storage.exactRecoveryOutputId) {
391
- return `current output was stored as metadata-only with exact duplicate recovery from outputId=${storage.exactRecoveryOutputId}`;
392
- }
393
- return `current output was stored as metadata-only by storagePolicy=${storage.policy}; exact raw output was not vaulted`;
394
- }
395
- function decideCommandStorage(options) {
396
- const exactnessSensitive = commandOutputIsExactnessSensitive(options);
397
- if (options.policy === "store-everything") {
398
- return {
399
- policy: options.policy,
400
- mode: "exact",
401
- exactnessSensitive,
402
- reason: "storagePolicy=store-everything stores every run output exactly.",
403
- };
404
- }
405
- if (!exactnessSensitive) {
406
- return {
407
- policy: options.policy,
408
- mode: "metadata-only",
409
- exactnessSensitive,
410
- reason: "Small non-sensitive successful run output is stored metadata-only by storagePolicy=hybrid-dedupe.",
411
- };
412
- }
413
- if (options.duplicate?.outputId && !options.hasScriptFilter) {
414
- return {
415
- policy: options.policy,
416
- mode: "duplicate-metadata",
417
- exactnessSensitive,
418
- exactRecoveryOutputId: options.duplicate.outputId,
419
- reason: `Exactness-sensitive run output duplicates prior exact outputId=${options.duplicate.outputId}; current record stores metadata only.`,
420
- };
421
- }
422
- return {
423
- policy: options.policy,
424
- mode: "exact",
425
- exactnessSensitive,
426
- reason: "Run output is exactness-sensitive or large/noisy; storagePolicy=hybrid-dedupe stores it exactly.",
427
- };
428
- }
429
- function commandOutputIsExactnessSensitive(options) {
430
- if (
431
- options.preserve === "full" ||
432
- options.executionStatus !== "success" ||
433
- options.hasFilters ||
434
- options.hasScriptFilter ||
435
- options.hasReducer
436
- ) {
437
- return true;
438
- }
439
- if (options.producerKind === "script") {
440
- return true;
441
- }
442
- if (
443
- options.outputBytes > options.thresholds.largeOutputBytes ||
444
- options.outputLines > options.thresholds.largeOutputLines
445
- ) {
446
- return true;
447
- }
448
- const goalText = `${options.goal ?? ""} ${commandText(options.command)}`.toLowerCase();
449
- if (/\b(verify|verification|test|tests|lint|typecheck|type-check|diagnos|debug|build|ci)\b/.test(goalText)) {
450
- return true;
451
- }
452
- return options.parserName !== "generic";
453
- }
454
- async function storeCommandMetadataOutput(options) {
455
- const metadata = {
456
- storagePolicy: options.storageDecision.policy,
457
- storageDecision: options.storageDecision.mode,
458
- storageReason: options.storageDecision.reason,
459
- command: options.command,
460
- executionStatus: options.runResult.executionStatus,
461
- exitCode: options.runResult.exitCode,
462
- parserName: options.parserName,
463
- exactnessSensitive: options.storageDecision.exactnessSensitive,
464
- byteCounts: {
465
- stdout: byteLength(options.runResult.stdout),
466
- stderr: byteLength(options.runResult.stderr),
467
- combined: byteLength(options.combined),
468
- },
469
- lineCounts: {
470
- stdout: countLines(options.runResult.stdout),
471
- stderr: countLines(options.runResult.stderr),
472
- combined: countLines(options.combined),
473
- },
474
- fingerprints: options.fingerprints,
475
- };
476
- if (options.cwd !== undefined) {
477
- metadata.cwd = options.cwd;
478
- }
479
- if (options.runResult.durationMs !== undefined) {
480
- metadata.durationMs = options.runResult.durationMs;
481
- }
482
- if (options.storageDecision.exactRecoveryOutputId !== undefined) {
483
- metadata.exactRecoveryOutputId = options.storageDecision.exactRecoveryOutputId;
484
- }
485
- return storeMetadataOutput(options.vault, {
486
- sessionId: options.sessionId,
487
- sourceKind: options.producer.kind === "script" ? "script" : "other",
488
- rawLineCount: countLines(options.combined),
489
- rawByteCount: byteLength(options.combined),
490
- rawSha256: hash(options.combined),
491
- decisionIds: [
492
- decisionId("run-metadata", commandText(options.command), options.sessionId, options.storageDecision.mode),
493
- ],
494
- producer: options.producer,
495
- metadata,
496
- });
497
- }
498
- function normalizeRunProducer(options) {
499
- const hasCommand = options.command !== undefined && options.command !== null;
500
- const hasScript = options.script !== undefined && options.script !== null;
501
- if (hasCommand === hasScript) {
502
- return {
503
- ok: false,
504
- path: "$",
505
- message: hasCommand
506
- ? "freeflow_run accepts either command or script, not both."
507
- : "freeflow_run requires either command or script.",
508
- };
509
- }
510
- if (hasCommand) {
511
- const command = options.command;
512
- if (typeof command === "string") {
513
- if (command.trim().length === 0) {
514
- return { ok: false, path: "$.command", message: "Expected non-empty shell command string." };
515
- }
516
- return { ok: true, producer: { kind: "command", command, producer: { kind: "command" } } };
517
- }
518
- if (
519
- Array.isArray(command) &&
520
- command.length > 0 &&
521
- command.every((part) => typeof part === "string" && part.length > 0)
522
- ) {
523
- return { ok: true, producer: { kind: "command", command, producer: { kind: "command" } } };
524
- }
525
- return {
526
- ok: false,
527
- path: "$.command",
528
- message: "Expected non-empty shell command string or command argument array.",
529
- };
530
- }
531
- const scriptValidation = normalizeRunScriptProducer(options.script);
532
- if (!scriptValidation.ok) {
533
- return scriptValidation;
534
- }
535
- return {
536
- ok: true,
537
- producer: {
538
- kind: "script",
539
- command: scriptCommandDescriptor(scriptValidation.script),
540
- script: scriptValidation.script,
541
- },
542
- };
543
- }
544
- function normalizeRunScriptProducer(input) {
545
- if (!isRecord(input)) {
546
- return { ok: false, path: "$.script", message: "freeflow_run script must be an object." };
547
- }
548
- const operation = { kind: "script" };
549
- if (input.language !== undefined) {
550
- operation.language = input.language;
551
- }
552
- if (input.code !== undefined) {
553
- operation.code = input.code;
554
- }
555
- if (input.label !== undefined) {
556
- operation.label = input.label;
557
- }
558
- const candidate = {
559
- sources: runScriptFilterSources("ffout_run_script_producer_validation"),
560
- operation,
561
- };
562
- if (input.limits !== undefined) {
563
- candidate.limits = input.limits;
564
- }
565
- const validation = validateTransformInput(candidate);
566
- if (!validation.ok) {
567
- const issue = validation.issues[0] ?? { path: "$.script", message: "Invalid script producer." };
568
- return {
569
- ok: false,
570
- path: issue.path.replace(/^\$\.operation/, "$.script").replace(/^\$\.limits/, "$.script.limits"),
571
- message: issue.message,
572
- };
573
- }
574
- const script = {
575
- language: input.language,
576
- code: input.code,
577
- };
578
- if (input.label !== undefined) {
579
- script.label = input.label;
580
- }
581
- if (input.limits !== undefined) {
582
- script.limits = input.limits;
583
- }
584
- return { ok: true, script };
585
- }
586
- function runScriptProducerOperation(script) {
587
- const operation = {
588
- kind: "script",
589
- language: script.language,
590
- code: script.code,
591
- };
592
- if (script.label !== undefined) {
593
- operation.label = script.label;
594
- }
595
- return operation;
596
- }
597
- function scriptProducerLimits(script, timeoutMs) {
598
- if (timeoutMs === undefined || script.limits?.timeoutMs !== undefined) {
599
- return script.limits;
600
- }
601
- return { ...(script.limits ?? {}), timeoutMs };
602
- }
603
- function scriptExecutionResult(execution) {
604
- const result = execution.result;
605
- return {
606
- stdout: result.stdout ?? "",
607
- stderr: result.stderr ?? "",
608
- executionStatus: scriptExecutionStatus(result.status),
609
- exitCode: result.exitCode ?? (result.status === "success" ? 0 : null),
610
- combined: combineOutputSections(result.stdout ?? "", result.stderr ?? ""),
611
- ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
612
- };
613
- }
614
- function scriptExecutionStatus(status) {
615
- if (status === "success") {
616
- return "success";
617
- }
618
- if (status === "timed_out") {
619
- return "timed_out";
620
- }
621
- return "failed";
622
- }
623
- function runScriptProducerMetadata(script, execution) {
624
- const metadata = {
625
- status: execution.ok
626
- ? execution.result.status
627
- : execution.executionStatus === "unavailable"
628
- ? "unavailable"
629
- : "failed",
630
- language: script.language,
631
- policy: "sandboxed",
632
- rawScriptPersistence: "disabled",
633
- codeSha256: scriptCodeSha256(script),
634
- limits: execution.limits,
635
- operation: execution.operation,
636
- };
637
- if (script.label !== undefined) {
638
- metadata.label = script.label;
639
- }
640
- if (execution.ok) {
641
- metadata.adapterId = execution.adapterId;
642
- metadata.adapterVersion = execution.adapterVersion;
643
- if (execution.runtime !== undefined) {
644
- metadata.runtime = execution.runtime;
645
- }
646
- metadata.stdoutBytes = byteLength(execution.result.stdout ?? "");
647
- metadata.stderrBytes = byteLength(execution.result.stderr ?? "");
648
- if (execution.result.exitCode !== undefined) {
649
- metadata.exitCode = execution.result.exitCode;
650
- }
651
- if (execution.result.durationMs !== undefined) {
652
- metadata.durationMs = execution.result.durationMs;
653
- }
654
- } else {
655
- if (execution.adapterId !== undefined) {
656
- metadata.adapterId = execution.adapterId;
657
- }
658
- if (execution.adapterVersion !== undefined) {
659
- metadata.adapterVersion = execution.adapterVersion;
660
- }
661
- metadata.failure = { kind: execution.failureKind, message: execution.message };
662
- metadata.transformExecution = {
663
- status: execution.executionStatus,
664
- failureKind: execution.failureKind,
665
- message: execution.message,
666
- };
667
- }
668
- return metadata;
669
- }
670
- function scriptProducerDescriptor(script, execution) {
671
- const descriptor = {
672
- kind: "script",
673
- name: script.label ?? `script:${script.language}`,
674
- };
675
- if (execution.ok) {
676
- descriptor.adapter = execution.adapterId;
677
- } else if (execution.adapterId !== undefined) {
678
- descriptor.adapter = execution.adapterId;
679
- }
680
- return descriptor;
681
- }
682
- function scriptCommandDescriptor(script) {
683
- const label = script.label ? ` label=${JSON.stringify(script.label)}` : "";
684
- return `script:${script.language} code=${scriptCodeSha256(script)}${label}`;
685
- }
686
- function scriptCodeSha256(script) {
687
- return `sha256_${hash(script.code)}`;
688
- }
689
- function runProducerValidationFailureResult(options) {
690
- return {
691
- toolStatus: "error",
692
- decisionId: decisionId("run-producer-validation", options.path, options.message),
693
- outputId: "",
694
- preserve: options.preserve,
695
- execution: { status: "failed", exitCode: null },
696
- producer: { kind: "other", name: "freeflow_run" },
697
- persistence: { status: "not_persisted", recoverability: "none" },
698
- routing: {
699
- status: "failed",
700
- route: "run",
701
- reason: `Invalid freeflow_run producer at ${options.path}: ${options.message}`,
702
- },
703
- summary: "Run producer was not executed because freeflow_run input was invalid.",
704
- recovery: {
705
- how: "No command or script output was captured. Fix the freeflow_run producer input and rerun.",
706
- },
707
- };
708
- }
709
- function scriptProducerExecutionFailureResult(options) {
710
- return {
711
- toolStatus: "error",
712
- decisionId: decisionId(
713
- "run-script-producer",
714
- commandText(options.command),
715
- options.execution.failureKind,
716
- options.execution.message,
717
- ),
718
- outputId: "",
719
- preserve: options.preserve,
720
- execution: { status: "failed", exitCode: null },
721
- producer: options.producer,
722
- scriptProducer: options.scriptProducer,
723
- failure: {
724
- kind: options.execution.failureKind,
725
- message: options.execution.message,
726
- },
727
- transformExecution: {
728
- status: options.execution.executionStatus,
729
- failureKind: options.execution.failureKind,
730
- message: options.execution.message,
731
- },
732
- persistence: { status: "not_persisted", recoverability: "none" },
733
- routing: {
734
- status: "failed",
735
- route: "run",
736
- reason: `Sandboxed script producer could not execute: ${options.execution.message}`,
737
- },
738
- summary: "Script producer was not executed; no stdout/stderr was captured.",
739
- recovery: {
740
- how: "No script output was captured. Enable scriptTransform with a proof-backed sandbox adapter, fix the script producer input, or use a shell command when host execution is intentionally required.",
741
- },
742
- };
743
- }
744
- function commandFilterValidationFailureResult(options) {
745
- return {
746
- toolStatus: "error",
747
- decisionId: decisionId("run-filter-validation", commandText(options.command), options.path, options.message),
748
- outputId: "",
749
- preserve: options.preserve,
750
- execution: { status: "failed", exitCode: null },
751
- producer: options.producer,
752
- persistence: { status: "not_persisted", recoverability: "none" },
753
- routing: {
754
- status: "failed",
755
- route: "run",
756
- reason: `Invalid freeflow_run filters at ${options.path}: ${options.message}`,
757
- },
758
- summary: "Run producer was not executed because freeflow_run filters were invalid.",
759
- recovery: {
760
- how: "No command or script output was captured. Fix the declarative filter and rerun through freeflow_run.",
761
- },
762
- };
763
- }
764
- function commandScriptFilterValidationFailureResult(options) {
765
- return {
766
- toolStatus: "error",
767
- decisionId: decisionId("run-script-filter-validation", commandText(options.command), options.path, options.message),
768
- outputId: "",
769
- preserve: options.preserve,
770
- execution: { status: "failed", exitCode: null },
771
- producer: options.producer,
772
- persistence: { status: "not_persisted", recoverability: "none" },
773
- routing: {
774
- status: "failed",
775
- route: "run",
776
- reason: `Invalid freeflow_run scriptFilter at ${options.path}: ${options.message}`,
777
- },
778
- summary: "Run producer was not executed because freeflow_run scriptFilter was invalid.",
779
- recovery: {
780
- how: "No command or script output was captured. Fix the sandboxed script filter and rerun through freeflow_run.",
781
- },
782
- };
783
- }
784
- function commandRoutingFailureResult(options) {
785
- const { stream, text } = routedCommandText({
786
- outputId: options.outputId,
787
- command: options.command,
788
- executionStatus: options.execution.status,
789
- exitCode: options.execution.exitCode,
790
- stdout: options.stdout,
791
- stderr: options.stderr,
792
- combined: options.combined,
793
- preserve: options.preserve,
794
- thresholds: DEFAULT_ROUTER_THRESHOLDS,
795
- });
796
- const fallback = assembleTextEvidence({ stream, text });
797
- const parser = {
798
- name: "router-fallback",
799
- confidence: 0,
800
- fidelity: fallback.fidelity,
801
- compressed: fallback.compressed,
802
- };
803
- const recovery = options.outputId
804
- ? options.storage !== undefined
805
- ? commandRecoveryHint(options.outputId, options.storage)
806
- : {
807
- how: `Routing failed after vault capture; use freeflow_search with source.kind=vault and outputId=${options.outputId} to recover exact run output.`,
808
- outputId: options.outputId,
809
- }
810
- : {
811
- how: "Run output could not be vaulted. A bounded in-memory preview was returned; rerun through the appropriate producer if exact recovery is required.",
812
- };
813
- return {
814
- toolStatus: "error",
815
- decisionId: decisionId("run-route-error", commandText(options.command), options.errorMessage),
816
- outputId: options.outputId,
817
- ...(options.record !== undefined ? { recordId: options.record.recordId } : {}),
818
- preserve: options.preserve,
819
- execution: options.execution,
820
- producer: options.record?.producer ?? options.producer,
821
- persistence: options.record?.persistence ?? { status: "not_persisted", recoverability: "none" },
822
- ...(options.record?.lineage !== undefined ? { lineage: options.record.lineage } : {}),
823
- ...(options.scriptProducer !== undefined ? { scriptProducer: options.scriptProducer } : {}),
824
- routing: {
825
- status: "failed",
826
- route: "run",
827
- reason: `Run producer executed, but Freeflow routing failed after execution: ${options.errorMessage}`,
828
- },
829
- summary: "Run producer executed, but Freeflow routing failed after execution.",
830
- importantLines: fallback.importantLines,
831
- parser,
832
- recovery,
833
- };
834
- }
835
- // Keep exactness-sensitive command routing aligned with
836
- // capabilities/output-router/references/safety-policy.md.
837
- function routeCommandOutput(options) {
838
- const outputBytes = byteLength(options.combined);
839
- const outputLines = countLines(options.combined);
840
- const isLarge =
841
- outputBytes > options.thresholds.largeOutputBytes || outputLines > options.thresholds.largeOutputLines;
842
- const parseInput = {
843
- command: options.command,
844
- executionStatus: options.executionStatus,
845
- exitCode: options.exitCode,
846
- stdout: options.stdout,
847
- stderr: options.stderr,
848
- combined: options.combined,
849
- };
850
- if (options.goal !== undefined) {
851
- Object.assign(parseInput, { goal: options.goal });
852
- }
853
- const parsed = parseCommandOutput(parseInput);
854
- const parser = parsed.parser;
855
- const statusText = `executionStatus=${options.executionStatus} exitCode=${options.exitCode}`;
856
- const parserText = parserTextFor(parser);
857
- if (options.filters) {
858
- return routeFilteredOutput({ ...options, filters: options.filters }, parsed, statusText);
859
- }
860
- if (options.preserve === "full") {
861
- return routeFullOutput(options, parsed, outputBytes, outputLines, statusText);
862
- }
863
- if (options.executionStatus !== "success") {
864
- return {
865
- decisionId: decisionId("run-route", options.outputId, options.executionStatus, "failed", parser.name),
866
- routingStatus: "routed",
867
- reason: `Command failed or did not complete (${statusText}); selected failure evidence was returned and ${commandStorageReason(options.storage)} before routing (${parserText}).`,
868
- summary: parsed.summary ?? `Command ${options.executionStatus} with exitCode=${options.exitCode}.`,
869
- importantLines: parsed.importantLines,
870
- parser,
871
- };
872
- }
873
- if (options.reducer) {
874
- return routeReducedCommandOutput({ ...options, reducer: options.reducer }, parsed, statusText);
875
- }
876
- if (isLarge) {
877
- return {
878
- decisionId: decisionId("run-route", options.outputId, "large-success", parser.name),
879
- routingStatus: "partial",
880
- reason: `Large successful run output (${outputBytes} bytes, ${outputLines} lines): ${commandStorageReason(options.storage)}; bounded important lines were returned instead of full output (${parserText}).`,
881
- summary: parsed.summary ?? `Command succeeded with ${outputLines} output lines and ${outputBytes} bytes.`,
882
- importantLines: parsed.importantLines,
883
- parser,
884
- };
885
- }
886
- const nearRaw = selectNearRawSuccessfulLines(options);
887
- const nearRawParser = parserWithEvidence(parser, nearRaw);
888
- return {
889
- decisionId: decisionId("run-route", options.outputId, "small-success", parser.name, nearRaw.fidelity),
890
- routingStatus: nearRaw.fidelity === "exact" ? "routed" : "partial",
891
- reason: `Small successful run output: ${commandStorageReason(options.storage)}; routed evidence was returned near-raw from the captured execution (${parserTextFor(nearRawParser)}).`,
892
- summary: parsed.summary ?? `Command success with exitCode=${options.exitCode}.`,
893
- importantLines: nearRaw.importantLines,
894
- parser: nearRawParser,
895
- };
896
- }
897
- function routeReducedCommandOutput(options, parsed, statusText) {
898
- const importantLines = reducerImportantLines(options.reducer);
899
- const parser = parserWithReducer(parsed.parser, options.reducer);
900
- return {
901
- decisionId: decisionId(
902
- "run-route",
903
- options.outputId,
904
- "reducer",
905
- options.reducer.result.name,
906
- options.reducer.result.version,
907
- ),
908
- routingStatus: "partial",
909
- reason: `Reducer ${options.reducer.result.name}@${options.reducer.result.version} selected for successful run output (${statusText}); raw output was vaulted before deterministic reduction (${parserTextFor(parser)}).`,
910
- summary: options.reducer.result.visibleText,
911
- importantLines,
912
- parser,
913
- reducer: runReducerMetadata(options.outputId, options.reducer, options.reducerRecord),
914
- ...(options.reducerRecord?.lineage !== undefined ? { lineage: options.reducerRecord.lineage } : {}),
915
- recovery: commandReducerRecoveryHint(options.outputId, options.reducerRecord?.outputId),
916
- };
917
- }
918
- function routeFilteredOutput(options, parsed, statusText) {
919
- const defaultText = routedCommandText(options);
920
- const filtered = applyRunOutputFilters({
921
- filters: options.filters,
922
- defaultStream: defaultText.stream,
923
- stdout: options.stdout,
924
- stderr: options.stderr,
925
- combined: options.combined,
926
- fallbackImportantLines: parsed.importantLines,
927
- preserveFallbackFailureEvidence: options.executionStatus !== "success",
928
- caps: {
929
- maxLines: options.thresholds.largeOutputLines,
930
- maxExcerptBytes: options.thresholds.largeOutputBytes,
931
- maxLineBytes: options.thresholds.largeOutputBytes,
932
- },
933
- });
934
- const parser = parserWithEvidence(parsed.parser, filtered.evidence);
935
- const fallbackText = filtered.metadata.fallbackPreservedFailureEvidence
936
- ? " No filtered lines matched, so parsed failure evidence was preserved instead of hiding the failure."
937
- : "";
938
- const selectedText = `${filtered.metadata.selectedLines}/${filtered.metadata.sourceLines} line(s) selected`;
939
- return {
940
- decisionId: decisionId("run-route", options.outputId, "filtered", filtered.description, parser.name),
941
- routingStatus: filtered.evidence.compressed ? "partial" : "routed",
942
- reason: `Run output was vaulted before declarative filters were applied (${statusText}; filters: ${filtered.description}; ${parserTextFor(parser)}).${fallbackText}`,
943
- summary: `${parsed.summary ?? `Command ${options.executionStatus} with exitCode=${options.exitCode}.`} Filtered routed evidence: ${selectedText} from ${filtered.stream}.`,
944
- importantLines: filtered.evidence.importantLines,
945
- parser: {
946
- ...parser,
947
- counts: {
948
- ...(parser.counts ?? {}),
949
- filterSourceLines: filtered.metadata.sourceLines,
950
- filterSelectedLines: filtered.metadata.selectedLines,
951
- },
952
- },
953
- filters: filtered.metadata,
954
- };
955
- }
956
- async function routeScriptFilteredOutput(options) {
957
- const { routeOptions, rawRecord, scriptFilter } = options;
958
- const baseRouting = routeCommandOutput(routeOptions);
959
- const operation = {
960
- kind: "script",
961
- language: scriptFilter.language,
962
- code: scriptFilter.code,
963
- };
964
- if (scriptFilter.label !== undefined) {
965
- operation.label = scriptFilter.label;
966
- }
967
- const transformOptions = {
968
- sessionId: options.sessionId,
969
- sources: runScriptFilterSources(rawRecord.outputId),
970
- operation,
971
- preserve: routeOptions.preserve,
972
- thresholds: routeOptions.thresholds,
973
- scriptSandboxAdapters: options.scriptSandboxAdapters,
974
- };
975
- if (options.scriptTransform !== undefined) {
976
- Object.assign(transformOptions, { scriptTransform: options.scriptTransform });
977
- }
978
- if (options.vaultRoot !== undefined) {
979
- Object.assign(transformOptions, { vaultRoot: options.vaultRoot });
980
- }
981
- if (options.vaultRetention !== undefined) {
982
- Object.assign(transformOptions, { vaultRetention: options.vaultRetention });
983
- }
984
- if (scriptFilter.limits !== undefined) {
985
- Object.assign(transformOptions, { limits: scriptFilter.limits });
986
- }
987
- const scriptResult = await freeflowTransform(transformOptions);
988
- const metadata = runScriptFilterMetadata(scriptFilter, scriptResult, rawRecord.outputId);
989
- if (isSuccessfulScriptFilterResult(scriptResult)) {
990
- const importantLines = evidencePacketsAsImportantLines(scriptResult.evidence);
991
- const parser = {
992
- name: `script-filter:${scriptFilter.language}`,
993
- confidence: 1,
994
- fidelity: "exact",
995
- compressed: scriptResult.routing.status === "partial",
996
- counts: {
997
- scriptEvidencePackets: scriptResult.evidence?.length ?? 0,
998
- },
999
- };
1000
- return {
1001
- decisionId: decisionId(
1002
- "run-route",
1003
- rawRecord.outputId,
1004
- "script-filter",
1005
- scriptResult.outputId,
1006
- scriptFilter.language,
1007
- ),
1008
- routingStatus: scriptResult.routing.status,
1009
- reason: `Run output was vaulted as outputId=${rawRecord.outputId} before a sandboxed ${scriptFilter.language} script filter ran over captured stdout/stderr/combined. ${scriptResult.routing.reason}`,
1010
- summary: `${baseRouting.summary} Script filter completed; transformed outputId=${scriptResult.outputId}.`,
1011
- importantLines,
1012
- parser,
1013
- ...(baseRouting.filters !== undefined ? { filters: baseRouting.filters } : {}),
1014
- scriptFilter: metadata,
1015
- ...(scriptResult.lineage !== undefined ? { lineage: scriptResult.lineage } : {}),
1016
- ...(scriptResult.evidence !== undefined ? { evidence: scriptResult.evidence } : {}),
1017
- recovery: commandScriptFilterRecoveryHint(rawRecord.outputId, scriptResult.outputId),
1018
- };
1019
- }
1020
- const failureMessage = scriptResult.failure?.message ?? scriptResult.routing.reason;
1021
- return {
1022
- ...baseRouting,
1023
- decisionId: decisionId(
1024
- "run-route",
1025
- rawRecord.outputId,
1026
- "script-filter-failed",
1027
- scriptFilter.language,
1028
- failureMessage,
1029
- ),
1030
- routingStatus: baseRouting.routingStatus === "failed" ? "failed" : "partial",
1031
- reason: `Run producer executed and raw output was vaulted as outputId=${rawRecord.outputId}, but the sandboxed ${scriptFilter.language} script filter did not produce transformed output: ${failureMessage} Base run evidence was returned instead.`,
1032
- summary: `${baseRouting.summary} Script filter did not produce transformed output: ${failureMessage}`,
1033
- parser: {
1034
- ...baseRouting.parser,
1035
- counts: {
1036
- ...(baseRouting.parser.counts ?? {}),
1037
- scriptFilterFailures: 1,
1038
- },
1039
- },
1040
- scriptFilter: metadata,
1041
- ...(scriptResult.failure !== undefined ? { failure: scriptResult.failure } : {}),
1042
- ...(scriptResult.transformExecution !== undefined ? { transformExecution: scriptResult.transformExecution } : {}),
1043
- ...(scriptResult.lineage !== undefined ? { lineage: scriptResult.lineage } : {}),
1044
- recovery: commandScriptFilterRecoveryHint(rawRecord.outputId),
1045
- };
1046
- }
1047
- function routeFullOutput(options, parsed, outputBytes, outputLines, statusText) {
1048
- const full = selectFullOutputLines(options);
1049
- const parser = parserWithEvidence(parsed.parser, full);
1050
- const routedStatus = full.fidelity === "exact" ? "routed" : "partial";
1051
- const capText = `${options.thresholds.largeOutputBytes} byte full-context cap`;
1052
- const reason =
1053
- full.fidelity === "exact"
1054
- ? `preserve=full returned exact run output within the ${capText} after ${commandStorageReason(options.storage)} (${statusText}; ${parserTextFor(parser)}).`
1055
- : `preserve=full output exceeded the bounded context policy (${outputBytes} bytes, ${outputLines} lines); a bounded preview was returned with raw vault recovery (${statusText}; ${parserTextFor(parser)}).`;
1056
- return {
1057
- decisionId: decisionId("run-route", options.outputId, "preserve-full", full.fidelity, parsed.parser.name),
1058
- routingStatus: routedStatus,
1059
- reason,
1060
- summary: parsed.summary ?? `Command ${options.executionStatus} with exitCode=${options.exitCode}.`,
1061
- importantLines: full.importantLines,
1062
- parser,
1063
- };
1064
- }
1065
- function selectNearRawSuccessfulLines(options) {
1066
- const { stream, text } = routedCommandText(options);
1067
- return assembleTextEvidence({
1068
- stream,
1069
- text,
1070
- caps: {
1071
- maxLines: options.thresholds.largeOutputLines,
1072
- maxExcerptBytes: options.thresholds.largeOutputBytes,
1073
- maxLineBytes: options.thresholds.largeOutputBytes,
1074
- },
1075
- });
1076
- }
1077
- function selectFullOutputLines(options) {
1078
- const { stream, text } = routedCommandText(options);
1079
- return assembleTextEvidence({
1080
- stream,
1081
- text,
1082
- caps: {
1083
- maxLines: Number.MAX_SAFE_INTEGER,
1084
- maxExcerptBytes: options.thresholds.largeOutputBytes,
1085
- maxLineBytes: options.thresholds.largeOutputBytes,
1086
- },
1087
- });
1088
- }
1089
- function routedCommandText(options) {
1090
- if (options.stdout.length > 0 && options.stderr.length === 0) {
1091
- return { stream: "stdout", text: options.stdout };
1092
- }
1093
- if (options.stderr.length > 0 && options.stdout.length === 0) {
1094
- return { stream: "stderr", text: options.stderr };
1095
- }
1096
- return { stream: "combined", text: options.combined };
1097
- }
1098
- function runScriptFilterSources(outputId) {
1099
- return [
1100
- { kind: "vault", outputId, stream: "stdout", alias: "stdout" },
1101
- { kind: "vault", outputId, stream: "stderr", alias: "stderr" },
1102
- { kind: "vault", outputId, stream: "combined", alias: "combined" },
1103
- ];
1104
- }
1105
- function isSuccessfulScriptFilterResult(result) {
1106
- return (
1107
- result.toolStatus === "ok" &&
1108
- result.failure === undefined &&
1109
- typeof result.outputId === "string" &&
1110
- result.outputId.length > 0
1111
- );
1112
- }
1113
- function runScriptFilterMetadata(input, result, rawOutputId) {
1114
- const metadata = {
1115
- status: isSuccessfulScriptFilterResult(result) ? "success" : (result.transformExecution?.status ?? "failed"),
1116
- language: input.language,
1117
- sourceAliases: ["stdout", "stderr", "combined"],
1118
- rawOutputId,
1119
- };
1120
- if (input.label !== undefined) {
1121
- metadata.label = input.label;
1122
- }
1123
- if ("operation" in result && result.operation !== undefined) {
1124
- metadata.operation = result.operation;
1125
- }
1126
- if (result.outputId !== undefined) {
1127
- metadata.outputId = result.outputId;
1128
- }
1129
- if (result.recordId !== undefined) {
1130
- metadata.recordId = result.recordId;
1131
- }
1132
- if (result.persistence !== undefined) {
1133
- metadata.persistence = result.persistence;
1134
- }
1135
- if (result.lineage !== undefined) {
1136
- metadata.lineage = result.lineage;
1137
- }
1138
- if (result.failure !== undefined) {
1139
- metadata.failure = result.failure;
1140
- }
1141
- if (result.transformExecution !== undefined) {
1142
- metadata.transformExecution = result.transformExecution;
1143
- }
1144
- if ("summary" in result && result.summary !== undefined) {
1145
- metadata.summary = result.summary;
1146
- }
1147
- return metadata;
1148
- }
1149
- function evidencePacketsAsImportantLines(evidence) {
1150
- if (!Array.isArray(evidence)) {
1151
- return [];
1152
- }
1153
- return evidence
1154
- .filter((packet) => typeof packet.excerpt === "string" && typeof packet.lines === "string")
1155
- .map((packet) => ({
1156
- stream: "combined",
1157
- lines: packet.lines,
1158
- excerpt: packet.excerpt,
1159
- }));
1160
- }
1161
- function storeReducerOutput(options) {
1162
- const operation = `run-reducer:${options.reducer.result.name}@${options.reducer.result.version}`;
1163
- return storeTextOutput(options.vault, {
1164
- sessionId: options.sessionId,
1165
- raw: options.reducer.result.visibleText,
1166
- sourceKind: "transform",
1167
- decisionIds: [
1168
- decisionId(
1169
- "run-reducer",
1170
- options.rawRecord.outputId,
1171
- options.reducer.result.name,
1172
- options.reducer.result.version,
1173
- ),
1174
- ],
1175
- producer: { kind: "transform", name: "freeflow_run reducer" },
1176
- lineage: {
1177
- sourceRecordIds: [options.rawRecord.recordId],
1178
- sourceOutputIds: [options.rawRecord.outputId],
1179
- operation,
1180
- operationHash: hash(
1181
- JSON.stringify({ operation, sourceOutputId: options.rawRecord.outputId, facts: options.reducer.result.facts }),
1182
- ),
1183
- },
1184
- });
1185
- }
1186
- function runReducerMetadata(rawOutputId, reducer, reducerRecord) {
1187
- return {
1188
- status: reducerRecord === undefined ? "selected" : "success",
1189
- name: reducer.result.name,
1190
- version: reducer.result.version,
1191
- confidence: reducer.result.confidence,
1192
- rawOutputId,
1193
- summary: reducer.result.visibleText,
1194
- facts: reducer.result.facts,
1195
- ...(reducerRecord !== undefined
1196
- ? {
1197
- outputId: reducerRecord.outputId,
1198
- recordId: reducerRecord.recordId,
1199
- persistence: reducerRecord.persistence,
1200
- lineage: reducerRecord.lineage,
1201
- }
1202
- : {}),
1203
- };
1204
- }
1205
- function commandReducerRecoveryHint(rawOutputId, transformedOutputId) {
1206
- if (transformedOutputId) {
1207
- return {
1208
- how: `Raw run output: use freeflow_search with source.kind=vault and outputId=${rawOutputId}. Reducer-transformed output: use freeflow_search with source.kind=vault, outputId=${transformedOutputId}, stream=raw, and an exact lineRange.`,
1209
- outputId: rawOutputId,
1210
- };
1211
- }
1212
- return {
1213
- how: `Reducer output was returned in the structured run result. Raw run output remains recoverable with freeflow_search source.kind=vault outputId=${rawOutputId}.`,
1214
- outputId: rawOutputId,
1215
- };
1216
- }
1217
- function commandScriptFilterRecoveryHint(rawOutputId, transformedOutputId) {
1218
- if (transformedOutputId) {
1219
- return {
1220
- how: `Raw run output: use freeflow_search with source.kind=vault and outputId=${rawOutputId}. Script-filtered transformed output: use freeflow_search with source.kind=vault, outputId=${transformedOutputId}, stream=raw, and an exact lineRange.`,
1221
- outputId: rawOutputId,
1222
- };
1223
- }
1224
- return {
1225
- how: `Script filter did not produce transformed output. Raw run output remains recoverable with freeflow_search source.kind=vault outputId=${rawOutputId}.`,
1226
- outputId: rawOutputId,
1227
- };
1228
- }
1229
- function normalizeRunScriptFilter(input) {
1230
- if (input === undefined || input === null) {
1231
- return { ok: true };
1232
- }
1233
- if (!isRecord(input)) {
1234
- return { ok: false, path: "$.scriptFilter", message: "freeflow_run scriptFilter must be an object." };
1235
- }
1236
- const operation = { kind: "script" };
1237
- if (input.language !== undefined) {
1238
- operation.language = input.language;
1239
- }
1240
- if (input.code !== undefined) {
1241
- operation.code = input.code;
1242
- }
1243
- if (input.label !== undefined) {
1244
- operation.label = input.label;
1245
- }
1246
- const candidate = {
1247
- sources: runScriptFilterSources("ffout_run_script_filter_validation"),
1248
- operation,
1249
- };
1250
- if (input.limits !== undefined) {
1251
- candidate.limits = input.limits;
1252
- }
1253
- const validation = validateTransformInput(candidate);
1254
- if (!validation.ok) {
1255
- const issue = validation.issues[0] ?? { path: "$.scriptFilter", message: "Invalid script filter." };
1256
- return {
1257
- ok: false,
1258
- path: issue.path.replace(/^\$\.operation/, "$.scriptFilter").replace(/^\$\.limits/, "$.scriptFilter.limits"),
1259
- message: issue.message,
1260
- };
1261
- }
1262
- const scriptFilter = {
1263
- language: input.language,
1264
- code: input.code,
1265
- };
1266
- if (input.label !== undefined) {
1267
- scriptFilter.label = input.label;
1268
- }
1269
- if (input.limits !== undefined) {
1270
- scriptFilter.limits = input.limits;
1271
- }
1272
- return { ok: true, scriptFilter };
1273
- }
1274
- function isRecord(value) {
1275
- return typeof value === "object" && value !== null && !Array.isArray(value);
1276
- }
1277
- function parserWithEvidence(parser, evidence) {
1278
- return {
1279
- ...parser,
1280
- fidelity: parser.fidelity === "lossy" || evidence.fidelity === "lossy" ? "lossy" : "exact",
1281
- compressed: evidence.compressed,
1282
- };
1283
- }
1284
- function parserTextFor(parser) {
1285
- return `parser=${parser.name} confidence=${parser.confidence.toFixed(2)} fidelity=${parser.fidelity}`;
1286
- }
1287
- function combineOutputSections(stdout, stderr) {
1288
- if (stdout.length === 0) {
1289
- return stderr;
1290
- }
1291
- if (stderr.length === 0) {
1292
- return stdout;
1293
- }
1294
- return `[stdout]\n${stdout}\n[stderr]\n${stderr}`;
1295
- }
1296
- function commandText(command) {
1297
- return typeof command === "string" ? command : command.join(" ");
1298
- }
1299
- function decisionId(...parts) {
1300
- return `ffdec_${hash(parts.join("\0")).slice(0, 16)}`;
1301
- }
1302
- function hash(value) {
1303
- return createHash("sha256").update(value).digest("hex");
1304
- }