@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +148 -154
- package/capabilities/cognitive-routing/SKILL.md +229 -0
- package/capabilities/context-virtualization/SKILL.md +103 -0
- package/capabilities/conversation-history/SKILL.md +53 -0
- package/command-surface.json +6 -13
- package/hooks/freeflow-runtime-context.mjs +35 -91
- package/hooks/tests/check-runtime-context-hook.sh +56 -39
- package/package.json +24 -16
- package/pi-extension/dist/cognitive-routing/commands.js +96 -0
- package/pi-extension/dist/cognitive-routing/config.js +236 -0
- package/pi-extension/dist/cognitive-routing/controller.js +862 -0
- package/pi-extension/dist/cognitive-routing/history.js +439 -0
- package/pi-extension/dist/cognitive-routing/host.js +126 -0
- package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
- package/pi-extension/dist/cognitive-routing/tool.js +211 -0
- package/pi-extension/dist/cognitive-routing/types.js +6 -0
- package/pi-extension/dist/context-virtualization/commands.js +89 -0
- package/pi-extension/dist/context-virtualization/projector.js +42 -0
- package/pi-extension/dist/context-virtualization/runtime.js +356 -0
- package/pi-extension/dist/context-virtualization/state.js +57 -0
- package/pi-extension/dist/context-virtualization/types.js +10 -0
- package/pi-extension/dist/conversation-history/passages.js +66 -0
- package/pi-extension/dist/conversation-history/ranking.js +105 -0
- package/pi-extension/dist/conversation-history/runtime.js +226 -0
- package/pi-extension/dist/conversation-history/search.js +56 -0
- package/pi-extension/dist/conversation-history/types.js +1 -0
- package/pi-extension/dist/freeflow-context/resolver.js +120 -0
- package/pi-extension/dist/freeflow-context/runtime.js +80 -0
- package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
- package/pi-extension/dist/freeflow-context/tool.js +308 -0
- package/pi-extension/dist/freeflow-context/types.js +12 -0
- package/pi-extension/dist/freeflow-context/visibility.js +28 -0
- package/pi-extension/dist/index.js +516 -98
- package/pi-extension/dist/runtime/runtime-context.js +318 -271
- package/pi-extension/dist/runtime/runtime-identity.js +5 -0
- package/pi-extension/dist/settings/settings-tui.js +78 -0
- package/pi-extension/dist/settings/settings-ui.js +593 -563
- package/pi-extension/dist/ui/text-component.js +57 -0
- package/pi-extension/src/cognitive-routing/commands.ts +119 -0
- package/pi-extension/src/cognitive-routing/config.ts +307 -0
- package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
- package/pi-extension/src/cognitive-routing/history.ts +638 -0
- package/pi-extension/src/cognitive-routing/host.ts +172 -0
- package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
- package/pi-extension/src/cognitive-routing/tool.ts +272 -0
- package/pi-extension/src/cognitive-routing/types.ts +89 -0
- package/pi-extension/src/context-virtualization/commands.ts +111 -0
- package/pi-extension/src/context-virtualization/projector.ts +62 -0
- package/pi-extension/src/context-virtualization/runtime.ts +406 -0
- package/pi-extension/src/context-virtualization/state.ts +79 -0
- package/pi-extension/src/context-virtualization/types.ts +92 -0
- package/pi-extension/src/conversation-history/passages.ts +81 -0
- package/pi-extension/src/conversation-history/ranking.ts +155 -0
- package/pi-extension/src/conversation-history/runtime.ts +250 -0
- package/pi-extension/src/conversation-history/search.ts +79 -0
- package/pi-extension/src/conversation-history/types.ts +31 -0
- package/pi-extension/src/freeflow-context/resolver.ts +139 -0
- package/pi-extension/src/freeflow-context/runtime.ts +100 -0
- package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
- package/pi-extension/src/freeflow-context/tool.ts +360 -0
- package/pi-extension/src/freeflow-context/types.ts +70 -0
- package/pi-extension/src/freeflow-context/visibility.ts +53 -0
- package/pi-extension/src/index.ts +553 -101
- package/pi-extension/src/runtime/runtime-context.ts +339 -281
- package/pi-extension/src/runtime/runtime-identity.ts +8 -0
- package/pi-extension/src/settings/settings-tui.ts +93 -0
- package/pi-extension/src/settings/settings-ui.ts +700 -613
- package/pi-extension/src/ui/text-component.ts +66 -0
- package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
- package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
- package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
- package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
- package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
- package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
- package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
- package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
- package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
- package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
- package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
- package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
- package/pi-extension/tests/conversation-history/search.test.js +97 -0
- package/pi-extension/tests/integration/pi-extension.test.js +927 -2048
- package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
- package/runtime/prompts/cognitive-routing.md +9 -0
- package/runtime/prompts/context-virtualization.md +3 -0
- package/runtime/prompts/conversation-history.md +3 -0
- package/runtime/prompts/core.md +13 -0
- package/runtime/prompts/interaction-contract.md +7 -0
- package/runtime/prompts/skills.md +65 -0
- package/skills/action-selection/SKILL.md +91 -0
- package/skills/bypass/SKILL.md +3 -2
- package/skills/commit-work/SKILL.md +1 -1
- package/skills/commit-work/references/staging-decisions.md +1 -1
- package/skills/decision-gate/SKILL.md +1 -1
- package/skills/design-for-depth/SKILL.md +14 -33
- package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
- package/skills/design-for-depth/references/interface-design-loop.md +1 -1
- package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
- package/skills/diagnose-failure/SKILL.md +20 -14
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
- package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
- package/skills/discuss/SKILL.md +34 -22
- package/skills/discuss/references/discussion-continuity.md +71 -0
- package/skills/evaluate-skill/SKILL.md +5 -5
- package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
- package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
- package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
- package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
- package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
- package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
- package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
- package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
- package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
- package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
- package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
- package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
- package/skills/execute-work/SKILL.md +18 -15
- package/skills/execute-work/references/code-practices.md +1 -1
- package/skills/execute-work/references/execute-work-edges.md +42 -0
- package/skills/finish-branch/SKILL.md +2 -2
- package/skills/finish-branch/references/integration-options.md +1 -1
- package/skills/handoff/SKILL.md +3 -3
- package/skills/launch-work/SKILL.md +1 -1
- package/skills/launch-work/references/launch-readiness.md +2 -2
- package/skills/migration-work/SKILL.md +2 -2
- package/skills/migration-work/references/migration-lifecycle.md +1 -1
- package/skills/mode-contract/SKILL.md +4 -4
- package/skills/release-work/SKILL.md +1 -1
- package/skills/release-work/references/release-evidence.md +1 -1
- package/skills/review-artifact/SKILL.md +28 -26
- package/skills/review-work/SKILL.md +28 -26
- package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
- package/skills/review-work/references/security-risk-lens.md +1 -1
- package/skills/setup-freeflow/SKILL.md +3 -1
- package/skills/setup-freeflow/references/activation-contract.md +2 -2
- package/skills/setup-freeflow/references/host-setup.md +2 -2
- package/skills/simplify-code/SKILL.md +1 -1
- package/skills/simplify-code/references/simplification-patterns.md +1 -1
- package/skills/tdd/SKILL.md +18 -38
- package/skills/tdd/references/test-design.md +2 -2
- package/skills/track-work/SKILL.md +362 -58
- package/skills/track-work/scripts/lib/codec.mjs +570 -0
- package/skills/track-work/scripts/lib/commands.mjs +568 -0
- package/skills/track-work/scripts/lib/contract.mjs +303 -0
- package/skills/track-work/scripts/lib/model.mjs +506 -0
- package/skills/track-work/scripts/lib/operations.mjs +965 -0
- package/skills/track-work/scripts/lib/record-store.mjs +264 -0
- package/skills/track-work/scripts/lib/result.mjs +37 -0
- package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
- package/skills/track-work/scripts/lib/schema.mjs +277 -0
- package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
- package/skills/track-work/scripts/lib/views.mjs +443 -0
- package/skills/track-work/scripts/lib/workspace.mjs +134 -0
- package/skills/track-work/scripts/working-record.mjs +122 -0
- package/skills/verify-work/SKILL.md +14 -2
- package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
- package/skills/verify-work/references/integration-evidence.md +1 -1
- package/skills/verify-work/references/performance-evidence.md +1 -1
- package/skills/workflow/SKILL.md +105 -53
- package/skills/workflow/references/domain-skill-composition.md +22 -42
- package/skills/write-plan/SKILL.md +1 -1
- package/skills/write-skill/SKILL.md +3 -2
- package/skills/write-skill/references/progressive-disclosure.md +17 -3
- package/capabilities/output-router/SKILL.md +0 -206
- package/capabilities/output-router/references/safety-policy.md +0 -40
- package/capabilities/output-router/references/setup.md +0 -110
- package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
- package/pi-extension/dist/output-router/native-safety-net.js +0 -216
- package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
- package/pi-extension/dist/output-router/renderers.js +0 -1023
- package/pi-extension/dist/output-router/router-tools.js +0 -554
- package/pi-extension/dist/output-router/schemas.js +0 -383
- package/pi-extension/dist/output-router/status.js +0 -744
- package/pi-extension/dist/output-router/utils.js +0 -502
- package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
- package/pi-extension/src/output-router/native-safety-net.ts +0 -248
- package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
- package/pi-extension/src/output-router/renderers.ts +0 -1108
- package/pi-extension/src/output-router/router-tools.ts +0 -579
- package/pi-extension/src/output-router/schemas.ts +0 -387
- package/pi-extension/src/output-router/status.ts +0 -791
- package/pi-extension/src/output-router/utils.ts +0 -554
- package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
- package/pi-extension/tests/output-router/search.test.js +0 -722
- package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
- package/router/dist/benchmarks/benchmark-harness.js +0 -92
- package/router/dist/benchmarks/benchmarks.d.ts +0 -109
- package/router/dist/benchmarks/benchmarks.js +0 -979
- package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
- package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
- package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
- package/router/dist/benchmarks/command-benchmarks.js +0 -698
- package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
- package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
- package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
- package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
- package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
- package/router/dist/benchmarks/context-saving-baseline.js +0 -470
- package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
- package/router/dist/benchmarks/index-benchmarks.js +0 -862
- package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
- package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
- package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
- package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
- package/router/dist/config/config.d.ts +0 -103
- package/router/dist/config/config.js +0 -632
- package/router/dist/config/router-contract.d.ts +0 -10
- package/router/dist/config/router-contract.js +0 -54
- package/router/dist/config/schema.d.ts +0 -27
- package/router/dist/config/schema.js +0 -648
- package/router/dist/config/types.d.ts +0 -573
- package/router/dist/config/types.js +0 -41
- package/router/dist/evidence/bounded-evidence.d.ts +0 -40
- package/router/dist/evidence/bounded-evidence.js +0 -323
- package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
- package/router/dist/evidence/evidence-range-selector.js +0 -232
- package/router/dist/evidence/evidence-search.d.ts +0 -27
- package/router/dist/evidence/evidence-search.js +0 -490
- package/router/dist/evidence/evidence.d.ts +0 -38
- package/router/dist/evidence/evidence.js +0 -200
- package/router/dist/evidence/failure-contracts.d.ts +0 -37
- package/router/dist/evidence/failure-contracts.js +0 -166
- package/router/dist/evidence/line-ranges.d.ts +0 -24
- package/router/dist/evidence/line-ranges.js +0 -22
- package/router/dist/experiments/local-index.d.ts +0 -64
- package/router/dist/experiments/local-index.js +0 -581
- package/router/dist/index.d.ts +0 -30
- package/router/dist/index.js +0 -32
- package/router/dist/local/local-traversal.d.ts +0 -18
- package/router/dist/local/local-traversal.js +0 -334
- package/router/dist/processing/engine.d.ts +0 -132
- package/router/dist/processing/engine.js +0 -533
- package/router/dist/processing/reducers.d.ts +0 -280
- package/router/dist/processing/reducers.js +0 -1673
- package/router/dist/processing/renderers.d.ts +0 -31
- package/router/dist/processing/renderers.js +0 -195
- package/router/dist/processing/scripts.d.ts +0 -91
- package/router/dist/processing/scripts.js +0 -386
- package/router/dist/repo/repo-traversal.d.ts +0 -18
- package/router/dist/repo/repo-traversal.js +0 -245
- package/router/dist/routing/observed-reducers.d.ts +0 -41
- package/router/dist/routing/observed-reducers.js +0 -661
- package/router/dist/routing/observed-routing.d.ts +0 -26
- package/router/dist/routing/observed-routing.js +0 -378
- package/router/dist/routing/parsers.d.ts +0 -16
- package/router/dist/routing/parsers.js +0 -521
- package/router/dist/routing/run-filters.d.ts +0 -60
- package/router/dist/routing/run-filters.js +0 -238
- package/router/dist/routing/run-reducers.d.ts +0 -49
- package/router/dist/routing/run-reducers.js +0 -122
- package/router/dist/sandbox/adapter-roots.d.ts +0 -46
- package/router/dist/sandbox/adapter-roots.js +0 -66
- package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
- package/router/dist/sandbox/eryx-python-adapter.js +0 -868
- package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
- package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
- package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
- package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
- package/router/dist/sandbox/script-sandbox.d.ts +0 -134
- package/router/dist/sandbox/script-sandbox.js +0 -337
- package/router/dist/setup/script-transform-adapters.d.ts +0 -32
- package/router/dist/setup/script-transform-adapters.js +0 -278
- package/router/dist/tools/batch.d.ts +0 -33
- package/router/dist/tools/batch.js +0 -586
- package/router/dist/tools/run.d.ts +0 -62
- package/router/dist/tools/run.js +0 -1304
- package/router/dist/tools/search.d.ts +0 -66
- package/router/dist/tools/search.js +0 -1364
- package/router/dist/transform/engine.d.ts +0 -168
- package/router/dist/transform/engine.js +0 -2072
- package/router/dist/vault/vault-index.d.ts +0 -125
- package/router/dist/vault/vault-index.js +0 -416
- package/router/dist/vault/vault.d.ts +0 -138
- package/router/dist/vault/vault.js +0 -630
- package/runtime/interaction-contract.md +0 -16
- package/skills/discuss/references/checkpoints.md +0 -66
- package/skills/execute-work/references/execution-loop.md +0 -77
- package/skills/track-work/references/working-record-schema.md +0 -378
- package/skills/workflow/references/workflow-loop.md +0 -92
- /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
- /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
package/router/dist/tools/run.js
DELETED
|
@@ -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
|
-
}
|