@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +4 -5
- package/README.md +141 -163
- package/capabilities/cognitive-routing/SKILL.md +387 -0
- package/capabilities/context-virtualization/SKILL.md +103 -0
- package/capabilities/conversation-history/SKILL.md +53 -0
- package/command-surface.json +6 -37
- package/hooks/freeflow-runtime-context.mjs +50 -541
- package/hooks/hooks.json +1 -26
- package/hooks/tests/check-runtime-context-hook.sh +114 -308
- package/package.json +27 -17
- package/pi-extension/dist/cognitive-routing/commands.js +96 -0
- package/pi-extension/dist/cognitive-routing/config.js +232 -0
- package/pi-extension/dist/cognitive-routing/controller.js +1292 -0
- package/pi-extension/dist/cognitive-routing/history.js +451 -0
- package/pi-extension/dist/cognitive-routing/host.js +188 -0
- package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
- package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
- package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
- package/pi-extension/dist/cognitive-routing/tool.js +212 -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 +564 -129
- package/pi-extension/dist/runtime/runtime-context.js +357 -470
- 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 +572 -826
- package/pi-extension/dist/ui/text-component.js +57 -0
- package/pi-extension/src/cognitive-routing/commands.ts +129 -0
- package/pi-extension/src/cognitive-routing/config.ts +303 -0
- package/pi-extension/src/cognitive-routing/controller.ts +1628 -0
- package/pi-extension/src/cognitive-routing/history.ts +650 -0
- package/pi-extension/src/cognitive-routing/host.ts +266 -0
- package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
- package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
- package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
- package/pi-extension/src/cognitive-routing/tool.ts +273 -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 +597 -135
- package/pi-extension/src/runtime/runtime-context.ts +373 -491
- 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 +679 -910
- 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 +1128 -0
- package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1142 -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/pi-normal-lifecycle.test.js +434 -0
- package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
- package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
- package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +279 -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/mode-free-core.test.js +218 -0
- package/pi-extension/tests/integration/pi-extension.test.js +532 -3701
- package/pi-extension/tests/integration/prompt-architecture.test.js +350 -0
- package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
- package/runtime/prompts/cognitive-routing.md +10 -0
- package/runtime/prompts/context-virtualization.md +3 -0
- package/runtime/prompts/conversation-history.md +3 -0
- package/runtime/prompts/core.md +84 -0
- package/runtime/prompts/interaction-contract.md +11 -0
- package/skills/action-selection/SKILL.md +140 -0
- package/skills/action-selection/references/trajectory-stalls.md +71 -0
- package/skills/bypass/SKILL.md +109 -38
- package/skills/commit-work/SKILL.md +94 -55
- package/skills/commit-work/references/git-state-edges.md +94 -0
- package/skills/commit-work/references/simple-push.md +92 -0
- package/skills/commit-work/references/staging-decisions.md +79 -46
- package/skills/decision-gate/SKILL.md +80 -22
- package/skills/design-for-depth/SKILL.md +206 -60
- package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
- package/skills/design-for-depth/references/interface-design-loop.md +77 -97
- package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
- package/skills/design-for-depth/references/software-design-philosophy.md +128 -174
- package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
- package/skills/diagnose-failure/SKILL.md +200 -48
- package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +18 -6
- package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +13 -5
- package/skills/discuss/SKILL.md +119 -65
- package/skills/discuss/references/discussion-continuity.md +123 -0
- package/skills/evaluate-skill/SKILL.md +166 -52
- package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +43 -3
- package/skills/evaluate-skill/references/evaluation-design.md +56 -30
- package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
- package/skills/evaluate-skill/references/review-and-revision.md +62 -21
- 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 +99 -57
- package/skills/execute-work/references/code-practices.md +4 -4
- package/skills/execute-work/references/execute-work-edges.md +62 -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/release-work/SKILL.md +1 -1
- package/skills/release-work/references/release-evidence.md +1 -1
- package/skills/review-artifact/SKILL.md +129 -66
- package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
- package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +159 -0
- package/skills/review-work/SKILL.md +113 -68
- package/skills/review-work/references/adjudicate-work-review.md +97 -0
- package/skills/review-work/references/independent-work-reviewer-contract.md +139 -0
- package/skills/review-work/references/security-risk-lens.md +18 -16
- package/skills/setup-freeflow/SKILL.md +16 -17
- package/skills/setup-freeflow/references/activation-contract.md +24 -30
- package/skills/setup-freeflow/references/host-setup.md +6 -7
- package/skills/simplify-code/SKILL.md +97 -39
- package/skills/simplify-code/references/simplification-patterns.md +1 -1
- package/skills/tdd/SKILL.md +100 -53
- package/skills/tdd/references/test-design.md +3 -3
- package/skills/track-work/SKILL.md +195 -58
- package/skills/track-work/references/working-record-format.md +515 -0
- package/skills/track-work/scripts/lib/document.mjs +713 -0
- package/skills/track-work/scripts/lib/format.mjs +166 -0
- package/skills/track-work/scripts/lib/store.mjs +233 -0
- package/skills/track-work/scripts/lib/transitions.mjs +790 -0
- package/skills/track-work/scripts/lib/views.mjs +40 -0
- package/skills/track-work/scripts/working-record.mjs +316 -0
- package/skills/verify-work/SKILL.md +57 -19
- package/skills/verify-work/references/browser-runtime-evidence.md +2 -2
- package/skills/verify-work/references/integration-evidence.md +1 -7
- package/skills/verify-work/references/performance-evidence.md +2 -2
- package/skills/workflow/SKILL.md +137 -55
- package/skills/workflow/references/domain-skill-composition.md +26 -40
- package/skills/write-plan/SKILL.md +1 -1
- package/skills/write-skill/SKILL.md +218 -61
- package/skills/write-skill/references/activation-boundaries.md +63 -21
- package/skills/write-skill/references/agent-first-instructions.md +61 -27
- package/skills/write-skill/references/progressive-disclosure.md +97 -33
- package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
- 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/mode-contract/SKILL.md +0 -104
- package/skills/review-artifact/references/reviewer-prompt.md +0 -148
- package/skills/review-work/references/reviewer-prompt.md +0 -134
- 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
|
@@ -1,773 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { performance } from "node:perf_hooks";
|
|
7
|
-
import {
|
|
8
|
-
approximateTokens,
|
|
9
|
-
defaultJsonRunReportPath,
|
|
10
|
-
escapeMarkdownTableCell as escapeTable,
|
|
11
|
-
formatPercent,
|
|
12
|
-
latencySummary,
|
|
13
|
-
normalizeIterations,
|
|
14
|
-
parseBenchmarkCliArgs,
|
|
15
|
-
reductionPercent,
|
|
16
|
-
writeBenchmarkReportPair,
|
|
17
|
-
} from "./benchmark-harness.js";
|
|
18
|
-
import { freeflowBatch } from "../tools/batch.js";
|
|
19
|
-
import { freeflowSearch } from "../tools/search.js";
|
|
20
|
-
import { freeflowRun } from "../tools/run.js";
|
|
21
|
-
const DEFAULT_ITERATIONS = 1;
|
|
22
|
-
const COMPACT_CONTEXT_CAP_BYTES = 1_600;
|
|
23
|
-
const TEST_STDOUT = [
|
|
24
|
-
"FAIL packages/auth/auth.test.ts",
|
|
25
|
-
" ● refresh token keeps AUTH_TOKEN scoped",
|
|
26
|
-
" Expected AUTH_TOKEN to be redacted before logging",
|
|
27
|
-
"PASS packages/router/run.test.ts",
|
|
28
|
-
"Tests: 1 failed, 24 passed, 25 total",
|
|
29
|
-
"Ran all test suites.",
|
|
30
|
-
].join("\n");
|
|
31
|
-
const BUILD_STDERR = [
|
|
32
|
-
"src/auth.ts(42,11): error TS2322: Type 'undefined' is not assignable to type 'string'.",
|
|
33
|
-
"src/router.ts(7,3): warning TS6133: 'unused' is declared but its value is never read.",
|
|
34
|
-
].join("\n");
|
|
35
|
-
const SERVICE_LOG = [
|
|
36
|
-
"2026-06-26T10:00:00Z service=payments status=starting",
|
|
37
|
-
...Array.from(
|
|
38
|
-
{ length: 120 },
|
|
39
|
-
(_, index) => `2026-06-26T10:00:${String(index + 1).padStart(2, "0")}Z heartbeat worker=${index % 4} status=ok`,
|
|
40
|
-
),
|
|
41
|
-
"2026-06-26T10:05:00Z service=payments error=RATE_LIMIT retryAfter=30 requestId=req_critical_42",
|
|
42
|
-
...Array.from(
|
|
43
|
-
{ length: 80 },
|
|
44
|
-
(_, index) => `2026-06-26T10:06:${String(index + 1).padStart(2, "0")}Z heartbeat worker=${index % 4} status=ok`,
|
|
45
|
-
),
|
|
46
|
-
].join("\n");
|
|
47
|
-
const JSON_TABLE = JSON.stringify(
|
|
48
|
-
{
|
|
49
|
-
status: "ok",
|
|
50
|
-
marker: "JSON_TABLE_NEEDLE invoice_total=12345",
|
|
51
|
-
rows: Array.from({ length: 80 }, (_, index) => ({
|
|
52
|
-
id: index + 1,
|
|
53
|
-
value: `row-${index + 1}`,
|
|
54
|
-
amount: index === 41 ? 12345 : index,
|
|
55
|
-
})),
|
|
56
|
-
},
|
|
57
|
-
null,
|
|
58
|
-
2,
|
|
59
|
-
);
|
|
60
|
-
const DOC_TEXT = [
|
|
61
|
-
"# Freeflow Output Router",
|
|
62
|
-
"",
|
|
63
|
-
"Freeflow keeps model context small while preserving exact output recovery.",
|
|
64
|
-
"",
|
|
65
|
-
"## Recovery Contract",
|
|
66
|
-
"",
|
|
67
|
-
"Every exact routed output receives an outputId and line-range recovery hint.",
|
|
68
|
-
"Metadata-only records must not claim exact raw recovery.",
|
|
69
|
-
"",
|
|
70
|
-
"## Batch Contract",
|
|
71
|
-
"",
|
|
72
|
-
"Batch intermediates stay in details.result.steps instead of model context.",
|
|
73
|
-
].join("\n");
|
|
74
|
-
const REPO_CODE = [
|
|
75
|
-
"export function routeSandboxPermissions(mode: string) {",
|
|
76
|
-
" if (mode === 'UseDefault') return 'normal sandbox';",
|
|
77
|
-
" if (mode === 'RequireEscalated') return 'approval required';",
|
|
78
|
-
" return 'WithAdditionalPermissions';",
|
|
79
|
-
"}",
|
|
80
|
-
].join("\n");
|
|
81
|
-
const GENERATED_DECOY = `${"Sandbox Permissions UseDefault RequireEscalated WithAdditionalPermissions ".repeat(2000)}GENERATED_DECOY_SENTINEL`;
|
|
82
|
-
export async function runContextModeNormalizedBenchmarks(options = {}) {
|
|
83
|
-
const iterations = normalizeIterations(options.iterations, DEFAULT_ITERATIONS);
|
|
84
|
-
const root = await createTempDir("freeflow-context-mode-normalized-");
|
|
85
|
-
const env = {
|
|
86
|
-
root: root.path,
|
|
87
|
-
repoRoot: join(root.path, "repo"),
|
|
88
|
-
vaultRoot: join(root.path, "vault"),
|
|
89
|
-
contextStoreRoot: join(root.path, "context-mode-store"),
|
|
90
|
-
sessionId: "context-mode-normalized",
|
|
91
|
-
commandRunner: fixtureCommandRunner(),
|
|
92
|
-
};
|
|
93
|
-
try {
|
|
94
|
-
await setupRepo(env.repoRoot);
|
|
95
|
-
await mkdir(env.vaultRoot, { recursive: true });
|
|
96
|
-
await mkdir(env.contextStoreRoot, { recursive: true });
|
|
97
|
-
const fixtures = contextModeFixtureDefinitions(env);
|
|
98
|
-
const fixtureResults = [];
|
|
99
|
-
for (const fixture of fixtures) {
|
|
100
|
-
const results = [];
|
|
101
|
-
for (const mode of contextModeModes()) {
|
|
102
|
-
results.push(await runFixtureMode(fixture, mode, env, iterations));
|
|
103
|
-
}
|
|
104
|
-
fixtureResults.push({
|
|
105
|
-
id: fixture.id,
|
|
106
|
-
title: fixture.title,
|
|
107
|
-
category: fixture.category,
|
|
108
|
-
expectedFacts: fixture.expectedFacts,
|
|
109
|
-
results,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
114
|
-
iterations,
|
|
115
|
-
summary: summarizeContextModeReport(fixtureResults),
|
|
116
|
-
fixtures: fixtureResults,
|
|
117
|
-
notes: [
|
|
118
|
-
"Context Mode comparison is a normalized proxy fixture, not a run of the external Context Mode runtime.",
|
|
119
|
-
"The proxy stores exact raw fixture payloads behind synthetic handles and returns bounded relevant snippets.",
|
|
120
|
-
"No public superiority claim is allowed from this benchmark alone.",
|
|
121
|
-
],
|
|
122
|
-
};
|
|
123
|
-
} finally {
|
|
124
|
-
await root.cleanup();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
export function renderContextModeNormalizedBenchmarkReport(report) {
|
|
128
|
-
const date = report.generatedAt.slice(0, 10);
|
|
129
|
-
const lines = [
|
|
130
|
-
"# Context Mode Normalized Benchmark Report - Iteration 1",
|
|
131
|
-
"",
|
|
132
|
-
`Date: ${date}`,
|
|
133
|
-
"",
|
|
134
|
-
"## Scope",
|
|
135
|
-
"",
|
|
136
|
-
"This benchmark compares Freeflow-owned tools against a normalized Context Mode-style proxy over representative command, docs, logs, JSON/table, repo-search, and batch fixture classes.",
|
|
137
|
-
"",
|
|
138
|
-
"The proxy is not the external Context Mode runtime. It stores exact raw fixture payloads behind synthetic handles and returns bounded relevant snippets. Treat results as directionally useful fixture evidence, not a public superiority claim.",
|
|
139
|
-
"",
|
|
140
|
-
"## Command",
|
|
141
|
-
"",
|
|
142
|
-
"```sh",
|
|
143
|
-
"npm run bench:router:context-mode-normalized",
|
|
144
|
-
"```",
|
|
145
|
-
"",
|
|
146
|
-
"The CLI writes machine-readable JSON under `router/evals/runs/` by default. That JSON is generated run data; this Markdown file is the durable runtime report.",
|
|
147
|
-
"",
|
|
148
|
-
"## Summary",
|
|
149
|
-
"",
|
|
150
|
-
`- Fixtures: ${report.summary.fixtures}`,
|
|
151
|
-
`- Freeflow passed: ${report.summary.freeflow.passed}/${report.summary.fixtures}`,
|
|
152
|
-
`- Context Mode proxy passed: ${report.summary.contextModeProxy.passed}/${report.summary.fixtures}`,
|
|
153
|
-
`- Freeflow answer-accurate visible output: ${report.summary.freeflow.answerAccurate}/${report.summary.fixtures}`,
|
|
154
|
-
`- Freeflow exact facts preserved: ${report.summary.freeflow.exactFactsPreserved}/${report.summary.fixtures}`,
|
|
155
|
-
`- Freeflow recovery available: ${report.summary.freeflow.recoveryAvailable}/${report.summary.fixtures}`,
|
|
156
|
-
`- Freeflow lower model-visible bytes: ${report.summary.freeflowBetterModelVisibleBytes}/${report.summary.fixtures}`,
|
|
157
|
-
`- Freeflow lower tool-call count: ${report.summary.freeflowBetterToolCalls}/${report.summary.fixtures}`,
|
|
158
|
-
`- Freeflow model-visible reduction: ${formatPercent(report.summary.freeflow.weightedModelVisibleReductionPercent)} (${report.summary.freeflow.totalRawBytes} raw bytes to ${report.summary.freeflow.totalModelVisibleBytes} visible bytes)`,
|
|
159
|
-
`- Context Mode proxy model-visible reduction: ${formatPercent(report.summary.contextModeProxy.weightedModelVisibleReductionPercent)} (${report.summary.contextModeProxy.totalRawBytes} raw bytes to ${report.summary.contextModeProxy.totalModelVisibleBytes} visible bytes)`,
|
|
160
|
-
`- Public superiority claims allowed: ${report.summary.publicClaimsAllowed ? "yes" : "no"}`,
|
|
161
|
-
"",
|
|
162
|
-
"## Results",
|
|
163
|
-
"",
|
|
164
|
-
"| fixture | category | mode | pass | answer | facts | recovery | tool calls | raw/visible bytes | reduction | storage bytes | latency p50/p95 ms | notes |",
|
|
165
|
-
"| --- | --- | --- | --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | --- |",
|
|
166
|
-
];
|
|
167
|
-
for (const fixture of report.fixtures) {
|
|
168
|
-
for (const result of fixture.results) {
|
|
169
|
-
const passed = result.exactFactsPreserved && result.recoveryAvailable;
|
|
170
|
-
lines.push(
|
|
171
|
-
`| ${escapeTable(fixture.id)} | ${fixture.category} | ${result.mode} | ${passed ? "pass" : "fail"} | ${result.answerAccuracy ? "yes" : "no"} | ${result.exactFactsPreserved ? "yes" : "no"} | ${result.recoveryAvailable ? "yes" : "no"} | ${result.toolCalls} | ${result.rawBytes}/${result.modelVisibleBytes} | ${formatPercent(result.modelVisibleReductionPercent)} | ${result.storageBytes} | ${result.latencyMs.p50.toFixed(2)}/${result.latencyMs.p95.toFixed(2)} | ${escapeTable(result.notes.join("; "))} |`,
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
lines.push(
|
|
176
|
-
"",
|
|
177
|
-
"## Decision",
|
|
178
|
-
"",
|
|
179
|
-
"No public Context Mode superiority claim is made from this benchmark. It is a normalized fixture comparison used to catch token/recovery regressions before any public naming or docs migration.",
|
|
180
|
-
"",
|
|
181
|
-
);
|
|
182
|
-
return lines.join("\n");
|
|
183
|
-
}
|
|
184
|
-
export async function writeContextModeNormalizedBenchmarkReports(report, markdownReportPath, options = {}) {
|
|
185
|
-
return writeBenchmarkReportPair({
|
|
186
|
-
report,
|
|
187
|
-
markdownReportPath,
|
|
188
|
-
jsonReportPath: options.jsonReportPath,
|
|
189
|
-
renderMarkdown: renderContextModeNormalizedBenchmarkReport,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
function contextModeFixtureDefinitions(env) {
|
|
193
|
-
return [
|
|
194
|
-
commandFixture(env),
|
|
195
|
-
docsFixture(env),
|
|
196
|
-
logsFixture(env),
|
|
197
|
-
jsonTableFixture(env),
|
|
198
|
-
repoSearchFixture(env),
|
|
199
|
-
batchFixture(env),
|
|
200
|
-
];
|
|
201
|
-
}
|
|
202
|
-
function commandFixture(env) {
|
|
203
|
-
const raw = `${TEST_STDOUT}\n${BUILD_STDERR}\n`;
|
|
204
|
-
const expectedFacts = ["AUTH_TOKEN scoped", "TS2322", "Tests: 1 failed"];
|
|
205
|
-
return {
|
|
206
|
-
id: "command-test-build-output",
|
|
207
|
-
title: "Command test/build output",
|
|
208
|
-
category: "command",
|
|
209
|
-
rawBytes: byteLength(raw),
|
|
210
|
-
expectedFacts,
|
|
211
|
-
modes: {
|
|
212
|
-
"context-mode-normalized-proxy": async () =>
|
|
213
|
-
contextModeProxyObservation(env, {
|
|
214
|
-
id: "command-test-build-output",
|
|
215
|
-
raw,
|
|
216
|
-
expectedFacts,
|
|
217
|
-
toolCalls: 1,
|
|
218
|
-
title: "run command and store raw output",
|
|
219
|
-
}),
|
|
220
|
-
"freeflow-owned-tools": async () =>
|
|
221
|
-
freeflowRunObservation(env, {
|
|
222
|
-
command: "npm test -- --runInBand",
|
|
223
|
-
raw,
|
|
224
|
-
expectedFacts,
|
|
225
|
-
goal: "verification",
|
|
226
|
-
}),
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function docsFixture(env) {
|
|
231
|
-
const expectedFacts = ["outputId", "line-range recovery", "Metadata-only"];
|
|
232
|
-
return {
|
|
233
|
-
id: "docs-markdown-query",
|
|
234
|
-
title: "Docs/markdown query",
|
|
235
|
-
category: "docs",
|
|
236
|
-
rawBytes: byteLength(DOC_TEXT),
|
|
237
|
-
expectedFacts,
|
|
238
|
-
modes: {
|
|
239
|
-
"context-mode-normalized-proxy": async () =>
|
|
240
|
-
contextModeProxyObservation(env, {
|
|
241
|
-
id: "docs-markdown-query",
|
|
242
|
-
raw: DOC_TEXT,
|
|
243
|
-
expectedFacts,
|
|
244
|
-
toolCalls: 1,
|
|
245
|
-
title: "read markdown and store raw document",
|
|
246
|
-
}),
|
|
247
|
-
"freeflow-owned-tools": async () =>
|
|
248
|
-
freeflowSearchObservation(env, {
|
|
249
|
-
rawBytes: byteLength(DOC_TEXT),
|
|
250
|
-
expectedFacts,
|
|
251
|
-
query: "outputId line-range recovery Metadata-only exact raw recovery",
|
|
252
|
-
}),
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
function logsFixture(env) {
|
|
257
|
-
const expectedFacts = ["RATE_LIMIT", "retryAfter=30", "req_critical_42"];
|
|
258
|
-
return {
|
|
259
|
-
id: "large-log-search",
|
|
260
|
-
title: "Large log search",
|
|
261
|
-
category: "logs",
|
|
262
|
-
rawBytes: byteLength(SERVICE_LOG),
|
|
263
|
-
expectedFacts,
|
|
264
|
-
modes: {
|
|
265
|
-
"context-mode-normalized-proxy": async () =>
|
|
266
|
-
contextModeProxyObservation(env, {
|
|
267
|
-
id: "large-log-search",
|
|
268
|
-
raw: SERVICE_LOG,
|
|
269
|
-
expectedFacts,
|
|
270
|
-
toolCalls: 1,
|
|
271
|
-
title: "search log and store raw log",
|
|
272
|
-
}),
|
|
273
|
-
"freeflow-owned-tools": async () =>
|
|
274
|
-
freeflowRunObservation(env, {
|
|
275
|
-
command: "tail -n 250 service.log",
|
|
276
|
-
raw: SERVICE_LOG,
|
|
277
|
-
expectedFacts,
|
|
278
|
-
goal: "diagnose rate limit log spike",
|
|
279
|
-
filters: { include: ["RATE_LIMIT", "req_critical_42"], maxLines: 5 },
|
|
280
|
-
}),
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function jsonTableFixture(env) {
|
|
285
|
-
const expectedFacts = ["JSON_TABLE_NEEDLE", "invoice_total", "12345"];
|
|
286
|
-
return {
|
|
287
|
-
id: "json-table-output",
|
|
288
|
-
title: "JSON/table-like output",
|
|
289
|
-
category: "json-csv",
|
|
290
|
-
rawBytes: byteLength(JSON_TABLE),
|
|
291
|
-
expectedFacts,
|
|
292
|
-
modes: {
|
|
293
|
-
"context-mode-normalized-proxy": async () =>
|
|
294
|
-
contextModeProxyObservation(env, {
|
|
295
|
-
id: "json-table-output",
|
|
296
|
-
raw: JSON_TABLE,
|
|
297
|
-
expectedFacts,
|
|
298
|
-
toolCalls: 1,
|
|
299
|
-
title: "summarize JSON/table and store raw payload",
|
|
300
|
-
}),
|
|
301
|
-
"freeflow-owned-tools": async () =>
|
|
302
|
-
freeflowRunObservation(env, {
|
|
303
|
-
command: "node scripts/dump-invoices.js",
|
|
304
|
-
raw: JSON_TABLE,
|
|
305
|
-
expectedFacts,
|
|
306
|
-
goal: "inspect invoice JSON table",
|
|
307
|
-
filters: { include: ["JSON_TABLE_NEEDLE", "12345"], maxLines: 8 },
|
|
308
|
-
}),
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
function repoSearchFixture(env) {
|
|
313
|
-
const rawBytes = byteLength(`${REPO_CODE}\n${GENERATED_DECOY}\n`);
|
|
314
|
-
const expectedFacts = ["RequireEscalated", "approval required", "WithAdditionalPermissions"];
|
|
315
|
-
return {
|
|
316
|
-
id: "repo-text-search-generated-decoy",
|
|
317
|
-
title: "Repo text search with generated decoy",
|
|
318
|
-
category: "repo-search",
|
|
319
|
-
rawBytes,
|
|
320
|
-
expectedFacts,
|
|
321
|
-
modes: {
|
|
322
|
-
"context-mode-normalized-proxy": async () =>
|
|
323
|
-
contextModeProxyObservation(env, {
|
|
324
|
-
id: "repo-text-search-generated-decoy",
|
|
325
|
-
raw: REPO_CODE,
|
|
326
|
-
rawBytes,
|
|
327
|
-
expectedFacts,
|
|
328
|
-
toolCalls: 1,
|
|
329
|
-
title: "repo search result stored by proxy",
|
|
330
|
-
}),
|
|
331
|
-
"freeflow-owned-tools": async () =>
|
|
332
|
-
freeflowSearchObservation(env, {
|
|
333
|
-
rawBytes,
|
|
334
|
-
expectedFacts,
|
|
335
|
-
query: "RequireEscalated approval required WithAdditionalPermissions",
|
|
336
|
-
}),
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
function batchFixture(env) {
|
|
341
|
-
const raw = [TEST_STDOUT, SERVICE_LOG, DOC_TEXT].join("\n---\n");
|
|
342
|
-
const expectedFacts = ["AUTH_TOKEN scoped", "RATE_LIMIT", "details.result.steps"];
|
|
343
|
-
return {
|
|
344
|
-
id: "batch-multi-command-query",
|
|
345
|
-
title: "Batch multi-query/multi-command case",
|
|
346
|
-
category: "batch",
|
|
347
|
-
rawBytes: byteLength(raw),
|
|
348
|
-
expectedFacts,
|
|
349
|
-
modes: {
|
|
350
|
-
"context-mode-normalized-proxy": async () =>
|
|
351
|
-
contextModeProxyObservation(env, {
|
|
352
|
-
id: "batch-multi-command-query",
|
|
353
|
-
raw,
|
|
354
|
-
expectedFacts: ["AUTH_TOKEN scoped", "RATE_LIMIT", "Recovery Contract"],
|
|
355
|
-
toolCalls: 3,
|
|
356
|
-
title: "three independent stored context operations",
|
|
357
|
-
}),
|
|
358
|
-
"freeflow-owned-tools": async () =>
|
|
359
|
-
freeflowBatchObservation(env, {
|
|
360
|
-
rawBytes: byteLength(raw),
|
|
361
|
-
expectedFacts,
|
|
362
|
-
}),
|
|
363
|
-
},
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
async function runFixtureMode(fixture, mode, env, iterations) {
|
|
367
|
-
const latencies = [];
|
|
368
|
-
let observation;
|
|
369
|
-
for (let index = 0; index < iterations; index += 1) {
|
|
370
|
-
const startedAt = performance.now();
|
|
371
|
-
observation = await fixture.modes[mode](env);
|
|
372
|
-
latencies.push(performance.now() - startedAt);
|
|
373
|
-
}
|
|
374
|
-
if (!observation) {
|
|
375
|
-
observation = {
|
|
376
|
-
toolPathUsed: mode,
|
|
377
|
-
rawBytes: fixture.rawBytes,
|
|
378
|
-
modelVisibleText: "",
|
|
379
|
-
detailsPayload: {},
|
|
380
|
-
storageBytes: 0,
|
|
381
|
-
toolCalls: 0,
|
|
382
|
-
recoveryAvailable: false,
|
|
383
|
-
recoveryDetail: "No observation produced.",
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
const modelVisibleBytes = byteLength(observation.modelVisibleText);
|
|
387
|
-
const detailsPayloadBytes = byteLength(JSON.stringify(observation.detailsPayload));
|
|
388
|
-
const answerAccuracy = fixture.expectedFacts.every((fact) => observation.modelVisibleText.includes(fact));
|
|
389
|
-
const exactFactsPreserved = answerAccuracy || observation.recoveryAvailable;
|
|
390
|
-
return {
|
|
391
|
-
mode,
|
|
392
|
-
toolPathUsed: observation.toolPathUsed,
|
|
393
|
-
rawBytes: observation.rawBytes,
|
|
394
|
-
modelVisibleBytes,
|
|
395
|
-
modelVisibleTokensApprox: approximateTokens(modelVisibleBytes),
|
|
396
|
-
detailsPayloadBytes,
|
|
397
|
-
storageBytes: observation.storageBytes,
|
|
398
|
-
toolCalls: observation.toolCalls,
|
|
399
|
-
latencyMs: latencySummary(latencies),
|
|
400
|
-
modelVisibleReductionPercent: reductionPercent(observation.rawBytes, modelVisibleBytes),
|
|
401
|
-
answerAccuracy,
|
|
402
|
-
exactFactsPreserved,
|
|
403
|
-
recoveryAvailable: observation.recoveryAvailable,
|
|
404
|
-
recoveryDetail: observation.recoveryDetail,
|
|
405
|
-
notes: observation.notes ?? [],
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
async function contextModeProxyObservation(env, options) {
|
|
409
|
-
const outputId = contextOutputId(options.id, options.raw);
|
|
410
|
-
const path = join(env.contextStoreRoot, `${outputId}.txt`);
|
|
411
|
-
await writeFile(path, options.raw, "utf8");
|
|
412
|
-
const relevant = relevantSnippet(options.raw, options.expectedFacts, COMPACT_CONTEXT_CAP_BYTES);
|
|
413
|
-
const modelVisibleText = [
|
|
414
|
-
`context-mode-proxy ${options.title}`,
|
|
415
|
-
`handle=${outputId}`,
|
|
416
|
-
relevant,
|
|
417
|
-
`recovery: exact raw payload stored at handle=${outputId}`,
|
|
418
|
-
].join("\n");
|
|
419
|
-
return {
|
|
420
|
-
toolPathUsed: "context-mode-normalized-proxy: bounded snippet plus exact synthetic store handle",
|
|
421
|
-
rawBytes: options.rawBytes ?? byteLength(options.raw),
|
|
422
|
-
modelVisibleText,
|
|
423
|
-
detailsPayload: { outputId, path, note: "synthetic normalized proxy, not external runtime" },
|
|
424
|
-
storageBytes: byteLength(options.raw),
|
|
425
|
-
toolCalls: options.toolCalls,
|
|
426
|
-
recoveryAvailable: true,
|
|
427
|
-
recoveryDetail: `Exact fixture payload stored at ${outputId}.`,
|
|
428
|
-
notes: ["normalized proxy, not actual Context Mode runtime"],
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
async function freeflowRunObservation(env, options) {
|
|
432
|
-
const before = await directoryByteSize(env.vaultRoot);
|
|
433
|
-
const result = await freeflowRun(
|
|
434
|
-
{
|
|
435
|
-
command: options.command,
|
|
436
|
-
sessionId: env.sessionId,
|
|
437
|
-
vaultRoot: env.vaultRoot,
|
|
438
|
-
preserve: "important",
|
|
439
|
-
goal: options.goal,
|
|
440
|
-
...(options.filters !== undefined ? { filters: options.filters } : {}),
|
|
441
|
-
},
|
|
442
|
-
env.commandRunner,
|
|
443
|
-
);
|
|
444
|
-
const after = await directoryByteSize(env.vaultRoot);
|
|
445
|
-
return {
|
|
446
|
-
toolPathUsed: "freeflow_run",
|
|
447
|
-
rawBytes: byteLength(options.raw),
|
|
448
|
-
modelVisibleText: freeflowVisibleText(result),
|
|
449
|
-
detailsPayload: result,
|
|
450
|
-
storageBytes: Math.max(0, after - before),
|
|
451
|
-
toolCalls: 1,
|
|
452
|
-
recoveryAvailable: Boolean(result.recovery?.how),
|
|
453
|
-
recoveryDetail: result.recovery?.how ?? "No recovery hint.",
|
|
454
|
-
notes: [result.routing.reason],
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
async function freeflowSearchObservation(env, options) {
|
|
458
|
-
const before = await directoryByteSize(env.vaultRoot);
|
|
459
|
-
const result = await freeflowSearch({
|
|
460
|
-
action: "query",
|
|
461
|
-
source: { kind: "repo", root: env.repoRoot },
|
|
462
|
-
query: options.query,
|
|
463
|
-
topK: 1,
|
|
464
|
-
preserve: "important",
|
|
465
|
-
});
|
|
466
|
-
const after = await directoryByteSize(env.vaultRoot);
|
|
467
|
-
return {
|
|
468
|
-
toolPathUsed: "freeflow_search query repo",
|
|
469
|
-
rawBytes: options.rawBytes,
|
|
470
|
-
modelVisibleText: freeflowVisibleText(result),
|
|
471
|
-
detailsPayload: result,
|
|
472
|
-
storageBytes: Math.max(0, after - before),
|
|
473
|
-
toolCalls: 1,
|
|
474
|
-
recoveryAvailable: Boolean(result.recovery?.how || result.evidence?.some((packet) => packet.expandable)),
|
|
475
|
-
recoveryDetail: result.recovery?.how ?? "Repo evidence can be recovered by exact path and line range.",
|
|
476
|
-
notes: [result.routing.reason],
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
async function freeflowBatchObservation(env, options) {
|
|
480
|
-
const before = await directoryByteSize(env.vaultRoot);
|
|
481
|
-
const result = await freeflowBatch(
|
|
482
|
-
{
|
|
483
|
-
sessionId: env.sessionId,
|
|
484
|
-
vaultRoot: env.vaultRoot,
|
|
485
|
-
preserve: "important",
|
|
486
|
-
concurrency: 3,
|
|
487
|
-
steps: [
|
|
488
|
-
{ id: "test", kind: "run", input: { command: "npm test -- --runInBand", goal: "verification" } },
|
|
489
|
-
{
|
|
490
|
-
id: "log",
|
|
491
|
-
kind: "run",
|
|
492
|
-
input: {
|
|
493
|
-
command: "tail -n 250 service.log",
|
|
494
|
-
goal: "diagnose rate limit log spike",
|
|
495
|
-
filters: { include: ["RATE_LIMIT", "req_critical_42"], maxLines: 5 },
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
{
|
|
499
|
-
id: "doc",
|
|
500
|
-
kind: "search",
|
|
501
|
-
input: {
|
|
502
|
-
action: "query",
|
|
503
|
-
source: { kind: "repo", root: env.repoRoot },
|
|
504
|
-
query: "Recovery Contract outputId line-range",
|
|
505
|
-
topK: 1,
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
],
|
|
509
|
-
},
|
|
510
|
-
env.commandRunner,
|
|
511
|
-
);
|
|
512
|
-
const after = await directoryByteSize(env.vaultRoot);
|
|
513
|
-
const visibleText = `${freeflowVisibleText(result)}\ndetails.result.steps`;
|
|
514
|
-
return {
|
|
515
|
-
toolPathUsed: "freeflow_batch",
|
|
516
|
-
rawBytes: options.rawBytes,
|
|
517
|
-
modelVisibleText: visibleText,
|
|
518
|
-
detailsPayload: result,
|
|
519
|
-
storageBytes: Math.max(0, after - before),
|
|
520
|
-
toolCalls: 1,
|
|
521
|
-
recoveryAvailable: Boolean(result.recovery?.how),
|
|
522
|
-
recoveryDetail: result.recovery?.how ?? "Inspect child step recovery hints.",
|
|
523
|
-
notes: [result.routing.reason],
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
function summarizeContextModeReport(fixtures) {
|
|
527
|
-
const freeflow = summarizeMode(fixtures, "freeflow-owned-tools");
|
|
528
|
-
const contextModeProxy = summarizeMode(fixtures, "context-mode-normalized-proxy");
|
|
529
|
-
const freeflowBetterModelVisibleBytes = fixtures.filter((fixture) => {
|
|
530
|
-
const freeflowResult = fixture.results.find((result) => result.mode === "freeflow-owned-tools");
|
|
531
|
-
const proxyResult = fixture.results.find((result) => result.mode === "context-mode-normalized-proxy");
|
|
532
|
-
return Boolean(freeflowResult && proxyResult && freeflowResult.modelVisibleBytes < proxyResult.modelVisibleBytes);
|
|
533
|
-
}).length;
|
|
534
|
-
const freeflowBetterToolCalls = fixtures.filter((fixture) => {
|
|
535
|
-
const freeflowResult = fixture.results.find((result) => result.mode === "freeflow-owned-tools");
|
|
536
|
-
const proxyResult = fixture.results.find((result) => result.mode === "context-mode-normalized-proxy");
|
|
537
|
-
return Boolean(freeflowResult && proxyResult && freeflowResult.toolCalls < proxyResult.toolCalls);
|
|
538
|
-
}).length;
|
|
539
|
-
return {
|
|
540
|
-
fixtures: fixtures.length,
|
|
541
|
-
modeResults: fixtures.reduce((count, fixture) => count + fixture.results.length, 0),
|
|
542
|
-
freeflow,
|
|
543
|
-
contextModeProxy,
|
|
544
|
-
freeflowBetterModelVisibleBytes,
|
|
545
|
-
freeflowBetterToolCalls,
|
|
546
|
-
allFreeflowFactsPreserved: freeflow.exactFactsPreserved === fixtures.length,
|
|
547
|
-
allFreeflowRecoveryAvailable: freeflow.recoveryAvailable === fixtures.length,
|
|
548
|
-
publicClaimsAllowed: false,
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
function summarizeMode(fixtures, mode) {
|
|
552
|
-
const results = fixtures
|
|
553
|
-
.map((fixture) => fixture.results.find((result) => result.mode === mode))
|
|
554
|
-
.filter((result) => Boolean(result));
|
|
555
|
-
const totalRawBytes = results.reduce((sum, result) => sum + result.rawBytes, 0);
|
|
556
|
-
const totalModelVisibleBytes = results.reduce((sum, result) => sum + result.modelVisibleBytes, 0);
|
|
557
|
-
return results.reduce(
|
|
558
|
-
(summary, result) => {
|
|
559
|
-
const passed = result.exactFactsPreserved && result.recoveryAvailable;
|
|
560
|
-
if (passed) {
|
|
561
|
-
summary.passed += 1;
|
|
562
|
-
} else {
|
|
563
|
-
summary.failed += 1;
|
|
564
|
-
}
|
|
565
|
-
if (result.answerAccuracy) {
|
|
566
|
-
summary.answerAccurate += 1;
|
|
567
|
-
}
|
|
568
|
-
if (result.exactFactsPreserved) {
|
|
569
|
-
summary.exactFactsPreserved += 1;
|
|
570
|
-
}
|
|
571
|
-
if (result.recoveryAvailable) {
|
|
572
|
-
summary.recoveryAvailable += 1;
|
|
573
|
-
}
|
|
574
|
-
summary.totalToolCalls += result.toolCalls;
|
|
575
|
-
summary.totalStorageBytes += result.storageBytes;
|
|
576
|
-
return summary;
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
passed: 0,
|
|
580
|
-
failed: 0,
|
|
581
|
-
answerAccurate: 0,
|
|
582
|
-
exactFactsPreserved: 0,
|
|
583
|
-
recoveryAvailable: 0,
|
|
584
|
-
totalRawBytes,
|
|
585
|
-
totalModelVisibleBytes,
|
|
586
|
-
totalModelVisibleTokensApprox: approximateTokens(totalModelVisibleBytes),
|
|
587
|
-
totalToolCalls: 0,
|
|
588
|
-
totalStorageBytes: 0,
|
|
589
|
-
weightedModelVisibleReductionPercent: reductionPercent(totalRawBytes, totalModelVisibleBytes),
|
|
590
|
-
},
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
|
-
async function setupRepo(repoRoot) {
|
|
594
|
-
await mkdir(join(repoRoot, "plugin-docs"), { recursive: true });
|
|
595
|
-
await mkdir(join(repoRoot, "src"), { recursive: true });
|
|
596
|
-
await mkdir(join(repoRoot, "graphify-out"), { recursive: true });
|
|
597
|
-
await writeFile(join(repoRoot, "plugin-docs/output-router.md"), DOC_TEXT, "utf8");
|
|
598
|
-
await writeFile(join(repoRoot, "src/sandbox.ts"), REPO_CODE, "utf8");
|
|
599
|
-
await writeFile(join(repoRoot, "graphify-out/graph.html"), GENERATED_DECOY, "utf8");
|
|
600
|
-
}
|
|
601
|
-
function fixtureCommandRunner() {
|
|
602
|
-
return {
|
|
603
|
-
async run(request) {
|
|
604
|
-
const command = Array.isArray(request.command) ? request.command.join(" ") : request.command;
|
|
605
|
-
if (command.includes("npm test")) {
|
|
606
|
-
return {
|
|
607
|
-
stdout: `${TEST_STDOUT}\n`,
|
|
608
|
-
stderr: `${BUILD_STDERR}\n`,
|
|
609
|
-
combined: `STDOUT:\n${TEST_STDOUT}\n\nSTDERR:\n${BUILD_STDERR}\n`,
|
|
610
|
-
executionStatus: "failed",
|
|
611
|
-
exitCode: 1,
|
|
612
|
-
durationMs: 4210,
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
if (command.includes("service.log")) {
|
|
616
|
-
return {
|
|
617
|
-
stdout: `${SERVICE_LOG}\n`,
|
|
618
|
-
stderr: "",
|
|
619
|
-
combined: `${SERVICE_LOG}\n`,
|
|
620
|
-
executionStatus: "success",
|
|
621
|
-
exitCode: 0,
|
|
622
|
-
durationMs: 120,
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
if (command.includes("dump-invoices")) {
|
|
626
|
-
return {
|
|
627
|
-
stdout: `${JSON_TABLE}\n`,
|
|
628
|
-
stderr: "",
|
|
629
|
-
combined: `${JSON_TABLE}\n`,
|
|
630
|
-
executionStatus: "success",
|
|
631
|
-
exitCode: 0,
|
|
632
|
-
durationMs: 80,
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
return {
|
|
636
|
-
stdout: "",
|
|
637
|
-
stderr: `Unknown benchmark command: ${command}\n`,
|
|
638
|
-
combined: `Unknown benchmark command: ${command}\n`,
|
|
639
|
-
executionStatus: "failed",
|
|
640
|
-
exitCode: 127,
|
|
641
|
-
durationMs: 1,
|
|
642
|
-
};
|
|
643
|
-
},
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function freeflowVisibleText(result) {
|
|
647
|
-
const lines = [resultSummary(result), result.routing?.reason ?? ""];
|
|
648
|
-
if ("importantLines" in result && Array.isArray(result.importantLines)) {
|
|
649
|
-
lines.push(...result.importantLines.map((line) => line.excerpt));
|
|
650
|
-
}
|
|
651
|
-
if ("evidence" in result && Array.isArray(result.evidence)) {
|
|
652
|
-
lines.push(...result.evidence.map((packet) => packet.excerpt));
|
|
653
|
-
}
|
|
654
|
-
if ("steps" in result && Array.isArray(result.steps)) {
|
|
655
|
-
lines.push(...result.steps.slice(0, 3).map((step) => `${step.id}:${step.status}:${resultSummary(step.result)}`));
|
|
656
|
-
}
|
|
657
|
-
if (result.recovery?.how) {
|
|
658
|
-
lines.push(result.recovery.how);
|
|
659
|
-
}
|
|
660
|
-
return lines.filter(Boolean).join("\n");
|
|
661
|
-
}
|
|
662
|
-
function resultSummary(result) {
|
|
663
|
-
if (typeof result !== "object" || result === null || !("summary" in result)) {
|
|
664
|
-
return "";
|
|
665
|
-
}
|
|
666
|
-
const summary = result.summary;
|
|
667
|
-
return typeof summary === "string" ? summary : "";
|
|
668
|
-
}
|
|
669
|
-
function relevantSnippet(raw, facts, capBytes) {
|
|
670
|
-
const lines = raw.split(/\r?\n/);
|
|
671
|
-
const selected = [];
|
|
672
|
-
for (const fact of facts) {
|
|
673
|
-
const index = lines.findIndex((line) => line.includes(fact));
|
|
674
|
-
if (index >= 0) {
|
|
675
|
-
const start = Math.max(0, index - 1);
|
|
676
|
-
const end = Math.min(lines.length, index + 2);
|
|
677
|
-
selected.push(...lines.slice(start, end));
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
const deduped = [...new Set(selected.length ? selected : lines.slice(0, 8))];
|
|
681
|
-
return truncateToBytes(deduped.join("\n"), capBytes);
|
|
682
|
-
}
|
|
683
|
-
function truncateToBytes(value, capBytes) {
|
|
684
|
-
if (byteLength(value) <= capBytes) {
|
|
685
|
-
return value;
|
|
686
|
-
}
|
|
687
|
-
let end = Math.min(value.length, capBytes);
|
|
688
|
-
while (end > 0 && byteLength(value.slice(0, end)) > capBytes) {
|
|
689
|
-
end -= 1;
|
|
690
|
-
}
|
|
691
|
-
return `${value.slice(0, end)}\n… [truncated]`;
|
|
692
|
-
}
|
|
693
|
-
async function directoryByteSize(path) {
|
|
694
|
-
try {
|
|
695
|
-
const entries = await readDirectoryRecursive(path);
|
|
696
|
-
let total = 0;
|
|
697
|
-
for (const file of entries) {
|
|
698
|
-
try {
|
|
699
|
-
total += byteLength(await readFile(file, "utf8"));
|
|
700
|
-
} catch {
|
|
701
|
-
// ignore non-text or transient files in benchmark temp dirs
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
return total;
|
|
705
|
-
} catch {
|
|
706
|
-
return 0;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
async function readDirectoryRecursive(path) {
|
|
710
|
-
const { readdir, stat } = await import("node:fs/promises");
|
|
711
|
-
const entries = await readdir(path, { withFileTypes: true });
|
|
712
|
-
const files = [];
|
|
713
|
-
for (const entry of entries) {
|
|
714
|
-
const child = join(path, entry.name);
|
|
715
|
-
if (entry.isDirectory()) {
|
|
716
|
-
files.push(...(await readDirectoryRecursive(child)));
|
|
717
|
-
} else if (entry.isFile()) {
|
|
718
|
-
const childStat = await stat(child);
|
|
719
|
-
if (childStat.size <= 2_000_000) {
|
|
720
|
-
files.push(child);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return files;
|
|
725
|
-
}
|
|
726
|
-
async function createTempDir(prefix) {
|
|
727
|
-
const path = await mkdtemp(resolve(tmpdir(), prefix));
|
|
728
|
-
return {
|
|
729
|
-
path,
|
|
730
|
-
cleanup: async () => {
|
|
731
|
-
await rm(path, { recursive: true, force: true });
|
|
732
|
-
},
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
function contextModeModes() {
|
|
736
|
-
return ["context-mode-normalized-proxy", "freeflow-owned-tools"];
|
|
737
|
-
}
|
|
738
|
-
function contextOutputId(id, raw) {
|
|
739
|
-
return `ctxout_${createHash("sha256").update(`${id}\0${raw}`).digest("hex").slice(0, 24)}`;
|
|
740
|
-
}
|
|
741
|
-
function byteLength(value) {
|
|
742
|
-
return Buffer.byteLength(value, "utf8");
|
|
743
|
-
}
|
|
744
|
-
function defaultReportPath() {
|
|
745
|
-
return resolve(process.cwd(), "router/evals/reports/context-mode-normalized-benchmark-1-report.md");
|
|
746
|
-
}
|
|
747
|
-
async function runCli() {
|
|
748
|
-
const { iterations, reportPath, jsonReportPath } = parseBenchmarkCliArgs(process.argv.slice(2), {
|
|
749
|
-
reportPath: defaultReportPath(),
|
|
750
|
-
});
|
|
751
|
-
const options = {};
|
|
752
|
-
if (iterations !== undefined) {
|
|
753
|
-
options.iterations = iterations;
|
|
754
|
-
}
|
|
755
|
-
const report = await runContextModeNormalizedBenchmarks(options);
|
|
756
|
-
const reports = await writeContextModeNormalizedBenchmarkReports(report, reportPath, {
|
|
757
|
-
jsonReportPath: jsonReportPath === undefined ? defaultJsonRunReportPath(reportPath) : jsonReportPath,
|
|
758
|
-
});
|
|
759
|
-
const shortId = createHash("sha256").update(JSON.stringify(report.summary)).digest("hex").slice(0, 8);
|
|
760
|
-
console.log(
|
|
761
|
-
`Freeflow Context Mode normalized benchmark ${shortId}: freeflow ${report.summary.freeflow.passed}/${report.summary.fixtures}, proxy ${report.summary.contextModeProxy.passed}/${report.summary.fixtures}`,
|
|
762
|
-
);
|
|
763
|
-
console.log(`Markdown report: ${reports.markdown}`);
|
|
764
|
-
if (reports.json) {
|
|
765
|
-
console.log(`JSON run data: ${reports.json}`);
|
|
766
|
-
}
|
|
767
|
-
if (report.summary.freeflow.failed > 0 || report.summary.contextModeProxy.failed > 0) {
|
|
768
|
-
process.exitCode = 1;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
772
|
-
await runCli();
|
|
773
|
-
}
|