@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
|
@@ -4,7 +4,9 @@ set -euo pipefail
|
|
|
4
4
|
ROOT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")/../.." && pwd)"
|
|
5
5
|
HOOK_PATH="$ROOT_DIR/hooks/freeflow-runtime-context.mjs"
|
|
6
6
|
HOOKS_JSON="$ROOT_DIR/hooks/hooks.json"
|
|
7
|
-
|
|
7
|
+
CORE_PROMPT="$ROOT_DIR/runtime/prompts/core.md"
|
|
8
|
+
INTERACTION_PROMPT="$ROOT_DIR/runtime/prompts/interaction-contract.md"
|
|
9
|
+
SKILLS_PROMPT="$ROOT_DIR/runtime/prompts/skills.md"
|
|
8
10
|
|
|
9
11
|
fail() {
|
|
10
12
|
echo "runtime-context hook check failed: $*" >&2
|
|
@@ -29,11 +31,9 @@ assert_not_contains() {
|
|
|
29
31
|
|
|
30
32
|
[[ -f "$HOOK_PATH" ]] || fail "missing hook script"
|
|
31
33
|
[[ -f "$HOOKS_JSON" ]] || fail "missing hooks manifest"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
[[ -n "$workflow_owner_line" ]] || fail "canonical Workflow owner sentinel missing"
|
|
36
|
-
[[ -n "$workflow_self_review_line" ]] || fail "canonical Workflow self-review sentinel missing"
|
|
34
|
+
for prompt_file in "$CORE_PROMPT" "$INTERACTION_PROMPT" "$SKILLS_PROMPT"; do
|
|
35
|
+
[[ -f "$prompt_file" ]] || fail "missing runtime prompt fragment: $prompt_file"
|
|
36
|
+
done
|
|
37
37
|
node --check "$HOOK_PATH"
|
|
38
38
|
node -e '
|
|
39
39
|
const fs = require("fs");
|
|
@@ -62,42 +62,55 @@ user_prompt_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$tmp_dir" | node
|
|
|
62
62
|
|
|
63
63
|
for expected in \
|
|
64
64
|
'"hookEventName":"SessionStart"' \
|
|
65
|
-
"# Freeflow
|
|
66
|
-
|
|
65
|
+
"# Freeflow Stable Guidance" \
|
|
66
|
+
"## Mode" \
|
|
67
|
+
"## Shared Terms" \
|
|
68
|
+
"## Three Nested Loops" \
|
|
69
|
+
"## Workflow Cue" \
|
|
70
|
+
"## Action Selection Cue" \
|
|
71
|
+
"## Supported Exit" \
|
|
72
|
+
"# Freeflow Interaction Contract" \
|
|
73
|
+
"Treat questions, criticism, examples, hypotheses, and tentative ideas as" \
|
|
67
74
|
'Configured default: `workflow` (repository)' \
|
|
68
75
|
'Effective mode: `workflow` (configured default, active)' \
|
|
69
|
-
"# Freeflow Interaction Contract" \
|
|
70
|
-
"Answer questions without inferring action." \
|
|
71
|
-
"# Freeflow Workflow Bootstrap" \
|
|
72
|
-
"Use feedback to choose the smallest useful next action." \
|
|
73
|
-
"$workflow_owner_line" \
|
|
74
|
-
"$workflow_self_review_line" \
|
|
75
76
|
'Interaction Contract: enabled' \
|
|
76
77
|
'Skills: enabled'; do
|
|
77
78
|
assert_contains "$codex_output" "$expected" "Codex config-only context"
|
|
78
79
|
done
|
|
79
80
|
assert_not_contains "$codex_output" "Output Router" "Codex host context"
|
|
80
81
|
assert_not_contains "$codex_output" "Output router:" "Codex host context"
|
|
82
|
+
|
|
83
|
+
# The Pi-only capability key is tolerated by shared host validators but is not
|
|
84
|
+
# injected into Codex or Claude runtime context.
|
|
85
|
+
cognitive_dir="$(mktemp -d)"
|
|
86
|
+
mkdir -p "$cognitive_dir/.freeflow"
|
|
87
|
+
printf '{"defaultMode":"workflow","cognitiveRouting":{"enabled":true,"profiles":{"standard":{"provider":"faux","model":"standard","thinkingLevel":"high"},"reasoning":{"provider":"faux","model":"reasoning","thinkingLevel":"max"}}}}\n' >"$cognitive_dir/.freeflow/config.json"
|
|
88
|
+
cognitive_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$cognitive_dir" | node "$HOOK_PATH" SessionStart)"
|
|
89
|
+
assert_contains "$cognitive_output" "# Freeflow Stable Guidance" "Pi-only capability tolerance"
|
|
90
|
+
assert_not_contains "$cognitive_output" "Cognitive Routing" "non-Pi capability isolation"
|
|
91
|
+
rm -rf "$cognitive_dir"
|
|
81
92
|
for excluded_heading in \
|
|
82
93
|
"# Freeflow Runtime Kernel" \
|
|
83
94
|
"## Freeflow Runtime Priority" \
|
|
84
95
|
"## Loaded Mode Contract Skill" \
|
|
85
96
|
"## Loaded Workflow Skill" \
|
|
86
97
|
"## Loaded Decision Gate Skill" \
|
|
98
|
+
"# Freeflow Workflow Bootstrap" \
|
|
87
99
|
"## Discovery-light" \
|
|
88
100
|
"## Loaded Output Router Skill"; do
|
|
89
101
|
assert_not_contains "$codex_output" "$excluded_heading" "default context"
|
|
90
102
|
done
|
|
91
|
-
|
|
92
|
-
[[ "$
|
|
103
|
+
skills_prompt_count="$(grep -Fo '## Shared Terms' <<<"$codex_output" | wc -l | tr -d ' ')"
|
|
104
|
+
[[ "$skills_prompt_count" == "1" ]] || fail "Codex SessionStart should load the Skills prompt exactly once"
|
|
93
105
|
assert_not_contains "$codex_output" "## Conversation Mode Boundary" "workflow context"
|
|
94
106
|
assert_not_contains "$codex_output" "## Strict Workflow Overlay" "workflow context"
|
|
95
107
|
|
|
96
108
|
claude_output="$(printf '{"hook_event_name":"SessionStart","source":"startup","cwd":"%s"}\n' "$tmp_dir" | node "$HOOK_PATH" SessionStart)"
|
|
97
109
|
assert_contains "$claude_output" '"hookEventName":"SessionStart"' "Claude wrapper"
|
|
98
110
|
assert_contains "$claude_output" "# Freeflow Interaction Contract" "Claude config-only context"
|
|
99
|
-
assert_contains "$claude_output" "
|
|
100
|
-
|
|
111
|
+
assert_contains "$claude_output" "## Shared Terms" "Claude skills context"
|
|
112
|
+
assert_not_contains "$claude_output" "# Freeflow Workflow Bootstrap" "Claude first-turn context"
|
|
113
|
+
assert_contains "$claude_output" "Treat questions, criticism, examples, hypotheses, and tentative ideas as" "Claude config-only context"
|
|
101
114
|
|
|
102
115
|
# Session mode changes are host-managed state: they apply before the same model request,
|
|
103
116
|
# survive lifecycle restoration, remain isolated by session id, and never edit repo config.
|
|
@@ -106,9 +119,12 @@ session_id="session/with unsafe path characters"
|
|
|
106
119
|
config_before="$(shasum -a 256 "$tmp_dir/.freeflow/config.json" | awk '{print $1}')"
|
|
107
120
|
mode_change_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Switch to conversation mode."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
|
|
108
121
|
assert_contains "$mode_change_output" '"hookEventName":"UserPromptSubmit"' "same-turn mode change wrapper"
|
|
122
|
+
assert_contains "$mode_change_output" '# Freeflow Runtime State' "same-turn mode change"
|
|
109
123
|
assert_contains "$mode_change_output" 'Session override: `conversation`' "same-turn mode change"
|
|
110
124
|
assert_contains "$mode_change_output" 'Effective mode: `conversation` (session override, active)' "same-turn mode change"
|
|
111
|
-
|
|
125
|
+
assert_not_contains "$mode_change_output" "# Freeflow Mode Update" "same-turn mode change"
|
|
126
|
+
assert_not_contains "$mode_change_output" "## Conversation Mode Boundary" "same-turn mode change"
|
|
127
|
+
assert_not_contains "$mode_change_output" "Do not call write, edit, or mutating tools" "same-turn mode change"
|
|
112
128
|
|
|
113
129
|
restored_mode_output="$(printf '{"hook_event_name":"SessionStart","source":"compact","session_id":"%s","cwd":"%s","model":"gpt-5"}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" SessionStart)"
|
|
114
130
|
assert_contains "$restored_mode_output" 'Configured default: `workflow` (repository)' "restored session mode"
|
|
@@ -129,8 +145,10 @@ default_prompt_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_
|
|
|
129
145
|
[[ -z "$default_prompt_output" ]] || fail "default-mode requests should remain agent-routed configuration decisions"
|
|
130
146
|
|
|
131
147
|
native_change_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"$mode-contract strict-workflow"}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
|
|
148
|
+
assert_contains "$native_change_output" '# Freeflow Runtime State' "Codex native skill mode control"
|
|
132
149
|
assert_contains "$native_change_output" 'Session override: `strict-workflow`' "Codex native skill mode control"
|
|
133
|
-
|
|
150
|
+
assert_not_contains "$native_change_output" "# Freeflow Mode Update" "Codex native skill mode control"
|
|
151
|
+
assert_not_contains "$native_change_output" "## Strict Workflow Overlay" "Codex native skill mode control"
|
|
134
152
|
|
|
135
153
|
claude_data_dir="$(mktemp -d)"
|
|
136
154
|
claude_mode_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"claude-session","cwd":"%s","prompt":"/freeflow:mode-contract conversation"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
|
|
@@ -173,7 +191,7 @@ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-st
|
|
|
173
191
|
malformed_transfer_file="$(find "$claude_data_dir/session-modes/claude-clear" -type f -name '*.json' -print -quit)"
|
|
174
192
|
printf 'null\n' >"$malformed_transfer_file"
|
|
175
193
|
claude_malformed_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-malformed-target","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-malformed" node "$HOOK_PATH" SessionStart)"
|
|
176
|
-
assert_contains "$claude_malformed_clear_output" '# Freeflow
|
|
194
|
+
assert_contains "$claude_malformed_clear_output" '# Freeflow Stable Guidance' "Claude malformed clear transfer"
|
|
177
195
|
assert_contains "$claude_malformed_clear_output" 'Session override: none' "Claude malformed clear transfer"
|
|
178
196
|
[[ ! -e "$malformed_transfer_file" ]] || fail "malformed Claude clear transfer should be consumed"
|
|
179
197
|
|
|
@@ -237,11 +255,12 @@ for source in startup resume clear compact; do
|
|
|
237
255
|
assert_contains "$event_output" '"hookEventName":"SessionStart"' "Claude $source wrapper"
|
|
238
256
|
fi
|
|
239
257
|
assert_contains "$event_output" "# Freeflow Interaction Contract" "$host $source SessionStart context"
|
|
240
|
-
assert_contains "$event_output" "
|
|
258
|
+
assert_contains "$event_output" "## Shared Terms" "$host $source SessionStart context"
|
|
241
259
|
contract_count="$(grep -Fo '# Freeflow Interaction Contract' <<<"$event_output" | wc -l | tr -d ' ')"
|
|
242
|
-
|
|
260
|
+
skills_count="$(grep -Fo '## Shared Terms' <<<"$event_output" | wc -l | tr -d ' ')"
|
|
243
261
|
[[ "$contract_count" == "1" ]] || fail "$host $source SessionStart should load the Interaction Contract exactly once"
|
|
244
|
-
[[ "$
|
|
262
|
+
[[ "$skills_count" == "1" ]] || fail "$host $source SessionStart should load the Skills prompt exactly once"
|
|
263
|
+
assert_not_contains "$event_output" "# Freeflow Workflow Bootstrap" "$host $source SessionStart context"
|
|
245
264
|
done
|
|
246
265
|
done
|
|
247
266
|
|
|
@@ -273,10 +292,10 @@ printf '{"defaultMode":"strict-workflow"}\n' >"$local_dir/.freeflow/local.json"
|
|
|
273
292
|
local_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_dir" | node "$HOOK_PATH" SessionStart)"
|
|
274
293
|
assert_contains "$local_output" 'Configured default: `strict-workflow` (personal)' "local default override"
|
|
275
294
|
assert_contains "$local_output" 'Effective mode: `strict-workflow` (configured default, active)' "local default source"
|
|
276
|
-
|
|
277
|
-
|
|
295
|
+
assert_not_contains "$local_output" "## Strict Workflow Overlay" "local strict overlay"
|
|
296
|
+
assert_not_contains "$local_output" "security, privacy, billing, data loss, migrations, public interfaces" "local strict overlay"
|
|
278
297
|
assert_contains "$local_output" "# Freeflow Interaction Contract" "local default override"
|
|
279
|
-
assert_contains "$local_output" "
|
|
298
|
+
assert_contains "$local_output" "## Shared Terms" "local default override"
|
|
280
299
|
rm -rf "$local_dir"
|
|
281
300
|
|
|
282
301
|
local_interaction_dir="$(mktemp -d)"
|
|
@@ -287,7 +306,7 @@ local_interaction_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_inte
|
|
|
287
306
|
assert_contains "$local_interaction_output" 'Interaction Contract: disabled' "independent local interaction override"
|
|
288
307
|
assert_contains "$local_interaction_output" 'Skills: enabled' "independent local interaction override"
|
|
289
308
|
assert_not_contains "$local_interaction_output" "# Freeflow Interaction Contract" "independent local interaction override"
|
|
290
|
-
assert_contains "$local_interaction_output" "
|
|
309
|
+
assert_contains "$local_interaction_output" "## Shared Terms" "independent local interaction override"
|
|
291
310
|
rm -rf "$local_interaction_dir"
|
|
292
311
|
|
|
293
312
|
local_skills_dir="$(mktemp -d)"
|
|
@@ -299,6 +318,7 @@ assert_contains "$local_skills_output" 'Resolved mode: `conversation` (dormant b
|
|
|
299
318
|
assert_contains "$local_skills_output" 'Interaction Contract: disabled' "local interaction override"
|
|
300
319
|
assert_contains "$local_skills_output" 'Skills: disabled' "local skills override"
|
|
301
320
|
assert_not_contains "$local_skills_output" "# Freeflow Interaction Contract" "local interaction override"
|
|
321
|
+
assert_not_contains "$local_skills_output" "# Freeflow Mode Update" "local skills override"
|
|
302
322
|
assert_not_contains "$local_skills_output" "# Freeflow Workflow Bootstrap" "local skills override"
|
|
303
323
|
rm -rf "$local_skills_dir"
|
|
304
324
|
|
|
@@ -315,17 +335,14 @@ mkdir -p "$local_disabled_dir/.freeflow"
|
|
|
315
335
|
printf '{"enabled":true,"defaultMode":"workflow"}\n' >"$local_disabled_dir/.freeflow/config.json"
|
|
316
336
|
printf '{"enabled":false,"processing":{"unsafeUnsandboxed":{"enabled":true}}}\n' >"$local_disabled_dir/.freeflow/local.json"
|
|
317
337
|
local_disabled_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_disabled_dir" | node "$HOOK_PATH" SessionStart)"
|
|
318
|
-
|
|
319
|
-
assert_contains "$local_disabled_output" 'Configured but inactive: `enabled` is false (personal).' "local master source"
|
|
320
|
-
assert_not_contains "$local_disabled_output" "# Freeflow Interaction Contract" "local master override"
|
|
338
|
+
[[ -z "$local_disabled_output" ]] || fail "local master override should suppress hook prompt delivery"
|
|
321
339
|
rm -rf "$local_disabled_dir"
|
|
322
340
|
|
|
323
341
|
disabled_dir="$(mktemp -d)"
|
|
324
342
|
mkdir -p "$disabled_dir/.freeflow"
|
|
325
343
|
printf '{"enabled":false,"defaultMode":"workflow"}\n' >"$disabled_dir/.freeflow/config.json"
|
|
326
344
|
disabled_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$disabled_dir" | node "$HOOK_PATH" SessionStart)"
|
|
327
|
-
|
|
328
|
-
assert_not_contains "$disabled_output" "# Freeflow Runtime Kernel" "disabled context"
|
|
345
|
+
[[ -z "$disabled_output" ]] || fail "disabled Freeflow should suppress hook prompt delivery"
|
|
329
346
|
rm -rf "$disabled_dir"
|
|
330
347
|
|
|
331
348
|
router_dir="$(mktemp -d)"
|
|
@@ -344,11 +361,11 @@ mkdir -p "$all_dir/.freeflow"
|
|
|
344
361
|
printf '{"defaultMode":"strict-workflow","outputRouter":{"enabled":true}}\n' >"$all_dir/.freeflow/config.json"
|
|
345
362
|
all_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$all_dir" | node "$HOOK_PATH" SessionStart)"
|
|
346
363
|
assert_contains "$all_output" "# Freeflow Interaction Contract" "Codex strict context"
|
|
347
|
-
assert_contains "$all_output" "
|
|
364
|
+
assert_contains "$all_output" "## Shared Terms" "Codex strict context"
|
|
348
365
|
assert_not_contains "$all_output" "Output Router" "Codex strict context"
|
|
349
366
|
assert_not_contains "$all_output" "Output router:" "Codex strict context"
|
|
350
367
|
assert_contains "$all_output" 'Effective mode: `strict-workflow` (configured default, active)' "Codex strict context"
|
|
351
|
-
|
|
368
|
+
assert_not_contains "$all_output" "## Strict Workflow Overlay" "all-capabilities strict overlay"
|
|
352
369
|
rm -rf "$all_dir"
|
|
353
370
|
|
|
354
371
|
conversation_dir="$(mktemp -d)"
|
|
@@ -357,9 +374,9 @@ printf '{"defaultMode":"conversation"}\n' >"$conversation_dir/.freeflow/config.j
|
|
|
357
374
|
conversation_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$conversation_dir" | node "$HOOK_PATH" SessionStart)"
|
|
358
375
|
assert_contains "$conversation_output" 'Configured default: `conversation` (repository)' "conversation context"
|
|
359
376
|
assert_contains "$conversation_output" 'Effective mode: `conversation` (configured default, active)' "conversation context"
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
377
|
+
assert_not_contains "$conversation_output" "## Conversation Mode Boundary" "conversation overlay"
|
|
378
|
+
assert_not_contains "$conversation_output" "Do not call write, edit, or mutating tools" "conversation overlay"
|
|
379
|
+
assert_not_contains "$conversation_output" "an execution skill does not override this boundary" "conversation overlay"
|
|
363
380
|
rm -rf "$conversation_dir"
|
|
364
381
|
|
|
365
382
|
observed_dir="$(mktemp -d)"
|
|
@@ -367,7 +384,7 @@ mkdir -p "$observed_dir/.freeflow"
|
|
|
367
384
|
printf '{"defaultMode":"workflow","outputRouter":{"enabled":true,"observedRouting":{"enabled":true}},"scriptTransform":{"enabled":false}}\n' >"$observed_dir/.freeflow/config.json"
|
|
368
385
|
observed_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$observed_dir" | node "$HOOK_PATH" SessionStart)"
|
|
369
386
|
assert_contains "$observed_output" "# Freeflow Interaction Contract" "Codex context with observed-routing config"
|
|
370
|
-
assert_contains "$observed_output" "
|
|
387
|
+
assert_contains "$observed_output" "## Shared Terms" "Codex context with observed-routing config"
|
|
371
388
|
assert_not_contains "$observed_output" "Output Router" "Codex context with observed-routing config"
|
|
372
389
|
assert_not_contains "$observed_output" "Output router:" "Codex context with observed-routing config"
|
|
373
390
|
rm -rf "$observed_dir"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hassangameryt/freeflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Feedback-based control system for coding agents.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -18,24 +18,33 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"format": "prettier --write .",
|
|
20
20
|
"format:check": "prettier --check .",
|
|
21
|
-
"format:router-dist": "prettier --write router/dist",
|
|
22
21
|
"format:pi-extension-dist": "prettier --write pi-extension/dist",
|
|
23
|
-
"build:router": "tsc -p router/tsconfig.json && npm run format:router-dist",
|
|
24
22
|
"build:pi-extension": "tsc -p pi-extension/tsconfig.json && npm run format:pi-extension-dist",
|
|
25
|
-
"build": "npm run build:
|
|
23
|
+
"build": "npm run build:pi-extension",
|
|
24
|
+
"typecheck": "tsc -p pi-extension/tsconfig.json --noEmit",
|
|
25
|
+
"check": "npm run typecheck && npm run check:source && npm run check:changelog && npm run check:release-workflow && npm run check:docs && npm run format:check && npm run check:release-version && npm run check:commands && npm run check:activation && npm run check:runtime && npm run check:package",
|
|
26
|
+
"check:source": "node scripts/validation/check-source-files.mjs",
|
|
27
|
+
"check:changelog": "node scripts/validation/check-changelog.mjs --structure",
|
|
28
|
+
"check:release-workflow": "node scripts/validation/check-release-workflow.mjs",
|
|
29
|
+
"check:docs": "node scripts/validation/check-docs.mjs",
|
|
30
|
+
"check:release-version": "node scripts/validation/check-release-version.mjs",
|
|
31
|
+
"check:commands": "scripts/validation/audit-command-surface.sh",
|
|
32
|
+
"check:activation": "scripts/validation/check-activation-contract.sh",
|
|
33
|
+
"check:runtime": "hooks/tests/check-runtime-context-hook.sh",
|
|
34
|
+
"check:package": "node scripts/validation/check-package.mjs",
|
|
35
|
+
"check:release-metadata": "scripts/validation/validate-release-metadata.sh --mode prepublish",
|
|
26
36
|
"test:pi-extension": "npm run build && node --test $(find pi-extension/tests -name '*.test.js' | sort)",
|
|
27
|
-
"test:router": "npm run build:router && node --test $(find router/tests -name '*.test.js' | sort)",
|
|
28
37
|
"test:skill-author": "node --test $(find .skill-eval/write-skill/tests -name '*.test.mjs' | sort)",
|
|
29
38
|
"test:skill-eval": "node --test $(find .skill-eval/evaluate-skill/tests -name '*.test.mjs' | sort)",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
+
"test:track-work": "node --test .skill-eval/track-work/tests/*.test.mjs",
|
|
40
|
+
"test:freeflow-snapshot": "node --test scripts/freeflow-snapshot.test.mjs",
|
|
41
|
+
"test:release-utils": "node --test scripts/release-utils.test.mjs",
|
|
42
|
+
"test:changelog": "node --test scripts/validation/check-changelog.test.mjs",
|
|
43
|
+
"test:release-workflow": "node --test scripts/validation/check-release-workflow.test.mjs",
|
|
44
|
+
"test:docs": "node --test scripts/validation/check-docs.test.mjs",
|
|
45
|
+
"release:prepare": "node scripts/release.mjs",
|
|
46
|
+
"release:notes": "node scripts/release-notes.mjs",
|
|
47
|
+
"snapshot:refresh": "node scripts/freeflow-snapshot.mjs"
|
|
39
48
|
},
|
|
40
49
|
"keywords": [
|
|
41
50
|
"pi-package",
|
|
@@ -67,11 +76,10 @@
|
|
|
67
76
|
"README.md",
|
|
68
77
|
"assets/**",
|
|
69
78
|
"capabilities/**",
|
|
79
|
+
"runtime/**",
|
|
70
80
|
"command-surface.json",
|
|
71
81
|
"hooks/**",
|
|
72
82
|
"pi-extension/**",
|
|
73
|
-
"router/dist/**",
|
|
74
|
-
"runtime/**",
|
|
75
83
|
"skills/**"
|
|
76
84
|
],
|
|
77
85
|
"peerDependencies": {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const PROFILE_VALUES = ["standard", "reasoning", "auto"];
|
|
2
|
+
const HISTORY_VALUES = ["history", "history active", "history anomalies"];
|
|
3
|
+
export function cognitiveRoutingProfileCompletions(prefix) {
|
|
4
|
+
const query = prefix ?? "";
|
|
5
|
+
return [...PROFILE_VALUES, ...HISTORY_VALUES]
|
|
6
|
+
.filter((value) => value.startsWith(query))
|
|
7
|
+
.map((value) => ({
|
|
8
|
+
value,
|
|
9
|
+
label: value,
|
|
10
|
+
description:
|
|
11
|
+
value === "auto"
|
|
12
|
+
? "Release the manual hold without changing the model"
|
|
13
|
+
: value === "history"
|
|
14
|
+
? "Show Cognitive Routing transition history"
|
|
15
|
+
: value === "history active"
|
|
16
|
+
? "Show current-branch Cognitive Routing history"
|
|
17
|
+
: value === "history anomalies"
|
|
18
|
+
? "Show Cognitive Routing history anomalies"
|
|
19
|
+
: `Hold ${value} profile manually`,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
function historyOptions(value) {
|
|
23
|
+
if (value === "history") return {};
|
|
24
|
+
if (value === "history active") return { scope: "active-branch" };
|
|
25
|
+
if (value === "history anomalies") return { anomaliesOnly: true };
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function formatHistory(result) {
|
|
29
|
+
const lines = [
|
|
30
|
+
`Cognitive Routing history: ${result.current.profile} · ${result.current.control}`,
|
|
31
|
+
`Events: ${result.events.length}; unresolved=${result.summary.unresolvedCount}; anomalies=${result.summary.anomalyCount}`,
|
|
32
|
+
];
|
|
33
|
+
if (result.summary.latestSemanticEventId) lines.push(`Latest semantic: ${result.summary.latestSemanticEventId}`);
|
|
34
|
+
if (result.summary.latestCompletedEventId) lines.push(`Latest completed: ${result.summary.latestCompletedEventId}`);
|
|
35
|
+
for (const event of result.events) {
|
|
36
|
+
const transition = event.from && event.to ? ` ${event.from} → ${event.to}` : "";
|
|
37
|
+
lines.push(
|
|
38
|
+
`${event.jsonlPosition}: ${event.outcome} · ${event.classification}${transition} · changed=${event.changed} · ${event.id}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
export async function handleCognitiveRoutingProfileCommand(args, ctx, controller) {
|
|
44
|
+
const input = (args ?? "").trim().toLowerCase();
|
|
45
|
+
const [action, ...rest] = input.split(/\s+/);
|
|
46
|
+
if (action !== "profile") return false;
|
|
47
|
+
const value = rest.join(" ");
|
|
48
|
+
const historyView = historyOptions(value);
|
|
49
|
+
if (historyView) {
|
|
50
|
+
const result = ctx.history
|
|
51
|
+
? await ctx.history(historyView)
|
|
52
|
+
: controller?.history
|
|
53
|
+
? controller.history(historyView)
|
|
54
|
+
: undefined;
|
|
55
|
+
if (result) {
|
|
56
|
+
ctx.ui?.notify?.(formatHistory(result), "info");
|
|
57
|
+
} else {
|
|
58
|
+
ctx.ui?.notify?.("Cognitive Routing history is unavailable for this session.", "warning");
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (!PROFILE_VALUES.includes(value)) {
|
|
63
|
+
ctx.ui?.notify?.(
|
|
64
|
+
"Usage: /freeflow profile standard, /freeflow profile reasoning, /freeflow profile auto, or /freeflow profile history [active|anomalies]",
|
|
65
|
+
"warning",
|
|
66
|
+
);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (typeof ctx.isIdle === "function" && !ctx.isIdle()) {
|
|
70
|
+
ctx.ui?.notify?.("Freeflow settings and profile changes are available only while Pi is idle.", "warning");
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (!controller) {
|
|
74
|
+
ctx.ui?.notify?.(
|
|
75
|
+
"Cognitive Routing is unavailable for this session. It may be disabled, unsupported by the Pi host, or suppressed by an explicit startup model selection.",
|
|
76
|
+
"warning",
|
|
77
|
+
);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (value === "auto") {
|
|
81
|
+
const result = await controller.setAutomaticControl();
|
|
82
|
+
if (result.status === "automatic") {
|
|
83
|
+
ctx.ui?.notify?.("Cognitive Routing manual hold released; automatic control is active.", "info");
|
|
84
|
+
} else {
|
|
85
|
+
ctx.ui?.notify?.(`Cognitive Routing could not release the manual hold: ${result.reason}.`, "warning");
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const result = await controller.setManualProfile(value);
|
|
90
|
+
if (result.status === "active") {
|
|
91
|
+
ctx.ui?.notify?.(`Cognitive Routing manual hold set to ${result.profile}.`, "info");
|
|
92
|
+
} else {
|
|
93
|
+
ctx.ui?.notify?.(`Cognitive Routing could not set the manual profile: ${result.reason}.`, "warning");
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COGNITIVE_ROUTING_PROFILE_NAMES,
|
|
3
|
+
COGNITIVE_ROUTING_SESSION_START_CONTROLS,
|
|
4
|
+
DEFAULT_COGNITIVE_ROUTING_SESSION_START,
|
|
5
|
+
} from "./types.js";
|
|
6
|
+
const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
7
|
+
const PROFILE_NAME_SET = new Set(COGNITIVE_ROUTING_PROFILE_NAMES);
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function invalid(source, code, message, profile) {
|
|
12
|
+
return { code, message, ...(profile ? { profile } : {}), source };
|
|
13
|
+
}
|
|
14
|
+
function parseProfile(value, source, name) {
|
|
15
|
+
if (!isRecord(value)) {
|
|
16
|
+
return { error: invalid(source, "invalid_profile", `${source} ${name} profile must be an object`, name) };
|
|
17
|
+
}
|
|
18
|
+
const allowedKeys = new Set(["provider", "model", "thinkingLevel"]);
|
|
19
|
+
const unsupportedKey = Object.keys(value).find((key) => !allowedKeys.has(key));
|
|
20
|
+
if (unsupportedKey) {
|
|
21
|
+
return {
|
|
22
|
+
error: invalid(
|
|
23
|
+
source,
|
|
24
|
+
"unsupported_key",
|
|
25
|
+
`${source} ${name} profile contains unsupported key: ${unsupportedKey}`,
|
|
26
|
+
name,
|
|
27
|
+
),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (typeof value.provider !== "string" || value.provider.trim() === "") {
|
|
31
|
+
return { error: invalid(source, "invalid_profile", `${source} ${name} profile requires provider`, name) };
|
|
32
|
+
}
|
|
33
|
+
if (typeof value.model !== "string" || value.model.trim() === "") {
|
|
34
|
+
return { error: invalid(source, "invalid_profile", `${source} ${name} profile requires model`, name) };
|
|
35
|
+
}
|
|
36
|
+
if (typeof value.thinkingLevel !== "string" || !THINKING_LEVELS.has(value.thinkingLevel)) {
|
|
37
|
+
return {
|
|
38
|
+
error: invalid(source, "invalid_profile", `${source} ${name} profile requires a valid thinkingLevel`, name),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
profile: {
|
|
43
|
+
provider: value.provider,
|
|
44
|
+
model: value.model,
|
|
45
|
+
thinkingLevel: value.thinkingLevel,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function parseSessionStart(value, source) {
|
|
50
|
+
if (!isRecord(value)) {
|
|
51
|
+
return {
|
|
52
|
+
error: invalid(source, "invalid_session_start", `${source} cognitiveRouting.sessionStart must be an object`),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const allowedKeys = new Set(["control", "profile"]);
|
|
56
|
+
const unsupportedKey = Object.keys(value).find((key) => !allowedKeys.has(key));
|
|
57
|
+
if (unsupportedKey) {
|
|
58
|
+
return {
|
|
59
|
+
error: invalid(
|
|
60
|
+
source,
|
|
61
|
+
"unsupported_key",
|
|
62
|
+
`${source} cognitiveRouting.sessionStart contains unsupported key: ${unsupportedKey}`,
|
|
63
|
+
),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
let control;
|
|
67
|
+
if (Object.hasOwn(value, "control")) {
|
|
68
|
+
if (typeof value.control !== "string" || !COGNITIVE_ROUTING_SESSION_START_CONTROLS.includes(value.control)) {
|
|
69
|
+
return {
|
|
70
|
+
error: invalid(
|
|
71
|
+
source,
|
|
72
|
+
"invalid_session_start",
|
|
73
|
+
`${source} cognitiveRouting.sessionStart.control must be automatic or manual`,
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
control = value.control;
|
|
78
|
+
}
|
|
79
|
+
let profile;
|
|
80
|
+
if (Object.hasOwn(value, "profile")) {
|
|
81
|
+
if (typeof value.profile !== "string" || !PROFILE_NAME_SET.has(value.profile)) {
|
|
82
|
+
return {
|
|
83
|
+
error: invalid(
|
|
84
|
+
source,
|
|
85
|
+
"invalid_session_start",
|
|
86
|
+
`${source} cognitiveRouting.sessionStart.profile must be standard or reasoning`,
|
|
87
|
+
),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
profile = value.profile;
|
|
91
|
+
}
|
|
92
|
+
return { sessionStart: { ...(control ? { control } : {}), ...(profile ? { profile } : {}) } };
|
|
93
|
+
}
|
|
94
|
+
function parseLayer(value, source) {
|
|
95
|
+
if (value === undefined) {
|
|
96
|
+
return { present: false, valid: true, profiles: {}, sessionStart: {} };
|
|
97
|
+
}
|
|
98
|
+
if (!isRecord(value)) {
|
|
99
|
+
return {
|
|
100
|
+
present: true,
|
|
101
|
+
valid: false,
|
|
102
|
+
profiles: {},
|
|
103
|
+
sessionStart: {},
|
|
104
|
+
error: invalid(source, "invalid_block", `${source} cognitiveRouting must be an object`),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const allowedKeys = new Set(["enabled", "profiles", "sessionStart"]);
|
|
108
|
+
const unsupportedKey = Object.keys(value).find((key) => !allowedKeys.has(key));
|
|
109
|
+
if (unsupportedKey) {
|
|
110
|
+
return {
|
|
111
|
+
present: true,
|
|
112
|
+
valid: false,
|
|
113
|
+
profiles: {},
|
|
114
|
+
sessionStart: {},
|
|
115
|
+
error: invalid(
|
|
116
|
+
source,
|
|
117
|
+
"unsupported_key",
|
|
118
|
+
`${source} cognitiveRouting contains unsupported key: ${unsupportedKey}`,
|
|
119
|
+
),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
let enabled;
|
|
123
|
+
if (Object.hasOwn(value, "enabled")) {
|
|
124
|
+
if (typeof value.enabled !== "boolean") {
|
|
125
|
+
return {
|
|
126
|
+
present: true,
|
|
127
|
+
valid: false,
|
|
128
|
+
profiles: {},
|
|
129
|
+
sessionStart: {},
|
|
130
|
+
error: invalid(source, "invalid_enabled", `${source} cognitiveRouting.enabled must be a boolean`),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
enabled = value.enabled;
|
|
134
|
+
}
|
|
135
|
+
let sessionStart = {};
|
|
136
|
+
if (Object.hasOwn(value, "sessionStart")) {
|
|
137
|
+
const parsed = parseSessionStart(value.sessionStart, source);
|
|
138
|
+
if (parsed.error)
|
|
139
|
+
return { present: true, valid: false, enabled, profiles: {}, sessionStart: {}, error: parsed.error };
|
|
140
|
+
sessionStart = parsed.sessionStart ?? {};
|
|
141
|
+
}
|
|
142
|
+
const profiles = {};
|
|
143
|
+
if (Object.hasOwn(value, "profiles")) {
|
|
144
|
+
if (!isRecord(value.profiles)) {
|
|
145
|
+
return {
|
|
146
|
+
present: true,
|
|
147
|
+
valid: false,
|
|
148
|
+
profiles: {},
|
|
149
|
+
sessionStart: {},
|
|
150
|
+
error: invalid(source, "invalid_profiles", `${source} cognitiveRouting.profiles must be an object`),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const unsupportedProfile = Object.keys(value.profiles).find((key) => !PROFILE_NAME_SET.has(key));
|
|
154
|
+
if (unsupportedProfile) {
|
|
155
|
+
return {
|
|
156
|
+
present: true,
|
|
157
|
+
valid: false,
|
|
158
|
+
profiles: {},
|
|
159
|
+
sessionStart: {},
|
|
160
|
+
error: invalid(
|
|
161
|
+
source,
|
|
162
|
+
"unsupported_key",
|
|
163
|
+
`${source} cognitiveRouting.profiles contains unsupported profile: ${unsupportedProfile}`,
|
|
164
|
+
),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
for (const name of COGNITIVE_ROUTING_PROFILE_NAMES) {
|
|
168
|
+
if (!Object.hasOwn(value.profiles, name)) continue;
|
|
169
|
+
const parsed = parseProfile(value.profiles[name], source, name);
|
|
170
|
+
if (parsed.error) {
|
|
171
|
+
return { present: true, valid: false, enabled, profiles: {}, sessionStart: {}, error: parsed.error };
|
|
172
|
+
}
|
|
173
|
+
profiles[name] = parsed.profile;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { present: true, valid: true, enabled, profiles, sessionStart };
|
|
177
|
+
}
|
|
178
|
+
export function resolveCognitiveRoutingConfig(repositoryConfig, personalConfig) {
|
|
179
|
+
const repositoryValue = isRecord(repositoryConfig) ? repositoryConfig.cognitiveRouting : undefined;
|
|
180
|
+
const personalValue = isRecord(personalConfig) ? personalConfig.cognitiveRouting : undefined;
|
|
181
|
+
const repository = parseLayer(repositoryValue, "repository");
|
|
182
|
+
const personal = parseLayer(personalValue, "personal");
|
|
183
|
+
const configured = repository.present || personal.present;
|
|
184
|
+
const profiles = {};
|
|
185
|
+
const profileSources = {};
|
|
186
|
+
for (const name of COGNITIVE_ROUTING_PROFILE_NAMES) {
|
|
187
|
+
if (repository.profiles[name]) {
|
|
188
|
+
profiles[name] = repository.profiles[name];
|
|
189
|
+
profileSources[name] = "repository";
|
|
190
|
+
}
|
|
191
|
+
if (personal.profiles[name]) {
|
|
192
|
+
profiles[name] = personal.profiles[name];
|
|
193
|
+
profileSources[name] = "personal";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const enabled = personal.enabled ?? repository.enabled ?? false;
|
|
197
|
+
let enabledSource = "default";
|
|
198
|
+
if (repository.enabled !== undefined) enabledSource = "repository";
|
|
199
|
+
if (personal.enabled !== undefined) enabledSource = "personal";
|
|
200
|
+
const sessionStart = {
|
|
201
|
+
control:
|
|
202
|
+
personal.sessionStart.control ??
|
|
203
|
+
repository.sessionStart.control ??
|
|
204
|
+
DEFAULT_COGNITIVE_ROUTING_SESSION_START.control,
|
|
205
|
+
profile:
|
|
206
|
+
personal.sessionStart.profile ??
|
|
207
|
+
repository.sessionStart.profile ??
|
|
208
|
+
DEFAULT_COGNITIVE_ROUTING_SESSION_START.profile,
|
|
209
|
+
};
|
|
210
|
+
const sessionStartSources = {
|
|
211
|
+
control:
|
|
212
|
+
personal.sessionStart.control !== undefined
|
|
213
|
+
? "personal"
|
|
214
|
+
: repository.sessionStart.control !== undefined
|
|
215
|
+
? "repository"
|
|
216
|
+
: "default",
|
|
217
|
+
profile:
|
|
218
|
+
personal.sessionStart.profile !== undefined
|
|
219
|
+
? "personal"
|
|
220
|
+
: repository.sessionStart.profile !== undefined
|
|
221
|
+
? "repository"
|
|
222
|
+
: "default",
|
|
223
|
+
};
|
|
224
|
+
const error = repository.error ?? personal.error;
|
|
225
|
+
return {
|
|
226
|
+
configured,
|
|
227
|
+
valid: repository.valid && personal.valid,
|
|
228
|
+
enabled,
|
|
229
|
+
enabledSource,
|
|
230
|
+
profiles,
|
|
231
|
+
profileSources,
|
|
232
|
+
sessionStart,
|
|
233
|
+
sessionStartSources,
|
|
234
|
+
...(error ? { error } : {}),
|
|
235
|
+
};
|
|
236
|
+
}
|