@hassangameryt/freeflow 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +148 -154
  5. package/capabilities/cognitive-routing/SKILL.md +229 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +6 -13
  9. package/hooks/freeflow-runtime-context.mjs +35 -91
  10. package/hooks/tests/check-runtime-context-hook.sh +56 -39
  11. package/package.json +24 -16
  12. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  13. package/pi-extension/dist/cognitive-routing/config.js +236 -0
  14. package/pi-extension/dist/cognitive-routing/controller.js +862 -0
  15. package/pi-extension/dist/cognitive-routing/history.js +439 -0
  16. package/pi-extension/dist/cognitive-routing/host.js +126 -0
  17. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  18. package/pi-extension/dist/cognitive-routing/tool.js +211 -0
  19. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  20. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  21. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  22. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  23. package/pi-extension/dist/context-virtualization/state.js +57 -0
  24. package/pi-extension/dist/context-virtualization/types.js +10 -0
  25. package/pi-extension/dist/conversation-history/passages.js +66 -0
  26. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  27. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  28. package/pi-extension/dist/conversation-history/search.js +56 -0
  29. package/pi-extension/dist/conversation-history/types.js +1 -0
  30. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  31. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  32. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  33. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  34. package/pi-extension/dist/freeflow-context/types.js +12 -0
  35. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  36. package/pi-extension/dist/index.js +516 -98
  37. package/pi-extension/dist/runtime/runtime-context.js +318 -271
  38. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  39. package/pi-extension/dist/settings/settings-tui.js +78 -0
  40. package/pi-extension/dist/settings/settings-ui.js +593 -563
  41. package/pi-extension/dist/ui/text-component.js +57 -0
  42. package/pi-extension/src/cognitive-routing/commands.ts +119 -0
  43. package/pi-extension/src/cognitive-routing/config.ts +307 -0
  44. package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
  45. package/pi-extension/src/cognitive-routing/history.ts +638 -0
  46. package/pi-extension/src/cognitive-routing/host.ts +172 -0
  47. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  48. package/pi-extension/src/cognitive-routing/tool.ts +272 -0
  49. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  50. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  51. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  52. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  53. package/pi-extension/src/context-virtualization/state.ts +79 -0
  54. package/pi-extension/src/context-virtualization/types.ts +92 -0
  55. package/pi-extension/src/conversation-history/passages.ts +81 -0
  56. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  57. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  58. package/pi-extension/src/conversation-history/search.ts +79 -0
  59. package/pi-extension/src/conversation-history/types.ts +31 -0
  60. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  61. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  62. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  63. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  64. package/pi-extension/src/freeflow-context/types.ts +70 -0
  65. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  66. package/pi-extension/src/index.ts +553 -101
  67. package/pi-extension/src/runtime/runtime-context.ts +339 -281
  68. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  69. package/pi-extension/src/settings/settings-tui.ts +93 -0
  70. package/pi-extension/src/settings/settings-ui.ts +700 -613
  71. package/pi-extension/src/ui/text-component.ts +66 -0
  72. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  73. package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
  74. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
  75. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  76. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  77. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  78. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
  79. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  80. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  81. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  82. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  83. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  84. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  85. package/pi-extension/tests/integration/pi-extension.test.js +927 -2048
  86. package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
  87. package/runtime/prompts/cognitive-routing.md +9 -0
  88. package/runtime/prompts/context-virtualization.md +3 -0
  89. package/runtime/prompts/conversation-history.md +3 -0
  90. package/runtime/prompts/core.md +13 -0
  91. package/runtime/prompts/interaction-contract.md +7 -0
  92. package/runtime/prompts/skills.md +65 -0
  93. package/skills/action-selection/SKILL.md +91 -0
  94. package/skills/bypass/SKILL.md +3 -2
  95. package/skills/commit-work/SKILL.md +1 -1
  96. package/skills/commit-work/references/staging-decisions.md +1 -1
  97. package/skills/decision-gate/SKILL.md +1 -1
  98. package/skills/design-for-depth/SKILL.md +14 -33
  99. package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
  100. package/skills/design-for-depth/references/interface-design-loop.md +1 -1
  101. package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
  102. package/skills/diagnose-failure/SKILL.md +20 -14
  103. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
  104. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
  105. package/skills/discuss/SKILL.md +34 -22
  106. package/skills/discuss/references/discussion-continuity.md +71 -0
  107. package/skills/evaluate-skill/SKILL.md +5 -5
  108. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
  109. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  110. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  111. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  112. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  113. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  114. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  115. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  116. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  117. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  118. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  119. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  120. package/skills/execute-work/SKILL.md +18 -15
  121. package/skills/execute-work/references/code-practices.md +1 -1
  122. package/skills/execute-work/references/execute-work-edges.md +42 -0
  123. package/skills/finish-branch/SKILL.md +2 -2
  124. package/skills/finish-branch/references/integration-options.md +1 -1
  125. package/skills/handoff/SKILL.md +3 -3
  126. package/skills/launch-work/SKILL.md +1 -1
  127. package/skills/launch-work/references/launch-readiness.md +2 -2
  128. package/skills/migration-work/SKILL.md +2 -2
  129. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  130. package/skills/mode-contract/SKILL.md +4 -4
  131. package/skills/release-work/SKILL.md +1 -1
  132. package/skills/release-work/references/release-evidence.md +1 -1
  133. package/skills/review-artifact/SKILL.md +28 -26
  134. package/skills/review-work/SKILL.md +28 -26
  135. package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
  136. package/skills/review-work/references/security-risk-lens.md +1 -1
  137. package/skills/setup-freeflow/SKILL.md +3 -1
  138. package/skills/setup-freeflow/references/activation-contract.md +2 -2
  139. package/skills/setup-freeflow/references/host-setup.md +2 -2
  140. package/skills/simplify-code/SKILL.md +1 -1
  141. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  142. package/skills/tdd/SKILL.md +18 -38
  143. package/skills/tdd/references/test-design.md +2 -2
  144. package/skills/track-work/SKILL.md +362 -58
  145. package/skills/track-work/scripts/lib/codec.mjs +570 -0
  146. package/skills/track-work/scripts/lib/commands.mjs +568 -0
  147. package/skills/track-work/scripts/lib/contract.mjs +303 -0
  148. package/skills/track-work/scripts/lib/model.mjs +506 -0
  149. package/skills/track-work/scripts/lib/operations.mjs +965 -0
  150. package/skills/track-work/scripts/lib/record-store.mjs +264 -0
  151. package/skills/track-work/scripts/lib/result.mjs +37 -0
  152. package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
  153. package/skills/track-work/scripts/lib/schema.mjs +277 -0
  154. package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
  155. package/skills/track-work/scripts/lib/views.mjs +443 -0
  156. package/skills/track-work/scripts/lib/workspace.mjs +134 -0
  157. package/skills/track-work/scripts/working-record.mjs +122 -0
  158. package/skills/verify-work/SKILL.md +14 -2
  159. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  160. package/skills/verify-work/references/integration-evidence.md +1 -1
  161. package/skills/verify-work/references/performance-evidence.md +1 -1
  162. package/skills/workflow/SKILL.md +105 -53
  163. package/skills/workflow/references/domain-skill-composition.md +22 -42
  164. package/skills/write-plan/SKILL.md +1 -1
  165. package/skills/write-skill/SKILL.md +3 -2
  166. package/skills/write-skill/references/progressive-disclosure.md +17 -3
  167. package/capabilities/output-router/SKILL.md +0 -206
  168. package/capabilities/output-router/references/safety-policy.md +0 -40
  169. package/capabilities/output-router/references/setup.md +0 -110
  170. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  171. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  172. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  173. package/pi-extension/dist/output-router/renderers.js +0 -1023
  174. package/pi-extension/dist/output-router/router-tools.js +0 -554
  175. package/pi-extension/dist/output-router/schemas.js +0 -383
  176. package/pi-extension/dist/output-router/status.js +0 -744
  177. package/pi-extension/dist/output-router/utils.js +0 -502
  178. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  179. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  180. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  181. package/pi-extension/src/output-router/renderers.ts +0 -1108
  182. package/pi-extension/src/output-router/router-tools.ts +0 -579
  183. package/pi-extension/src/output-router/schemas.ts +0 -387
  184. package/pi-extension/src/output-router/status.ts +0 -791
  185. package/pi-extension/src/output-router/utils.ts +0 -554
  186. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  187. package/pi-extension/tests/output-router/search.test.js +0 -722
  188. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  189. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  190. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  191. package/router/dist/benchmarks/benchmarks.js +0 -979
  192. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  193. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  194. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  195. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  196. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  197. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  198. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  199. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  200. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  201. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  202. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  203. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  204. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  205. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  206. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  207. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  208. package/router/dist/config/config.d.ts +0 -103
  209. package/router/dist/config/config.js +0 -632
  210. package/router/dist/config/router-contract.d.ts +0 -10
  211. package/router/dist/config/router-contract.js +0 -54
  212. package/router/dist/config/schema.d.ts +0 -27
  213. package/router/dist/config/schema.js +0 -648
  214. package/router/dist/config/types.d.ts +0 -573
  215. package/router/dist/config/types.js +0 -41
  216. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  217. package/router/dist/evidence/bounded-evidence.js +0 -323
  218. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  219. package/router/dist/evidence/evidence-range-selector.js +0 -232
  220. package/router/dist/evidence/evidence-search.d.ts +0 -27
  221. package/router/dist/evidence/evidence-search.js +0 -490
  222. package/router/dist/evidence/evidence.d.ts +0 -38
  223. package/router/dist/evidence/evidence.js +0 -200
  224. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  225. package/router/dist/evidence/failure-contracts.js +0 -166
  226. package/router/dist/evidence/line-ranges.d.ts +0 -24
  227. package/router/dist/evidence/line-ranges.js +0 -22
  228. package/router/dist/experiments/local-index.d.ts +0 -64
  229. package/router/dist/experiments/local-index.js +0 -581
  230. package/router/dist/index.d.ts +0 -30
  231. package/router/dist/index.js +0 -32
  232. package/router/dist/local/local-traversal.d.ts +0 -18
  233. package/router/dist/local/local-traversal.js +0 -334
  234. package/router/dist/processing/engine.d.ts +0 -132
  235. package/router/dist/processing/engine.js +0 -533
  236. package/router/dist/processing/reducers.d.ts +0 -280
  237. package/router/dist/processing/reducers.js +0 -1673
  238. package/router/dist/processing/renderers.d.ts +0 -31
  239. package/router/dist/processing/renderers.js +0 -195
  240. package/router/dist/processing/scripts.d.ts +0 -91
  241. package/router/dist/processing/scripts.js +0 -386
  242. package/router/dist/repo/repo-traversal.d.ts +0 -18
  243. package/router/dist/repo/repo-traversal.js +0 -245
  244. package/router/dist/routing/observed-reducers.d.ts +0 -41
  245. package/router/dist/routing/observed-reducers.js +0 -661
  246. package/router/dist/routing/observed-routing.d.ts +0 -26
  247. package/router/dist/routing/observed-routing.js +0 -378
  248. package/router/dist/routing/parsers.d.ts +0 -16
  249. package/router/dist/routing/parsers.js +0 -521
  250. package/router/dist/routing/run-filters.d.ts +0 -60
  251. package/router/dist/routing/run-filters.js +0 -238
  252. package/router/dist/routing/run-reducers.d.ts +0 -49
  253. package/router/dist/routing/run-reducers.js +0 -122
  254. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  255. package/router/dist/sandbox/adapter-roots.js +0 -66
  256. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  257. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  258. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  259. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  260. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  261. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  262. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  263. package/router/dist/sandbox/script-sandbox.js +0 -337
  264. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  265. package/router/dist/setup/script-transform-adapters.js +0 -278
  266. package/router/dist/tools/batch.d.ts +0 -33
  267. package/router/dist/tools/batch.js +0 -586
  268. package/router/dist/tools/run.d.ts +0 -62
  269. package/router/dist/tools/run.js +0 -1304
  270. package/router/dist/tools/search.d.ts +0 -66
  271. package/router/dist/tools/search.js +0 -1364
  272. package/router/dist/transform/engine.d.ts +0 -168
  273. package/router/dist/transform/engine.js +0 -2072
  274. package/router/dist/vault/vault-index.d.ts +0 -125
  275. package/router/dist/vault/vault-index.js +0 -416
  276. package/router/dist/vault/vault.d.ts +0 -138
  277. package/router/dist/vault/vault.js +0 -630
  278. package/runtime/interaction-contract.md +0 -16
  279. package/skills/discuss/references/checkpoints.md +0 -66
  280. package/skills/execute-work/references/execution-loop.md +0 -77
  281. package/skills/track-work/references/working-record-schema.md +0 -378
  282. package/skills/workflow/references/workflow-loop.md +0 -92
  283. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
  284. /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
