@hassangameryt/freeflow 0.5.0 → 0.7.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 (301) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +4 -5
  4. package/README.md +141 -163
  5. package/capabilities/cognitive-routing/SKILL.md +387 -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 -37
  9. package/hooks/freeflow-runtime-context.mjs +50 -541
  10. package/hooks/hooks.json +1 -26
  11. package/hooks/tests/check-runtime-context-hook.sh +114 -308
  12. package/package.json +27 -17
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +232 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +1292 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +451 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +188 -0
  18. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
  19. package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
  20. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  21. package/pi-extension/dist/cognitive-routing/tool.js +212 -0
  22. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  23. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  24. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  25. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  26. package/pi-extension/dist/context-virtualization/state.js +57 -0
  27. package/pi-extension/dist/context-virtualization/types.js +10 -0
  28. package/pi-extension/dist/conversation-history/passages.js +66 -0
  29. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  30. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  31. package/pi-extension/dist/conversation-history/search.js +56 -0
  32. package/pi-extension/dist/conversation-history/types.js +1 -0
  33. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  34. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  35. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  36. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  37. package/pi-extension/dist/freeflow-context/types.js +12 -0
  38. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  39. package/pi-extension/dist/index.js +564 -129
  40. package/pi-extension/dist/runtime/runtime-context.js +357 -470
  41. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  42. package/pi-extension/dist/settings/settings-tui.js +78 -0
  43. package/pi-extension/dist/settings/settings-ui.js +572 -826
  44. package/pi-extension/dist/ui/text-component.js +57 -0
  45. package/pi-extension/src/cognitive-routing/commands.ts +129 -0
  46. package/pi-extension/src/cognitive-routing/config.ts +303 -0
  47. package/pi-extension/src/cognitive-routing/controller.ts +1628 -0
  48. package/pi-extension/src/cognitive-routing/history.ts +650 -0
  49. package/pi-extension/src/cognitive-routing/host.ts +266 -0
  50. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
  51. package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
  52. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  53. package/pi-extension/src/cognitive-routing/tool.ts +273 -0
  54. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  55. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  56. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  57. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  58. package/pi-extension/src/context-virtualization/state.ts +79 -0
  59. package/pi-extension/src/context-virtualization/types.ts +92 -0
  60. package/pi-extension/src/conversation-history/passages.ts +81 -0
  61. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  62. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  63. package/pi-extension/src/conversation-history/search.ts +79 -0
  64. package/pi-extension/src/conversation-history/types.ts +31 -0
  65. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  66. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  67. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  68. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  69. package/pi-extension/src/freeflow-context/types.ts +70 -0
  70. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  71. package/pi-extension/src/index.ts +597 -135
  72. package/pi-extension/src/runtime/runtime-context.ts +373 -491
  73. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  74. package/pi-extension/src/settings/settings-tui.ts +93 -0
  75. package/pi-extension/src/settings/settings-ui.ts +679 -910
  76. package/pi-extension/src/ui/text-component.ts +66 -0
  77. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  78. package/pi-extension/tests/cognitive-routing/controller.test.js +1128 -0
  79. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1142 -0
  80. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  81. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  82. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  83. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
  84. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
  85. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
  86. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +279 -0
  87. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  88. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  89. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  90. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  91. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  92. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  93. package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
  94. package/pi-extension/tests/integration/pi-extension.test.js +532 -3701
  95. package/pi-extension/tests/integration/prompt-architecture.test.js +350 -0
  96. package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
  97. package/runtime/prompts/cognitive-routing.md +10 -0
  98. package/runtime/prompts/context-virtualization.md +3 -0
  99. package/runtime/prompts/conversation-history.md +3 -0
  100. package/runtime/prompts/core.md +84 -0
  101. package/runtime/prompts/interaction-contract.md +11 -0
  102. package/skills/action-selection/SKILL.md +140 -0
  103. package/skills/action-selection/references/trajectory-stalls.md +71 -0
  104. package/skills/bypass/SKILL.md +109 -38
  105. package/skills/commit-work/SKILL.md +94 -55
  106. package/skills/commit-work/references/git-state-edges.md +94 -0
  107. package/skills/commit-work/references/simple-push.md +92 -0
  108. package/skills/commit-work/references/staging-decisions.md +79 -46
  109. package/skills/decision-gate/SKILL.md +80 -22
  110. package/skills/design-for-depth/SKILL.md +206 -60
  111. package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
  112. package/skills/design-for-depth/references/interface-design-loop.md +77 -97
  113. package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
  114. package/skills/design-for-depth/references/software-design-philosophy.md +128 -174
  115. package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
  116. package/skills/diagnose-failure/SKILL.md +200 -48
  117. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +18 -6
  118. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +13 -5
  119. package/skills/discuss/SKILL.md +119 -65
  120. package/skills/discuss/references/discussion-continuity.md +123 -0
  121. package/skills/evaluate-skill/SKILL.md +166 -52
  122. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +43 -3
  123. package/skills/evaluate-skill/references/evaluation-design.md +56 -30
  124. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  125. package/skills/evaluate-skill/references/review-and-revision.md +62 -21
  126. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  127. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  128. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  129. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  130. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  131. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  132. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  133. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  134. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  135. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  136. package/skills/execute-work/SKILL.md +99 -57
  137. package/skills/execute-work/references/code-practices.md +4 -4
  138. package/skills/execute-work/references/execute-work-edges.md +62 -0
  139. package/skills/finish-branch/SKILL.md +2 -2
  140. package/skills/finish-branch/references/integration-options.md +1 -1
  141. package/skills/handoff/SKILL.md +3 -3
  142. package/skills/launch-work/SKILL.md +1 -1
  143. package/skills/launch-work/references/launch-readiness.md +2 -2
  144. package/skills/migration-work/SKILL.md +2 -2
  145. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  146. package/skills/release-work/SKILL.md +1 -1
  147. package/skills/release-work/references/release-evidence.md +1 -1
  148. package/skills/review-artifact/SKILL.md +129 -66
  149. package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
  150. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +159 -0
  151. package/skills/review-work/SKILL.md +113 -68
  152. package/skills/review-work/references/adjudicate-work-review.md +97 -0
  153. package/skills/review-work/references/independent-work-reviewer-contract.md +139 -0
  154. package/skills/review-work/references/security-risk-lens.md +18 -16
  155. package/skills/setup-freeflow/SKILL.md +16 -17
  156. package/skills/setup-freeflow/references/activation-contract.md +24 -30
  157. package/skills/setup-freeflow/references/host-setup.md +6 -7
  158. package/skills/simplify-code/SKILL.md +97 -39
  159. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  160. package/skills/tdd/SKILL.md +100 -53
  161. package/skills/tdd/references/test-design.md +3 -3
  162. package/skills/track-work/SKILL.md +195 -58
  163. package/skills/track-work/references/working-record-format.md +515 -0
  164. package/skills/track-work/scripts/lib/document.mjs +713 -0
  165. package/skills/track-work/scripts/lib/format.mjs +166 -0
  166. package/skills/track-work/scripts/lib/store.mjs +233 -0
  167. package/skills/track-work/scripts/lib/transitions.mjs +790 -0
  168. package/skills/track-work/scripts/lib/views.mjs +40 -0
  169. package/skills/track-work/scripts/working-record.mjs +316 -0
  170. package/skills/verify-work/SKILL.md +57 -19
  171. package/skills/verify-work/references/browser-runtime-evidence.md +2 -2
  172. package/skills/verify-work/references/integration-evidence.md +1 -7
  173. package/skills/verify-work/references/performance-evidence.md +2 -2
  174. package/skills/workflow/SKILL.md +137 -55
  175. package/skills/workflow/references/domain-skill-composition.md +26 -40
  176. package/skills/write-plan/SKILL.md +1 -1
  177. package/skills/write-skill/SKILL.md +218 -61
  178. package/skills/write-skill/references/activation-boundaries.md +63 -21
  179. package/skills/write-skill/references/agent-first-instructions.md +61 -27
  180. package/skills/write-skill/references/progressive-disclosure.md +97 -33
  181. package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
  182. package/capabilities/output-router/SKILL.md +0 -206
  183. package/capabilities/output-router/references/safety-policy.md +0 -40
  184. package/capabilities/output-router/references/setup.md +0 -110
  185. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  186. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  187. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  188. package/pi-extension/dist/output-router/renderers.js +0 -1023
  189. package/pi-extension/dist/output-router/router-tools.js +0 -554
  190. package/pi-extension/dist/output-router/schemas.js +0 -383
  191. package/pi-extension/dist/output-router/status.js +0 -744
  192. package/pi-extension/dist/output-router/utils.js +0 -502
  193. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  194. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  195. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  196. package/pi-extension/src/output-router/renderers.ts +0 -1108
  197. package/pi-extension/src/output-router/router-tools.ts +0 -579
  198. package/pi-extension/src/output-router/schemas.ts +0 -387
  199. package/pi-extension/src/output-router/status.ts +0 -791
  200. package/pi-extension/src/output-router/utils.ts +0 -554
  201. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  202. package/pi-extension/tests/output-router/search.test.js +0 -722
  203. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  204. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  205. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  206. package/router/dist/benchmarks/benchmarks.js +0 -979
  207. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  208. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  209. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  210. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  211. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  212. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  213. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  214. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  215. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  216. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  217. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  218. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  219. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  220. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  221. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  222. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  223. package/router/dist/config/config.d.ts +0 -103
  224. package/router/dist/config/config.js +0 -632
  225. package/router/dist/config/router-contract.d.ts +0 -10
  226. package/router/dist/config/router-contract.js +0 -54
  227. package/router/dist/config/schema.d.ts +0 -27
  228. package/router/dist/config/schema.js +0 -648
  229. package/router/dist/config/types.d.ts +0 -573
  230. package/router/dist/config/types.js +0 -41
  231. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  232. package/router/dist/evidence/bounded-evidence.js +0 -323
  233. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  234. package/router/dist/evidence/evidence-range-selector.js +0 -232
  235. package/router/dist/evidence/evidence-search.d.ts +0 -27
  236. package/router/dist/evidence/evidence-search.js +0 -490
  237. package/router/dist/evidence/evidence.d.ts +0 -38
  238. package/router/dist/evidence/evidence.js +0 -200
  239. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  240. package/router/dist/evidence/failure-contracts.js +0 -166
  241. package/router/dist/evidence/line-ranges.d.ts +0 -24
  242. package/router/dist/evidence/line-ranges.js +0 -22
  243. package/router/dist/experiments/local-index.d.ts +0 -64
  244. package/router/dist/experiments/local-index.js +0 -581
  245. package/router/dist/index.d.ts +0 -30
  246. package/router/dist/index.js +0 -32
  247. package/router/dist/local/local-traversal.d.ts +0 -18
  248. package/router/dist/local/local-traversal.js +0 -334
  249. package/router/dist/processing/engine.d.ts +0 -132
  250. package/router/dist/processing/engine.js +0 -533
  251. package/router/dist/processing/reducers.d.ts +0 -280
  252. package/router/dist/processing/reducers.js +0 -1673
  253. package/router/dist/processing/renderers.d.ts +0 -31
  254. package/router/dist/processing/renderers.js +0 -195
  255. package/router/dist/processing/scripts.d.ts +0 -91
  256. package/router/dist/processing/scripts.js +0 -386
  257. package/router/dist/repo/repo-traversal.d.ts +0 -18
  258. package/router/dist/repo/repo-traversal.js +0 -245
  259. package/router/dist/routing/observed-reducers.d.ts +0 -41
  260. package/router/dist/routing/observed-reducers.js +0 -661
  261. package/router/dist/routing/observed-routing.d.ts +0 -26
  262. package/router/dist/routing/observed-routing.js +0 -378
  263. package/router/dist/routing/parsers.d.ts +0 -16
  264. package/router/dist/routing/parsers.js +0 -521
  265. package/router/dist/routing/run-filters.d.ts +0 -60
  266. package/router/dist/routing/run-filters.js +0 -238
  267. package/router/dist/routing/run-reducers.d.ts +0 -49
  268. package/router/dist/routing/run-reducers.js +0 -122
  269. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  270. package/router/dist/sandbox/adapter-roots.js +0 -66
  271. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  272. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  273. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  274. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  275. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  276. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  277. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  278. package/router/dist/sandbox/script-sandbox.js +0 -337
  279. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  280. package/router/dist/setup/script-transform-adapters.js +0 -278
  281. package/router/dist/tools/batch.d.ts +0 -33
  282. package/router/dist/tools/batch.js +0 -586
  283. package/router/dist/tools/run.d.ts +0 -62
  284. package/router/dist/tools/run.js +0 -1304
  285. package/router/dist/tools/search.d.ts +0 -66
  286. package/router/dist/tools/search.js +0 -1364
  287. package/router/dist/transform/engine.d.ts +0 -168
  288. package/router/dist/transform/engine.js +0 -2072
  289. package/router/dist/vault/vault-index.d.ts +0 -125
  290. package/router/dist/vault/vault-index.js +0 -416
  291. package/router/dist/vault/vault.d.ts +0 -138
  292. package/router/dist/vault/vault.js +0 -630
  293. package/runtime/interaction-contract.md +0 -16
  294. package/skills/discuss/references/checkpoints.md +0 -66
  295. package/skills/execute-work/references/execution-loop.md +0 -77
  296. package/skills/mode-contract/SKILL.md +0 -104
  297. package/skills/review-artifact/references/reviewer-prompt.md +0 -148
  298. package/skills/review-work/references/reviewer-prompt.md +0 -134
  299. package/skills/track-work/references/working-record-schema.md +0 -378
  300. package/skills/workflow/references/workflow-loop.md +0 -92
  301. /package/skills/handoff/references/{templates.md → handoff-templates.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
- }