@@ -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
- WORKFLOW_SKILL="$ROOT_DIR/skills/workflow/SKILL.md"
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
- [[ -f "$WORKFLOW_SKILL" ]] || fail "missing canonical Workflow skill"
33
- workflow_owner_line="$(grep -m1 '^The active agent owns ' "$WORKFLOW_SKILL" || true)"
34
- workflow_self_review_line="$(grep -m1 '^Self-review is silent ' "$WORKFLOW_SKILL" || true)"
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 Runtime Context" \
66
- 'Runtime delivery: confirmed for this lifecycle-hook invocation.' \
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
- workflow_bootstrap_count="$(grep -Fo '# Freeflow Workflow Bootstrap' <<<"$codex_output" | wc -l | tr -d ' ')"
92
- [[ "$workflow_bootstrap_count" == "1" ]] || fail "Codex SessionStart should load Workflow exactly once"
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" "# Freeflow Workflow Bootstrap" "Claude first-turn context"
100
- assert_contains "$claude_output" "Answer questions without inferring action." "Claude config-only context"
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
- assert_contains "$mode_change_output" "## Conversation Mode Boundary" "same-turn mode change"
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
- assert_contains "$native_change_output" "## Strict Workflow Overlay" "Codex native skill mode control"
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 Runtime Context' "Claude malformed clear transfer"
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" "# Freeflow Workflow Bootstrap" "$host $source SessionStart context"
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
- bootstrap_count="$(grep -Fo '# Freeflow Workflow Bootstrap' <<<"$event_output" | wc -l | tr -d ' ')"
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
- [[ "$bootstrap_count" == "1" ]] || fail "$host $source SessionStart should load Workflow exactly once"
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
- assert_contains "$local_output" "## Strict Workflow Overlay" "local strict overlay"
277
- assert_contains "$local_output" "security, privacy, billing, data loss, migrations, public interfaces" "local strict overlay"
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" "# Freeflow Workflow Bootstrap" "local default override"
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" "# Freeflow Workflow Bootstrap" "independent local interaction override"
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
- assert_contains "$local_disabled_output" "# Freeflow Disabled" "local master override"
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
- assert_contains "$disabled_output" "# Freeflow Disabled" "disabled context"
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" "# Freeflow Workflow Bootstrap" "Codex strict context"
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
- assert_contains "$all_output" "## Strict Workflow Overlay" "all-capabilities strict overlay"
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
- assert_contains "$conversation_output" "## Conversation Mode Boundary" "conversation overlay"
361
- assert_contains "$conversation_output" "Do not call write, edit, or mutating tools" "conversation overlay"
362
- assert_contains "$conversation_output" "an execution skill does not override this boundary" "conversation overlay"
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" "# Freeflow Workflow Bootstrap" "Codex context with observed-routing config"
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.5.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:router && npm run build:pi-extension",
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
- "bench:router": "npm run build:router && node router/dist/benchmarks/benchmarks.js",
31
- "bench:router:commands": "npm run build:router && node router/dist/benchmarks/command-benchmarks.js",
32
- "bench:router:context-saving-baseline": "npm run build:router && node router/dist/benchmarks/context-saving-baseline.js",
33
- "bench:router:context-mode-normalized": "npm run build:router && node router/dist/benchmarks/context-mode-normalized-benchmarks.js",
34
- "bench:router:context-mode-real-deep": "npm run build:router && node router/dist/benchmarks/context-mode-real-deep-benchmark.js",
35
- "bench:router:storage-policy": "npm run build:router && node router/dist/benchmarks/storage-policy-benchmarks.js",
36
- "bench:router:index": "npm run build:router && node router/dist/benchmarks/index-benchmarks.js",
37
- "bench:router:vault-index": "npm run build:router && node router/dist/benchmarks/vault-index-benchmarks.js",
38
- "bench:router:codex-qa": "npm run build:router && node router/dist/benchmarks/codex-qa-benchmarks.js"
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
+ }