@hassangameryt/freeflow 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +4 -5
  4. package/README.md +141 -163
  5. package/capabilities/cognitive-routing/SKILL.md +387 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +6 -37
  9. package/hooks/freeflow-runtime-context.mjs +50 -541
  10. package/hooks/hooks.json +1 -26
  11. package/hooks/tests/check-runtime-context-hook.sh +114 -308
  12. package/package.json +27 -17
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +232 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +1292 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +451 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +188 -0
  18. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
  19. package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
  20. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  21. package/pi-extension/dist/cognitive-routing/tool.js +212 -0
  22. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  23. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  24. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  25. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  26. package/pi-extension/dist/context-virtualization/state.js +57 -0
  27. package/pi-extension/dist/context-virtualization/types.js +10 -0
  28. package/pi-extension/dist/conversation-history/passages.js +66 -0
  29. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  30. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  31. package/pi-extension/dist/conversation-history/search.js +56 -0
  32. package/pi-extension/dist/conversation-history/types.js +1 -0
  33. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  34. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  35. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  36. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  37. package/pi-extension/dist/freeflow-context/types.js +12 -0
  38. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  39. package/pi-extension/dist/index.js +564 -129
  40. package/pi-extension/dist/runtime/runtime-context.js +357 -470
  41. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  42. package/pi-extension/dist/settings/settings-tui.js +78 -0
  43. package/pi-extension/dist/settings/settings-ui.js +572 -826
  44. package/pi-extension/dist/ui/text-component.js +57 -0
  45. package/pi-extension/src/cognitive-routing/commands.ts +129 -0
  46. package/pi-extension/src/cognitive-routing/config.ts +303 -0
  47. package/pi-extension/src/cognitive-routing/controller.ts +1628 -0
  48. package/pi-extension/src/cognitive-routing/history.ts +650 -0
  49. package/pi-extension/src/cognitive-routing/host.ts +266 -0
  50. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
  51. package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
  52. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  53. package/pi-extension/src/cognitive-routing/tool.ts +273 -0
  54. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  55. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  56. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  57. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  58. package/pi-extension/src/context-virtualization/state.ts +79 -0
  59. package/pi-extension/src/context-virtualization/types.ts +92 -0
  60. package/pi-extension/src/conversation-history/passages.ts +81 -0
  61. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  62. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  63. package/pi-extension/src/conversation-history/search.ts +79 -0
  64. package/pi-extension/src/conversation-history/types.ts +31 -0
  65. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  66. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  67. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  68. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  69. package/pi-extension/src/freeflow-context/types.ts +70 -0
  70. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  71. package/pi-extension/src/index.ts +597 -135
  72. package/pi-extension/src/runtime/runtime-context.ts +373 -491
  73. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  74. package/pi-extension/src/settings/settings-tui.ts +93 -0
  75. package/pi-extension/src/settings/settings-ui.ts +679 -910
  76. package/pi-extension/src/ui/text-component.ts +66 -0
  77. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  78. package/pi-extension/tests/cognitive-routing/controller.test.js +1128 -0
  79. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1142 -0
  80. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  81. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  82. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  83. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
  84. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
  85. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
  86. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +279 -0
  87. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  88. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  89. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  90. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  91. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  92. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  93. package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
  94. package/pi-extension/tests/integration/pi-extension.test.js +532 -3701
  95. package/pi-extension/tests/integration/prompt-architecture.test.js +350 -0
  96. package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
  97. package/runtime/prompts/cognitive-routing.md +10 -0
  98. package/runtime/prompts/context-virtualization.md +3 -0
  99. package/runtime/prompts/conversation-history.md +3 -0
  100. package/runtime/prompts/core.md +84 -0
  101. package/runtime/prompts/interaction-contract.md +11 -0
  102. package/skills/action-selection/SKILL.md +140 -0
  103. package/skills/action-selection/references/trajectory-stalls.md +71 -0
  104. package/skills/bypass/SKILL.md +109 -38
  105. package/skills/commit-work/SKILL.md +94 -55
  106. package/skills/commit-work/references/git-state-edges.md +94 -0
  107. package/skills/commit-work/references/simple-push.md +92 -0
  108. package/skills/commit-work/references/staging-decisions.md +79 -46
  109. package/skills/decision-gate/SKILL.md +80 -22
  110. package/skills/design-for-depth/SKILL.md +206 -60
  111. package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
  112. package/skills/design-for-depth/references/interface-design-loop.md +77 -97
  113. package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
  114. package/skills/design-for-depth/references/software-design-philosophy.md +128 -174
  115. package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
  116. package/skills/diagnose-failure/SKILL.md +200 -48
  117. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +18 -6
  118. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +13 -5
  119. package/skills/discuss/SKILL.md +119 -65
  120. package/skills/discuss/references/discussion-continuity.md +123 -0
  121. package/skills/evaluate-skill/SKILL.md +166 -52
  122. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +43 -3
  123. package/skills/evaluate-skill/references/evaluation-design.md +56 -30
  124. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  125. package/skills/evaluate-skill/references/review-and-revision.md +62 -21
  126. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  127. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  128. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  129. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  130. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  131. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  132. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  133. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  134. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  135. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  136. package/skills/execute-work/SKILL.md +99 -57
  137. package/skills/execute-work/references/code-practices.md +4 -4
  138. package/skills/execute-work/references/execute-work-edges.md +62 -0
  139. package/skills/finish-branch/SKILL.md +2 -2
  140. package/skills/finish-branch/references/integration-options.md +1 -1
  141. package/skills/handoff/SKILL.md +3 -3
  142. package/skills/launch-work/SKILL.md +1 -1
  143. package/skills/launch-work/references/launch-readiness.md +2 -2
  144. package/skills/migration-work/SKILL.md +2 -2
  145. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  146. package/skills/release-work/SKILL.md +1 -1
  147. package/skills/release-work/references/release-evidence.md +1 -1
  148. package/skills/review-artifact/SKILL.md +129 -66
  149. package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
  150. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +159 -0
  151. package/skills/review-work/SKILL.md +113 -68
  152. package/skills/review-work/references/adjudicate-work-review.md +97 -0
  153. package/skills/review-work/references/independent-work-reviewer-contract.md +139 -0
  154. package/skills/review-work/references/security-risk-lens.md +18 -16
  155. package/skills/setup-freeflow/SKILL.md +16 -17
  156. package/skills/setup-freeflow/references/activation-contract.md +24 -30
  157. package/skills/setup-freeflow/references/host-setup.md +6 -7
  158. package/skills/simplify-code/SKILL.md +97 -39
  159. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  160. package/skills/tdd/SKILL.md +100 -53
  161. package/skills/tdd/references/test-design.md +3 -3
  162. package/skills/track-work/SKILL.md +195 -58
  163. package/skills/track-work/references/working-record-format.md +515 -0
  164. package/skills/track-work/scripts/lib/document.mjs +713 -0
  165. package/skills/track-work/scripts/lib/format.mjs +166 -0
  166. package/skills/track-work/scripts/lib/store.mjs +233 -0
  167. package/skills/track-work/scripts/lib/transitions.mjs +790 -0
  168. package/skills/track-work/scripts/lib/views.mjs +40 -0
  169. package/skills/track-work/scripts/working-record.mjs +316 -0
  170. package/skills/verify-work/SKILL.md +57 -19
  171. package/skills/verify-work/references/browser-runtime-evidence.md +2 -2
  172. package/skills/verify-work/references/integration-evidence.md +1 -7
  173. package/skills/verify-work/references/performance-evidence.md +2 -2
  174. package/skills/workflow/SKILL.md +137 -55
  175. package/skills/workflow/references/domain-skill-composition.md +26 -40
  176. package/skills/write-plan/SKILL.md +1 -1
  177. package/skills/write-skill/SKILL.md +218 -61
  178. package/skills/write-skill/references/activation-boundaries.md +63 -21
  179. package/skills/write-skill/references/agent-first-instructions.md +61 -27
  180. package/skills/write-skill/references/progressive-disclosure.md +97 -33
  181. package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
  182. package/capabilities/output-router/SKILL.md +0 -206
  183. package/capabilities/output-router/references/safety-policy.md +0 -40
  184. package/capabilities/output-router/references/setup.md +0 -110
  185. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  186. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  187. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  188. package/pi-extension/dist/output-router/renderers.js +0 -1023
  189. package/pi-extension/dist/output-router/router-tools.js +0 -554
  190. package/pi-extension/dist/output-router/schemas.js +0 -383
  191. package/pi-extension/dist/output-router/status.js +0 -744
  192. package/pi-extension/dist/output-router/utils.js +0 -502
  193. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  194. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  195. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  196. package/pi-extension/src/output-router/renderers.ts +0 -1108
  197. package/pi-extension/src/output-router/router-tools.ts +0 -579
  198. package/pi-extension/src/output-router/schemas.ts +0 -387
  199. package/pi-extension/src/output-router/status.ts +0 -791
  200. package/pi-extension/src/output-router/utils.ts +0 -554
  201. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  202. package/pi-extension/tests/output-router/search.test.js +0 -722
  203. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  204. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  205. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  206. package/router/dist/benchmarks/benchmarks.js +0 -979
  207. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  208. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  209. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  210. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  211. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  212. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  213. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  214. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  215. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  216. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  217. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  218. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  219. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  220. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  221. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  222. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  223. package/router/dist/config/config.d.ts +0 -103
  224. package/router/dist/config/config.js +0 -632
  225. package/router/dist/config/router-contract.d.ts +0 -10
  226. package/router/dist/config/router-contract.js +0 -54
  227. package/router/dist/config/schema.d.ts +0 -27
  228. package/router/dist/config/schema.js +0 -648
  229. package/router/dist/config/types.d.ts +0 -573
  230. package/router/dist/config/types.js +0 -41
  231. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  232. package/router/dist/evidence/bounded-evidence.js +0 -323
  233. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  234. package/router/dist/evidence/evidence-range-selector.js +0 -232
  235. package/router/dist/evidence/evidence-search.d.ts +0 -27
  236. package/router/dist/evidence/evidence-search.js +0 -490
  237. package/router/dist/evidence/evidence.d.ts +0 -38
  238. package/router/dist/evidence/evidence.js +0 -200
  239. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  240. package/router/dist/evidence/failure-contracts.js +0 -166
  241. package/router/dist/evidence/line-ranges.d.ts +0 -24
  242. package/router/dist/evidence/line-ranges.js +0 -22
  243. package/router/dist/experiments/local-index.d.ts +0 -64
  244. package/router/dist/experiments/local-index.js +0 -581
  245. package/router/dist/index.d.ts +0 -30
  246. package/router/dist/index.js +0 -32
  247. package/router/dist/local/local-traversal.d.ts +0 -18
  248. package/router/dist/local/local-traversal.js +0 -334
  249. package/router/dist/processing/engine.d.ts +0 -132
  250. package/router/dist/processing/engine.js +0 -533
  251. package/router/dist/processing/reducers.d.ts +0 -280
  252. package/router/dist/processing/reducers.js +0 -1673
  253. package/router/dist/processing/renderers.d.ts +0 -31
  254. package/router/dist/processing/renderers.js +0 -195
  255. package/router/dist/processing/scripts.d.ts +0 -91
  256. package/router/dist/processing/scripts.js +0 -386
  257. package/router/dist/repo/repo-traversal.d.ts +0 -18
  258. package/router/dist/repo/repo-traversal.js +0 -245
  259. package/router/dist/routing/observed-reducers.d.ts +0 -41
  260. package/router/dist/routing/observed-reducers.js +0 -661
  261. package/router/dist/routing/observed-routing.d.ts +0 -26
  262. package/router/dist/routing/observed-routing.js +0 -378
  263. package/router/dist/routing/parsers.d.ts +0 -16
  264. package/router/dist/routing/parsers.js +0 -521
  265. package/router/dist/routing/run-filters.d.ts +0 -60
  266. package/router/dist/routing/run-filters.js +0 -238
  267. package/router/dist/routing/run-reducers.d.ts +0 -49
  268. package/router/dist/routing/run-reducers.js +0 -122
  269. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  270. package/router/dist/sandbox/adapter-roots.js +0 -66
  271. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  272. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  273. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  274. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  275. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  276. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  277. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  278. package/router/dist/sandbox/script-sandbox.js +0 -337
  279. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  280. package/router/dist/setup/script-transform-adapters.js +0 -278
  281. package/router/dist/tools/batch.d.ts +0 -33
  282. package/router/dist/tools/batch.js +0 -586
  283. package/router/dist/tools/run.d.ts +0 -62
  284. package/router/dist/tools/run.js +0 -1304
  285. package/router/dist/tools/search.d.ts +0 -66
  286. package/router/dist/tools/search.js +0 -1364
  287. package/router/dist/transform/engine.d.ts +0 -168
  288. package/router/dist/transform/engine.js +0 -2072
  289. package/router/dist/vault/vault-index.d.ts +0 -125
  290. package/router/dist/vault/vault-index.js +0 -416
  291. package/router/dist/vault/vault.d.ts +0 -138
  292. package/router/dist/vault/vault.js +0 -630
  293. package/runtime/interaction-contract.md +0 -16
  294. package/skills/discuss/references/checkpoints.md +0 -66
  295. package/skills/execute-work/references/execution-loop.md +0 -77
  296. package/skills/mode-contract/SKILL.md +0 -104
  297. package/skills/review-artifact/references/reviewer-prompt.md +0 -148
  298. package/skills/review-work/references/reviewer-prompt.md +0 -134
  299. package/skills/track-work/references/working-record-schema.md +0 -378
  300. package/skills/workflow/references/workflow-loop.md +0 -92
  301. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
@@ -1,17 +1,16 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { normalizeFreeflowConfig, normalizeLocalFreeflowConfig } from "../../../router/dist/index.js";
5
- export const VALID_MODES = new Set(["conversation", "workflow", "strict-workflow"]);
4
+ import { resolveCognitiveRoutingState } from "../cognitive-routing/runtime.js";
5
+ import { supportsCognitiveRoutingModelRegistry } from "../cognitive-routing/host.js";
6
6
  export const WORKFLOW_COMMANDS = [
7
7
  { command: "discuss", skill: "discuss" },
8
- { command: "discover", skill: "discuss" },
8
+ { command: "action-selection", skill: "action-selection" },
9
9
  { command: "track-work", skill: "track-work" },
10
10
  { command: "write-spec", skill: "write-spec" },
11
11
  { command: "review-artifact", skill: "review-artifact" },
12
12
  { command: "write-plan", skill: "write-plan" },
13
13
  { command: "execute-work", skill: "execute-work" },
14
- { command: "execute-plan", skill: "execute-work" },
15
14
  { command: "simplify-code", skill: "simplify-code" },
16
15
  { command: "migration-work", skill: "migration-work" },
17
16
  { command: "diagnose-failure", skill: "diagnose-failure" },
@@ -26,6 +25,7 @@ export const WORKFLOW_COMMANDS = [
26
25
  ];
27
26
  export const CONTRIBUTOR_COMMANDS = ["setup-freeflow", "write-skill", "evaluate-skill"];
28
27
  export const FREEFLOW_MODEL_SKILL_NAMES = [
28
+ "action-selection",
29
29
  "bypass",
30
30
  "commit-work",
31
31
  "decision-gate",
@@ -37,7 +37,6 @@ export const FREEFLOW_MODEL_SKILL_NAMES = [
37
37
  "execute-work",
38
38
  "finish-branch",
39
39
  "handoff",
40
- "mode-contract",
41
40
  "release-work",
42
41
  "review-artifact",
43
42
  "review-work",
@@ -52,54 +51,99 @@ export const FREEFLOW_MODEL_SKILL_NAMES = [
52
51
  "write-skill",
53
52
  "write-spec",
54
53
  ];
54
+ export const FREEFLOW_CAPABILITY_SKILL_NAMES = ["cognitive-routing", "context-virtualization", "conversation-history"];
55
55
  export function freeflowSkillPath(skillName) {
56
56
  return fileURLToPath(new URL(`../../../skills/${skillName}/SKILL.md`, import.meta.url));
57
57
  }
58
- export function freeflowModelSkillPaths() {
59
- return FREEFLOW_MODEL_SKILL_NAMES.map((skillName) => freeflowSkillPath(skillName));
58
+ export function freeflowCapabilitySkillPath(skillName) {
59
+ return fileURLToPath(new URL(`../../../capabilities/${skillName}/SKILL.md`, import.meta.url));
60
+ }
61
+ export function freeflowModelSkillPaths(capabilityState = undefined) {
62
+ const paths = FREEFLOW_MODEL_SKILL_NAMES.map((skillName) => freeflowSkillPath(skillName));
63
+ const capabilityStates = {
64
+ "cognitive-routing": capabilityState?.cognitiveRouting,
65
+ "context-virtualization": capabilityState?.contextVirtualization,
66
+ "conversation-history": capabilityState?.conversationHistory,
67
+ };
68
+ for (const skillName of FREEFLOW_CAPABILITY_SKILL_NAMES) {
69
+ if (capabilityStates[skillName]?.effective === true) {
70
+ paths.push(freeflowCapabilitySkillPath(skillName));
71
+ }
72
+ }
73
+ return paths;
60
74
  }
61
- const MODE_STATE_ENTRY = "freeflow-mode";
62
75
  const SESSION_OVERRIDES_ENTRY = "freeflow-session-overrides";
63
- const SESSION_CORE_KEYS = new Set(["enabled", "interactionContract", "skillsEnabled"]);
64
- const RESET_MODE_ARGS = new Set(["reset"]);
65
- export const FREEFLOW_STATUS_TOOL_NAME = "freeflow_status";
66
- export const OUTPUT_ROUTER_TOOL_NAMES = ["freeflow_search", "freeflow_run", "freeflow_batch"];
76
+ const SESSION_CORE_KEYS = new Set(["enabled", "contextVirtualization", "conversationHistory"]);
77
+ export const COGNITIVE_ROUTING_SWITCH_TOOL_NAME = "freeflow_switch_profile";
78
+ export const FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE = "freeflow-runtime-state";
79
+ export const COGNITIVE_ROUTING_RUNTIME_STATE_MESSAGE_TYPE = "freeflow-cognitive-routing-runtime-state";
67
80
  export const WORKFLOW_BOOTSTRAP_MESSAGE_TYPE = "freeflow-workflow-bootstrap";
81
+ export const COGNITIVE_ROUTING_BOOTSTRAP_MESSAGE_TYPE = "freeflow-cognitive-routing-bootstrap";
82
+ export const FREEFLOW_BOOTSTRAP_MESSAGE_TYPE = "freeflow-bootstrap";
68
83
  let runtimeContextCache = null;
69
- let currentModeOverride = null;
70
84
  let currentSessionOverrides = {};
71
- let lastRouterConfigWarningKey = null;
85
+ export function isPromptAvailable(value) {
86
+ return typeof value === "string" && value.trim().length > 0;
87
+ }
88
+ export function hasUsableMandatoryPrompts(freeflowContext) {
89
+ return (
90
+ isPromptAvailable(freeflowContext?.corePrompt) && isPromptAvailable(freeflowContext?.interactionContractPrompt)
91
+ );
92
+ }
93
+ async function readPromptFile(url) {
94
+ try {
95
+ const prompt = await readFile(url, "utf8");
96
+ return isPromptAvailable(prompt) ? prompt.trim() : null;
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
72
101
  async function loadRuntimeContext(capabilityState = undefined) {
73
- const interactionContractEnabled = capabilityState?.interactionContract?.effective === true;
74
- const skillsEnabled = capabilityState?.skills?.effective === true;
75
- const outputRouterEnabled = capabilityState?.outputRouter?.enabled === true;
76
- const [interactionContract, workflowSkill, outputRouterSkill] = await Promise.all([
77
- interactionContractEnabled
78
- ? readFile(new URL("../../../runtime/interaction-contract.md", import.meta.url), "utf8")
102
+ const freeflowEnabled = capabilityState?.enabled === true;
103
+ const contextVirtualizationEnabled = capabilityState?.contextVirtualization?.effective === true;
104
+ const conversationHistoryEnabled = capabilityState?.conversationHistory?.effective === true;
105
+ const cognitiveRoutingEnabled = capabilityState?.cognitiveRouting?.effective === true;
106
+ const [
107
+ corePrompt,
108
+ interactionContractPrompt,
109
+ cognitiveRoutingPrompt,
110
+ contextVirtualizationPrompt,
111
+ conversationHistoryPrompt,
112
+ ] = await Promise.all([
113
+ freeflowEnabled
114
+ ? readPromptFile(new URL("../../../runtime/prompts/core.md", import.meta.url))
115
+ : Promise.resolve(null),
116
+ freeflowEnabled
117
+ ? readPromptFile(new URL("../../../runtime/prompts/interaction-contract.md", import.meta.url))
118
+ : Promise.resolve(null),
119
+ cognitiveRoutingEnabled
120
+ ? readPromptFile(new URL("../../../runtime/prompts/cognitive-routing.md", import.meta.url))
79
121
  : Promise.resolve(null),
80
- skillsEnabled
81
- ? readFile(new URL("../../../skills/workflow/SKILL.md", import.meta.url), "utf8")
122
+ contextVirtualizationEnabled
123
+ ? readPromptFile(new URL("../../../runtime/prompts/context-virtualization.md", import.meta.url))
82
124
  : Promise.resolve(null),
83
- outputRouterEnabled
84
- ? readFile(new URL("../../../capabilities/output-router/SKILL.md", import.meta.url), "utf8")
125
+ conversationHistoryEnabled
126
+ ? readPromptFile(new URL("../../../runtime/prompts/conversation-history.md", import.meta.url))
85
127
  : Promise.resolve(null),
86
128
  ]);
87
- return { interactionContract, workflowSkill, outputRouterSkill };
129
+ return {
130
+ corePrompt,
131
+ interactionContractPrompt,
132
+ cognitiveRoutingPrompt,
133
+ contextVirtualizationPrompt,
134
+ conversationHistoryPrompt,
135
+ };
88
136
  }
89
137
  function runtimeContextCacheSatisfies(capabilityState) {
90
- if (!runtimeContextCache) {
91
- return false;
92
- }
93
- if (capabilityState?.interactionContract?.effective === true && !runtimeContextCache.interactionContract) {
94
- return false;
95
- }
96
- if (capabilityState?.skills?.effective === true && !runtimeContextCache.workflowSkill) {
97
- return false;
98
- }
99
- if (capabilityState?.outputRouter?.enabled === true && !runtimeContextCache.outputRouterSkill) {
100
- return false;
101
- }
102
- return true;
138
+ if (!runtimeContextCache) return false;
139
+ const expected = {
140
+ corePrompt: capabilityState?.enabled === true,
141
+ interactionContractPrompt: capabilityState?.enabled === true,
142
+ cognitiveRoutingPrompt: capabilityState?.cognitiveRouting?.effective === true,
143
+ contextVirtualizationPrompt: capabilityState?.contextVirtualization?.effective === true,
144
+ conversationHistoryPrompt: capabilityState?.conversationHistory?.effective === true,
145
+ };
146
+ return Object.entries(expected).every(([key, required]) => !required || isPromptAvailable(runtimeContextCache[key]));
103
147
  }
104
148
  export async function refreshRuntimeContext(capabilityState = undefined) {
105
149
  runtimeContextCache = await loadRuntimeContext(capabilityState);
@@ -111,67 +155,45 @@ export async function getRuntimeContext(capabilityState = undefined) {
111
155
  }
112
156
  return refreshRuntimeContext(capabilityState);
113
157
  }
114
- function activeSessionEntries(sessionManager) {
115
- try {
116
- if (typeof sessionManager?.buildContextEntries === "function") {
117
- const entries = sessionManager.buildContextEntries();
118
- if (Array.isArray(entries)) {
119
- return entries;
120
- }
121
- }
122
- if (typeof sessionManager?.getEntries === "function") {
123
- const entries = sessionManager.getEntries();
124
- if (Array.isArray(entries)) {
125
- return entries;
126
- }
127
- }
128
- } catch {
129
- // A missing session snapshot should reload useful context rather than suppress it.
158
+ const BOOTSTRAP_COMPONENTS = Object.freeze({
159
+ workflow: "workflow",
160
+ cognitiveRouting: "cognitive-routing",
161
+ });
162
+ function bootstrapEntryComponents(entry) {
163
+ const isCustomMessage = entry?.role === "custom" || entry?.type === "custom_message" || entry?.type === "custom";
164
+ if (!isCustomMessage) return [];
165
+ if (entry.customType === FREEFLOW_BOOTSTRAP_MESSAGE_TYPE) {
166
+ if (Array.isArray(entry.details?.components)) return entry.details.components;
167
+ return [
168
+ ...(entry.content?.includes("<!-- freeflow-bootstrap:workflow -->") ? [BOOTSTRAP_COMPONENTS.workflow] : []),
169
+ ...(entry.content?.includes("<!-- freeflow-bootstrap:cognitive-routing -->")
170
+ ? [BOOTSTRAP_COMPONENTS.cognitiveRouting]
171
+ : []),
172
+ ];
173
+ }
174
+ if (entry.customType === WORKFLOW_BOOTSTRAP_MESSAGE_TYPE) {
175
+ return [BOOTSTRAP_COMPONENTS.workflow];
176
+ }
177
+ if (entry.customType === COGNITIVE_ROUTING_BOOTSTRAP_MESSAGE_TYPE) {
178
+ return [BOOTSTRAP_COMPONENTS.cognitiveRouting];
130
179
  }
131
180
  return [];
132
181
  }
133
- export function workflowBootstrapMessage(freeflowContext, capabilityState, sessionManager) {
134
- if (capabilityState?.skills?.effective !== true || !freeflowContext?.workflowSkill) {
135
- return undefined;
136
- }
137
- const alreadyLoaded = activeSessionEntries(sessionManager).some(
138
- (entry) => entry?.type === "custom_message" && entry?.customType === WORKFLOW_BOOTSTRAP_MESSAGE_TYPE,
139
- );
140
- if (alreadyLoaded) {
141
- return undefined;
142
- }
143
- return {
144
- customType: WORKFLOW_BOOTSTRAP_MESSAGE_TYPE,
145
- content: `# Freeflow Workflow Bootstrap\n\n${freeflowContext.workflowSkill.trim()}`,
146
- display: false,
147
- details: { skill: "workflow", source: "first-turn-bootstrap" },
148
- };
182
+ export function filterBootstrapMessage(message) {
183
+ return bootstrapEntryComponents(message).length === 0 ? message : undefined;
149
184
  }
150
185
  function isRecord(value) {
151
186
  return typeof value === "object" && value !== null && !Array.isArray(value);
152
187
  }
153
188
  function validateCoreConfigFields(value) {
154
- if (value.defaultMode !== undefined && !VALID_MODES.has(value.defaultMode)) {
155
- return `invalid defaultMode: ${JSON.stringify(value.defaultMode)}`;
156
- }
157
189
  if (value.enabled !== undefined && typeof value.enabled !== "boolean") {
158
190
  return "enabled must be a boolean";
159
191
  }
160
- if (value.interactionContract !== undefined && typeof value.interactionContract !== "boolean") {
161
- return "interactionContract must be a boolean";
192
+ if (value.contextVirtualization !== undefined && typeof value.contextVirtualization !== "boolean") {
193
+ return "contextVirtualization must be a boolean";
162
194
  }
163
- if (value.skills !== undefined) {
164
- if (!isRecord(value.skills)) {
165
- return "skills must be an object";
166
- }
167
- for (const key of Object.keys(value.skills)) {
168
- if (key !== "enabled") {
169
- return `unsupported skills config key: ${key}`;
170
- }
171
- }
172
- if (value.skills.enabled !== undefined && typeof value.skills.enabled !== "boolean") {
173
- return "skills.enabled must be a boolean";
174
- }
195
+ if (value.conversationHistory !== undefined && typeof value.conversationHistory !== "boolean") {
196
+ return "conversationHistory must be a boolean";
175
197
  }
176
198
  return null;
177
199
  }
@@ -179,14 +201,15 @@ function validateFreeflowConfigShape(value) {
179
201
  if (!isRecord(value)) {
180
202
  return "config must be a JSON object";
181
203
  }
204
+ // Keep retired capability keys shape-tolerated so existing repositories remain activated; runtime behavior ignores them.
182
205
  const allowedKeys = new Set([
183
206
  "enabled",
184
- "defaultMode",
185
- "interactionContract",
186
- "skills",
187
207
  "outputRouter",
188
208
  "observedRouting",
189
209
  "scriptTransform",
210
+ "cognitiveRouting",
211
+ "contextVirtualization",
212
+ "conversationHistory",
190
213
  ]);
191
214
  for (const key of Object.keys(value)) {
192
215
  if (!allowedKeys.has(key)) {
@@ -206,7 +229,13 @@ function validateFreeflowLocalConfigShape(value) {
206
229
  if (!isRecord(value)) {
207
230
  return "local config must be a JSON object";
208
231
  }
209
- const allowedKeys = new Set(["enabled", "defaultMode", "interactionContract", "skills", "processing"]);
232
+ const allowedKeys = new Set([
233
+ "enabled",
234
+ "processing",
235
+ "cognitiveRouting",
236
+ "contextVirtualization",
237
+ "conversationHistory",
238
+ ]);
210
239
  for (const key of Object.keys(value)) {
211
240
  if (!allowedKeys.has(key)) {
212
241
  return `unsupported top-level local config key: ${key}`;
@@ -275,23 +304,18 @@ function resolveLayeredValue(repository, local, key, fallback) {
275
304
  }
276
305
  function resolveCoreConfig(repository, local) {
277
306
  const enabled = resolveLayeredValue(repository, local, "enabled", true);
278
- const interactionContract = resolveLayeredValue(repository, local, "interactionContract", true);
279
- const defaultMode = resolveLayeredValue(repository, local, "defaultMode", "workflow");
280
- const repositorySkills = isRecord(repository.skills) ? repository.skills : {};
281
- const localSkills = isRecord(local.skills) ? local.skills : {};
282
- const skillsEnabled = resolveLayeredValue(repositorySkills, localSkills, "enabled", true);
307
+ const contextVirtualization = resolveLayeredValue(repository, local, "contextVirtualization", false);
308
+ const conversationHistory = resolveLayeredValue(repository, local, "conversationHistory", false);
283
309
  return {
284
310
  config: {
285
311
  enabled: enabled.value,
286
- interactionContract: interactionContract.value,
287
- skills: { enabled: skillsEnabled.value },
288
- defaultMode: defaultMode.value,
312
+ contextVirtualization: contextVirtualization.value,
313
+ conversationHistory: conversationHistory.value,
289
314
  },
290
315
  sources: {
291
316
  enabled: enabled.source,
292
- interactionContract: interactionContract.source,
293
- skillsEnabled: skillsEnabled.source,
294
- defaultMode: defaultMode.source,
317
+ contextVirtualization: contextVirtualization.source,
318
+ conversationHistory: conversationHistory.source,
295
319
  },
296
320
  };
297
321
  }
@@ -309,21 +333,20 @@ function resolveSessionCoreConfig(layers) {
309
333
  const configured = layers.coreConfig;
310
334
  const sources = layers.sources;
311
335
  const enabled = currentSessionOverrides.enabled;
312
- const interactionContract = currentSessionOverrides.interactionContract;
313
- const skillsEnabled = currentSessionOverrides.skillsEnabled;
336
+ const contextVirtualization = currentSessionOverrides.contextVirtualization;
337
+ const conversationHistory = currentSessionOverrides.conversationHistory;
314
338
  return {
315
339
  config: {
316
340
  enabled: typeof enabled === "boolean" ? enabled : configured.enabled,
317
- interactionContract:
318
- typeof interactionContract === "boolean" ? interactionContract : configured.interactionContract,
319
- skills: { enabled: typeof skillsEnabled === "boolean" ? skillsEnabled : configured.skills.enabled },
320
- defaultMode: configured.defaultMode,
341
+ contextVirtualization:
342
+ typeof contextVirtualization === "boolean" ? contextVirtualization : configured.contextVirtualization,
343
+ conversationHistory:
344
+ typeof conversationHistory === "boolean" ? conversationHistory : configured.conversationHistory,
321
345
  },
322
346
  sources: {
323
347
  enabled: typeof enabled === "boolean" ? "session" : sources.enabled,
324
- interactionContract: typeof interactionContract === "boolean" ? "session" : sources.interactionContract,
325
- skillsEnabled: typeof skillsEnabled === "boolean" ? "session" : sources.skillsEnabled,
326
- defaultMode: sources.defaultMode,
348
+ contextVirtualization: typeof contextVirtualization === "boolean" ? "session" : sources.contextVirtualization,
349
+ conversationHistory: typeof conversationHistory === "boolean" ? "session" : sources.conversationHistory,
327
350
  },
328
351
  };
329
352
  }
@@ -351,15 +374,32 @@ export async function readFreeflowConfigLayers(cwd) {
351
374
  parseError: blockingState?.parseError ?? null,
352
375
  };
353
376
  }
354
- export async function readCapabilityState(cwd) {
377
+ export async function readCapabilityState(cwd, host = undefined, extensionHost = undefined) {
355
378
  const layers = await readFreeflowConfigLayers(cwd);
356
- const parsed = layers.repository.valid ? layers.repository.parsed : {};
357
- const normalized = normalizeFreeflowConfig(parsed);
358
379
  const effectiveCore = resolveSessionCoreConfig(layers);
359
380
  const enabled = layers.configured && effectiveCore.config.enabled;
360
- const interactionContractConfigEnabled = effectiveCore.config.interactionContract;
361
- const skillsConfigEnabled = effectiveCore.config.skills.enabled;
362
- const outputRouterConfigEnabled = normalized.config.outputRouter.enabled;
381
+ const contextVirtualizationConfigEnabled = effectiveCore.config.contextVirtualization;
382
+ const conversationHistoryConfigEnabled = effectiveCore.config.conversationHistory;
383
+ const hostSupportsCognitiveRouting = supportsCognitiveRoutingModelRegistry(host);
384
+ const configuredCognitiveRouting = await resolveCognitiveRoutingState(
385
+ layers.repository.parsed,
386
+ layers.local.parsed,
387
+ hostSupportsCognitiveRouting ? host : undefined,
388
+ );
389
+ const disabledReason = enabled ? undefined : { code: "disabled", message: "Freeflow is disabled" };
390
+ const childCapability = (configuredEnabled) => ({
391
+ enabled: configuredEnabled,
392
+ effective: enabled && configuredEnabled,
393
+ ...(disabledReason ? { blockingReason: disabledReason } : {}),
394
+ });
395
+ const cognitiveRouting = enabled
396
+ ? configuredCognitiveRouting
397
+ : {
398
+ ...configuredCognitiveRouting,
399
+ enabled: false,
400
+ effective: false,
401
+ blockingReason: disabledReason,
402
+ };
363
403
  return {
364
404
  configured: layers.configured,
365
405
  repositoryConfigured: layers.repositoryConfigured,
@@ -375,111 +415,30 @@ export async function readCapabilityState(cwd) {
375
415
  configuredSources: layers.sources,
376
416
  sessionOverrides: { ...currentSessionOverrides },
377
417
  configSources: effectiveCore.sources,
378
- defaultMode: effectiveCore.config.defaultMode,
379
418
  enabled,
380
- interactionContract: {
381
- enabled: interactionContractConfigEnabled,
382
- effective: enabled && interactionContractConfigEnabled,
383
- },
384
- skills: {
385
- enabled: skillsConfigEnabled,
386
- effective: enabled && skillsConfigEnabled,
387
- },
388
- outputRouter: {
389
- enabled: enabled && outputRouterConfigEnabled,
390
- configEnabled: outputRouterConfigEnabled,
391
- },
419
+ contextVirtualization: childCapability(contextVirtualizationConfigEnabled),
420
+ conversationHistory: childCapability(conversationHistoryConfigEnabled),
421
+ hostSupportsCognitiveRouting,
422
+ cognitiveRouting,
392
423
  };
393
424
  }
394
425
  export const readRuntimeState = readCapabilityState;
395
- export async function readOutputRouterConfig(cwd) {
396
- const layers = await readFreeflowConfigLayers(cwd);
397
- const parsed = layers.repository.valid ? layers.repository.parsed : {};
398
- const localParsed = layers.local.valid ? layers.local.parsed : {};
399
- const normalized = normalizeFreeflowConfig(parsed);
400
- const local = normalizeLocalFreeflowConfig(localParsed);
401
- const freeflowEnabled = layers.configured && layers.coreConfig.enabled;
402
- const effectiveConfig = freeflowEnabled
403
- ? normalized.config
404
- : {
405
- ...normalized.config,
406
- outputRouter: { ...normalized.config.outputRouter, enabled: false },
407
- observedRouting: {
408
- ...normalized.config.observedRouting,
409
- enabled: false,
410
- },
411
- scriptTransform: {
412
- ...normalized.config.scriptTransform,
413
- enabled: false,
414
- },
415
- };
416
- const configWarnings = [...normalized.warnings];
417
- if (layers.repository.exists && !layers.repository.valid) {
418
- configWarnings.unshift(
419
- `.freeflow/config.json could not be parsed; Freeflow runtime is inactive. ${layers.repository.parseError ?? ""}`.trim(),
420
- );
421
- }
422
- if (layers.local.exists && !layers.local.valid) {
423
- configWarnings.unshift(
424
- `.freeflow/local.json could not be parsed; Freeflow runtime is inactive. ${layers.local.parseError ?? ""}`.trim(),
425
- );
426
- }
427
- return {
428
- config: effectiveConfig.outputRouter,
429
- freeflowConfig: effectiveConfig,
430
- localConfig: local.config,
431
- warnings: [...configWarnings, ...local.warnings],
432
- };
433
- }
434
- export function notifyRouterConfigWarnings(ctx, routerConfigResult) {
435
- if (!routerConfigResult.warnings.length) {
436
- return;
437
- }
438
- const key = routerConfigResult.warnings.join("\n");
439
- if (key === lastRouterConfigWarningKey) {
440
- return;
441
- }
442
- lastRouterConfigWarningKey = key;
443
- ctx.ui.notify(`Freeflow config warning: ${routerConfigResult.warnings.join(" ")}`, "warning");
444
- }
445
- export function restoreModeOverride(ctx) {
446
- currentModeOverride = null;
426
+ export function restoreSessionOverrides(ctx) {
447
427
  currentSessionOverrides = {};
448
428
  const entries = ctx.sessionManager?.getBranch?.() ?? ctx.sessionManager?.getEntries?.() ?? [];
449
429
  for (const entry of entries) {
450
- if (entry.type !== "custom") continue;
451
- if (entry.customType === MODE_STATE_ENTRY) {
452
- const mode = entry.data?.currentMode;
453
- currentModeOverride = VALID_MODES.has(mode) ? mode : null;
454
- } else if (entry.customType === SESSION_OVERRIDES_ENTRY) {
430
+ if (entry.type === "custom" && entry.customType === SESSION_OVERRIDES_ENTRY) {
455
431
  currentSessionOverrides = normalizeSessionOverrides(entry.data?.overrides);
456
432
  }
457
433
  }
458
434
  }
459
- export async function readModeState(cwd) {
460
- const layers = await readFreeflowConfigLayers(cwd);
461
- const effectiveCore = resolveSessionCoreConfig(layers);
462
- const repositoryDefaultMode = VALID_MODES.has(layers.repository.parsed.defaultMode)
463
- ? layers.repository.parsed.defaultMode
464
- : "workflow";
465
- const personalDefaultMode = VALID_MODES.has(layers.local.parsed.defaultMode) ? layers.local.parsed.defaultMode : null;
466
- const sessionMode = VALID_MODES.has(currentModeOverride) ? currentModeOverride : null;
467
- const resolvedMode = sessionMode ?? effectiveCore.config.defaultMode;
468
- const active = layers.configured && effectiveCore.config.enabled && effectiveCore.config.skills.enabled;
469
- return {
470
- repositoryDefaultMode,
471
- repositoryDefaultModeSource: VALID_MODES.has(layers.repository.parsed.defaultMode) ? "repository" : "builtin",
472
- personalDefaultMode,
473
- defaultMode: effectiveCore.config.defaultMode,
474
- defaultModeSource: effectiveCore.sources.defaultMode,
475
- currentMode: sessionMode,
476
- sessionMode,
477
- resolvedMode,
478
- active,
479
- effectiveMode: active ? resolvedMode : null,
480
- };
481
- }
482
- export function setModeStatus(ctx, modeState, capabilityState = undefined) {
435
+ export function setFreeflowStatus(
436
+ ctx,
437
+ capabilityState = undefined,
438
+ cognitiveRoutingRuntime = undefined,
439
+ freeflowContext = undefined,
440
+ options = {},
441
+ ) {
483
442
  if (capabilityState && !capabilityState.configured) {
484
443
  ctx.ui.setStatus("freeflow", capabilityState.configExists ? "freeflow: config error" : "freeflow: setup needed");
485
444
  return;
@@ -489,188 +448,200 @@ export function setModeStatus(ctx, modeState, capabilityState = undefined) {
489
448
  ctx.ui.setStatus("freeflow", `freeflow: off${source}`);
490
449
  return;
491
450
  }
492
- const active = [];
493
- if (capabilityState?.interactionContract.effective) {
494
- active.push("interaction");
495
- } else if (capabilityState?.configSources?.interactionContract === "session") {
496
- active.push("interaction off (session)");
497
- }
498
- if (capabilityState?.skills.effective) {
499
- active.push(`${modeState.effectiveMode}${modeState.currentMode ? " (session)" : ""}`);
500
- } else if (capabilityState?.configSources?.skillsEnabled === "session") {
501
- active.push("skills off (session)");
451
+ if (capabilityState?.enabled === true && !hasUsableMandatoryPrompts(freeflowContext)) {
452
+ ctx.ui.setStatus("freeflow", "freeflow: unavailable");
453
+ return;
502
454
  }
503
- if (capabilityState?.outputRouter.enabled) {
504
- active.push("router");
455
+ const active = [];
456
+ const cognitiveRouting = capabilityState?.cognitiveRouting;
457
+ const cognitiveRoutingInactive =
458
+ cognitiveRouting?.enabled === true && cognitiveRoutingRuntime?.runtimeStatus === "inactive";
459
+ const cognitiveRoutingBlocked =
460
+ cognitiveRouting?.enabled === true &&
461
+ (cognitiveRouting?.blockingReason?.code === "runtime_blocked" ||
462
+ cognitiveRoutingRuntime?.runtimeStatus === "blocked");
463
+ const cognitiveRoutingActive = cognitiveRouting?.effective === true && cognitiveRoutingRuntime?.effective === true;
464
+ const startupSelectionSuppressesCognitiveRouting = options.startupSelectionSuppressed === true;
465
+ if (cognitiveRoutingInactive) {
466
+ active.push("cognitive inactive");
467
+ } else if (cognitiveRoutingBlocked) {
468
+ active.push(`cognitive blocked · ${cognitiveRoutingRuntime?.runtimeReason ?? "runtime_blocked"}`);
469
+ } else if (cognitiveRoutingActive) {
470
+ const profile = cognitiveRoutingRuntime.activeProfile;
471
+ const control =
472
+ cognitiveRoutingRuntime.controlMode === "manual-standard" ||
473
+ cognitiveRoutingRuntime.controlMode === "manual-reasoning"
474
+ ? "manual hold"
475
+ : "automatic";
476
+ active.push(`${profile} · ${control}`);
477
+ } else if (cognitiveRouting?.enabled === true) {
478
+ if (cognitiveRouting.blockingReason?.code === "runtime_disabled") {
479
+ active.push("cognitive blocked · runtime_disabled");
480
+ } else if (
481
+ cognitiveRouting.effective === true &&
482
+ cognitiveRoutingRuntime === undefined &&
483
+ options.cognitiveRoutingStartupPending === true &&
484
+ !startupSelectionSuppressesCognitiveRouting
485
+ ) {
486
+ const startupProfile =
487
+ cognitiveRouting.sessionStart?.control === "manual"
488
+ ? (cognitiveRouting.sessionStart.profile ?? "reasoning")
489
+ : "reasoning";
490
+ active.push(`${startupProfile} · pending`);
491
+ } else {
492
+ const reason =
493
+ cognitiveRouting.effective === true
494
+ ? (cognitiveRoutingRuntime?.blockingReason?.code ?? "runtime_inactive")
495
+ : (cognitiveRouting.blockingReason?.code ?? "unavailable");
496
+ active.push(`cognitive blocked · ${reason}`);
497
+ }
505
498
  }
506
- ctx.ui.setStatus("freeflow", `freeflow: ${active.length > 0 ? active.join(" · ") : "idle"}`);
507
- }
508
- function modeSourceLabel(source) {
509
- if (source === "local") return "personal override";
510
- if (source === "repository") return "repository default";
511
- return "built-in default";
512
- }
513
- function describeModeState(modeState) {
514
- const configuredDefault = `configured default ${modeState.defaultMode} (${modeSourceLabel(modeState.defaultModeSource)})`;
515
- if (modeState.sessionMode) {
516
- return `session ${modeState.sessionMode}; ${configuredDefault}`;
499
+ if (capabilityState?.contextVirtualization?.effective || capabilityState?.conversationHistory?.effective) {
500
+ active.push("context");
517
501
  }
518
- return configuredDefault;
502
+ ctx.ui.setStatus("freeflow", `freeflow: ${active.length > 0 ? active.join(" · ") : "active"}`);
519
503
  }
520
504
  export function skillPrompt(skill, args) {
521
505
  const trimmed = args?.trim();
522
506
  return trimmed ? `/skill:${skill}\n\n${trimmed}` : `/skill:${skill}`;
523
507
  }
524
- function outputRouterModeGuidance(mode, skillsEnabled = true) {
525
- if (!skillsEnabled) {
526
- return "Freeflow Skills are disabled: no Freeflow workflow mode is active; apply only output-router evidence guidance.";
527
- }
528
- if (mode === "conversation") {
529
- return "conversation mode: keep routed-tool guidance soft; answer questions directly.";
530
- }
531
- if (mode === "strict-workflow") {
532
- return "strict-workflow mode: strongest guidance; prefer exact, recoverable routed evidence for risky work.";
533
- }
534
- return "workflow mode: prefer routed tools for exploration and likely-large command output.";
535
- }
536
- function outputRouterContext(modeState, freeflowContext, routerConfigResult, capabilityState) {
537
- const safetyNetText =
538
- routerConfigResult.config.postToolRouting === "off"
539
- ? ""
540
- : "\n\nOutput-router config note: large native read/bash outputs may be vaulted and replaced with labeled routed output. Use freeflow_search with the output id to recover exact content.";
541
- return `## Loaded Output Router Skill
542
-
543
- Mode guidance: ${outputRouterModeGuidance(modeState.effectiveMode, capabilityState.skills.effective)}${safetyNetText}
544
-
545
- \`\`\`md
546
- ${freeflowContext.outputRouterSkill.trim()}
547
- \`\`\``;
548
- }
549
- function capabilityContext(capabilityState) {
550
- const sessionSuffix = (source) => (source === "session" ? " (session override)" : "");
551
- const interactionContract = `${capabilityState.interactionContract.effective ? "enabled" : "disabled"}${sessionSuffix(capabilityState.configSources.interactionContract)}`;
552
- const skills = `${capabilityState.skills.effective ? "enabled" : "disabled"}${sessionSuffix(capabilityState.configSources.skillsEnabled)}`;
553
- const outputRouter = capabilityState.outputRouter.enabled ? "enabled" : "disabled";
554
- return `## Freeflow Capabilities
555
-
556
- - Interaction contract: ${interactionContract}. Configure with \`/freeflow settings\` or temporarily with \`/freeflow settings session\`; inspect with \`/freeflow status\`.
557
- - Skills: ${skills}. Configure with \`/freeflow settings\` or temporarily with \`/freeflow settings session\`; inspect with \`/freeflow status\`.
558
- - Output router: ${outputRouter}. Configure with \`/freeflow settings\` or \`/output-router\`; inspect with \`/output-router status\`.
559
-
560
- Disabled capabilities are named only for status/config awareness. Capability-specific instructions and tools are active only while that capability is enabled.`;
561
- }
562
- function hasModelFacingCapability(capabilityState) {
563
- return (
564
- capabilityState.interactionContract.effective ||
565
- capabilityState.skills.effective ||
566
- capabilityState.outputRouter.enabled
508
+ function publicCognitiveRoutingControl(controlMode) {
509
+ if (controlMode === "automatic") return "automatic";
510
+ if (controlMode === "manual-standard" || controlMode === "manual-reasoning") return "manual";
511
+ return "unavailable";
512
+ }
513
+ function publicCognitiveRoutingProfile(activeProfile, effective) {
514
+ if (effective !== true) return "unavailable";
515
+ return activeProfile === "standard" || activeProfile === "reasoning" ? activeProfile : "unavailable";
516
+ }
517
+ function publicCapabilityStatus(capability) {
518
+ if (capability?.effective === true) return "active";
519
+ if (!capability) return "unavailable";
520
+ const blockingCode = capability.blockingReason?.code;
521
+ if (blockingCode && blockingCode !== "disabled") return "unavailable";
522
+ return "inactive";
523
+ }
524
+ function publicCognitiveRoutingStatus(capability, runtime) {
525
+ if (runtime?.runtimeStatus === "inactive") return "inactive";
526
+ return publicCapabilityStatus(capability);
527
+ }
528
+ export function freeflowRuntimeStateMessage(
529
+ capabilityState,
530
+ cognitiveRoutingRuntime = undefined,
531
+ freeflowContext = undefined,
532
+ ) {
533
+ const cognitiveRoutingEffective = capabilityState?.cognitiveRouting?.effective === true;
534
+ const profile = publicCognitiveRoutingProfile(
535
+ cognitiveRoutingRuntime?.activeProfile,
536
+ cognitiveRoutingEffective && cognitiveRoutingRuntime?.effective === true,
567
537
  );
538
+ const control =
539
+ profile === "unavailable" ? "unavailable" : publicCognitiveRoutingControl(cognitiveRoutingRuntime?.controlMode);
540
+ const mandatoryPromptAvailable = capabilityState?.enabled !== true || hasUsableMandatoryPrompts(freeflowContext);
541
+ const freeflowStatus = capabilityState?.configured
542
+ ? capabilityState.enabled
543
+ ? mandatoryPromptAvailable
544
+ ? "active"
545
+ : "unavailable"
546
+ : "inactive"
547
+ : capabilityState?.configExists
548
+ ? "config error"
549
+ : "setup needed";
550
+ return {
551
+ role: "custom",
552
+ customType: FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE,
553
+ content: [
554
+ "# Freeflow Runtime State",
555
+ "",
556
+ "This is extension-generated runtime state. Use it to interpret the stable Freeflow guidance.",
557
+ "",
558
+ `Freeflow: ${freeflowStatus}`,
559
+ "",
560
+ "Capabilities:",
561
+ `- Context Virtualization: ${publicCapabilityStatus(capabilityState?.contextVirtualization)}`,
562
+ `- Conversation History: ${publicCapabilityStatus(capabilityState?.conversationHistory)}`,
563
+ `- Cognitive Routing: ${publicCognitiveRoutingStatus(capabilityState?.cognitiveRouting, cognitiveRoutingRuntime)}`,
564
+ "",
565
+ "Cognitive Routing:",
566
+ `- Control: \`${control}\``,
567
+ `- Profile: \`${profile}\``,
568
+ ].join("\n"),
569
+ display: false,
570
+ details: { source: "provider-request-runtime-state" },
571
+ };
568
572
  }
569
- function modeOverlayContext(mode) {
570
- if (mode === "conversation") {
571
- return `
572
-
573
- ## Conversation Mode Boundary
574
-
575
- Conversation mode is active and read-only.
576
-
577
- Do not call write, edit, or mutating tools. Do not create, delete, commit, push, or change repository, system, external, or durable state.
578
-
579
- A mutation request does not switch mode, and an execution skill does not override this boundary. Explain that conversation mode is read-only and ask the user to switch to workflow or strict-workflow.`;
580
- }
581
- if (mode === "strict-workflow") {
582
- return `
583
-
584
- ## Strict Workflow Overlay
585
-
586
- Strict Workflow is active. Use the adaptive Workflow, but increase decision, evidence, verification, and checkpoint pressure at high-risk or hard-to-reverse boundaries.
587
-
588
- For work affecting security, privacy, billing, data loss, migrations, public interfaces, compatibility, deployment, or architecture:
589
-
590
- - stop for any user-owned choice or source conflict;
591
- - inspect the relevant risk surface before crossing the boundary;
592
- - select only artifacts, checkpoints, and independent review that materially reduce risk;
593
- - verify at the affected boundary before claiming success.
594
-
595
- Do not manufacture ceremony for low-risk, reversible work. Strict Workflow does not authorize mutation, bypass safety, or make every implementation detail a user decision.`;
596
- }
597
- return "";
598
- }
599
- function activeModeContext(modeState) {
600
- return `## Freeflow Mode State
601
-
602
- Repository default mode: \`${modeState.repositoryDefaultMode}\` (${modeState.repositoryDefaultModeSource}).
603
- Personal default override: \`${modeState.personalDefaultMode ?? "none"}\`.
604
- Configured default mode: \`${modeState.defaultMode}\` (${modeSourceLabel(modeState.defaultModeSource)}).
605
- Session mode override: \`${modeState.sessionMode ?? "none"}\`.
606
- Resolved mode: \`${modeState.resolvedMode}\`.
607
- Effective Freeflow mode: \`${modeState.effectiveMode}\`.
608
-
609
- Mode behavior:
610
- - \`conversation\`: answer, discuss, critique, and inspect read-only; require a mode change before mutating state.
611
- - \`workflow\`: use the adaptive workflow for consequential or mutating work.
612
- - \`strict-workflow\`: use the same workflow with stronger decision, evidence, and checkpoint pressure at high-risk or hard-to-reverse boundaries.
613
-
614
- Task type, risk classification, and direct skill calls do not change mode. Recommend another mode when useful; the user decides.
615
- Do not announce mode on every reply. Mention it when the user asks, configuration is discussed, or it changes the next action.${modeOverlayContext(modeState.effectiveMode)}`;
616
- }
617
- function inactiveModeContext(modeState) {
618
- return `## Freeflow Mode State
619
-
620
- Repository default mode: \`${modeState.repositoryDefaultMode}\` (${modeState.repositoryDefaultModeSource}).
621
- Personal default override: \`${modeState.personalDefaultMode ?? "none"}\`.
622
- Configured default mode: \`${modeState.defaultMode}\` (${modeSourceLabel(modeState.defaultModeSource)}).
623
- Session mode override: \`${modeState.sessionMode ?? "none"}\`.
624
- Resolved mode: \`${modeState.resolvedMode}\` (inactive because Skills are disabled).
625
- Effective Freeflow mode: \`none\`.
626
-
627
- Freeflow workflow modes are dormant until Skills are enabled with \`/freeflow settings\`.`;
628
- }
629
- function controlPlaneContext(modeState, capabilityState) {
630
- return `# Freeflow Control Plane
631
-
632
- Freeflow is enabled for this repo, but no model-facing capabilities are enabled.
633
- These lines are status/config awareness only; do not apply Freeflow workflow or output-router behavior.
634
-
635
- ${inactiveModeContext(modeState)}
636
-
637
- ${capabilityContext(capabilityState)}
638
-
639
- Use \`/freeflow settings\` to enable the Interaction Contract, Skills, or Output Router. The read-only \`freeflow_status\` diagnostic may be available so the model can answer setup/status questions.`;
640
- }
641
- export function runtimeContext(modeState, freeflowContext, routerConfigResult, capabilityState) {
642
- if (!capabilityState.configured) {
573
+ export function withoutFreeflowRuntimeState(messages) {
574
+ return (Array.isArray(messages) ? messages : []).filter(
575
+ (message) =>
576
+ message?.customType !== FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE &&
577
+ message?.customType !== COGNITIVE_ROUTING_RUNTIME_STATE_MESSAGE_TYPE,
578
+ );
579
+ }
580
+ function lastUserMessageIndex(messages) {
581
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
582
+ if (messages[index]?.role === "user") return index;
583
+ }
584
+ return -1;
585
+ }
586
+ function insertRuntimeStateBeforeLatestUser(messages, runtimeState) {
587
+ const latestUserIndex = lastUserMessageIndex(messages);
588
+ if (latestUserIndex < 0) return [...messages, runtimeState];
589
+ return [...messages.slice(0, latestUserIndex), runtimeState, ...messages.slice(latestUserIndex)];
590
+ }
591
+ export function withFreeflowRuntimeState(
592
+ messages,
593
+ capabilityState,
594
+ cognitiveRoutingRuntime = undefined,
595
+ freeflowContext = undefined,
596
+ options = {},
597
+ ) {
598
+ const source = Array.isArray(messages) ? messages : [];
599
+ const runtimeState = freeflowRuntimeStateMessage(capabilityState, cognitiveRoutingRuntime, freeflowContext);
600
+ const runtimeStateMessages = source.filter(
601
+ (message) =>
602
+ message?.customType === FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE ||
603
+ message?.customType === COGNITIVE_ROUTING_RUNTIME_STATE_MESSAGE_TYPE,
604
+ );
605
+ const withoutRuntimeState = withoutFreeflowRuntimeState(source);
606
+ const expectedRuntimeStateIndex = lastUserMessageIndex(withoutRuntimeState);
607
+ const runtimeStateIndex = source.findIndex((message) => message?.customType === FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE);
608
+ const unchanged =
609
+ options.force !== true &&
610
+ runtimeStateMessages.length === 1 &&
611
+ runtimeStateMessages[0]?.customType === FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE &&
612
+ runtimeStateMessages[0]?.content === runtimeState.content &&
613
+ runtimeStateIndex === (expectedRuntimeStateIndex < 0 ? withoutRuntimeState.length : expectedRuntimeStateIndex);
614
+ if (unchanged) return source;
615
+ return insertRuntimeStateBeforeLatestUser(withoutRuntimeState, runtimeState);
616
+ }
617
+ export function runtimeContext(freeflowContext, capabilityState) {
618
+ if (
619
+ capabilityState?.configured !== true ||
620
+ capabilityState?.enabled !== true ||
621
+ !hasUsableMandatoryPrompts(freeflowContext)
622
+ ) {
643
623
  return "";
644
624
  }
645
- if (!capabilityState.enabled) {
646
- return `# Freeflow Disabled
647
-
648
- Freeflow is disabled by \`.freeflow/config.json\` for this repo. Do not apply the Freeflow Interaction Contract, workflow, or output-router behavior unless the user re-enables Freeflow with \`/freeflow enable\` or \`/freeflow settings\`.
649
-
650
- These instructions are context-loading only. They do not override user instructions, repo instructions, or host safety and approval policy.`;
651
- }
652
- if (!hasModelFacingCapability(capabilityState)) {
653
- return controlPlaneContext(modeState, capabilityState);
654
- }
655
- const modeText = capabilityState.skills.effective ? activeModeContext(modeState) : inactiveModeContext(modeState);
656
- const interactionContractText = capabilityState.interactionContract.effective
657
- ? `\n\n${freeflowContext.interactionContract.trim()}`
658
- : "";
659
- const routerText =
660
- capabilityState.outputRouter.enabled && routerConfigResult.config.enabled
661
- ? `\n\n${outputRouterContext(modeState, freeflowContext, routerConfigResult, capabilityState)}`
662
- : "";
663
- return `# Freeflow Runtime Context
664
-
665
- Freeflow Pi extension loaded this before the agent turn.
666
- Runtime delivery: confirmed for this Pi \`before_agent_start\` invocation.
667
- These instructions are context-loading only. They do not override user instructions, repo instructions, or host safety and approval policy.
668
-
669
- ${modeText}
670
-
671
- ${capabilityContext(capabilityState)}${interactionContractText}${routerText}
672
-
673
- This Pi extension loads enabled runtime context before every agent turn and routes commands only; it does not enforce policy, block tools, grant permissions, or create repo-local hooks.`;
625
+ const blocks = [freeflowContext.corePrompt.trim(), freeflowContext.interactionContractPrompt.trim()];
626
+ if (
627
+ capabilityState.cognitiveRouting?.effective === true &&
628
+ isPromptAvailable(freeflowContext.cognitiveRoutingPrompt)
629
+ ) {
630
+ blocks.push(freeflowContext.cognitiveRoutingPrompt.trim());
631
+ }
632
+ if (
633
+ capabilityState.contextVirtualization?.effective === true &&
634
+ isPromptAvailable(freeflowContext.contextVirtualizationPrompt)
635
+ ) {
636
+ blocks.push(freeflowContext.contextVirtualizationPrompt.trim());
637
+ }
638
+ if (
639
+ capabilityState.conversationHistory?.effective === true &&
640
+ isPromptAvailable(freeflowContext.conversationHistoryPrompt)
641
+ ) {
642
+ blocks.push(freeflowContext.conversationHistoryPrompt.trim());
643
+ }
644
+ return blocks.filter(Boolean).join("\n\n");
674
645
  }
675
646
  export async function setSessionCoreOverride(key, value, ctx, pi) {
676
647
  if (!SESSION_CORE_KEYS.has(key)) {
@@ -693,106 +664,22 @@ export async function setSessionCoreOverride(key, value, ctx, pi) {
693
664
  pi?.appendEntry?.(SESSION_OVERRIDES_ENTRY, { overrides: { ...currentSessionOverrides } });
694
665
  return {
695
666
  changed: true,
696
- reloadRequired: key === "enabled" || key === "skillsEnabled",
667
+ reloadRequired: key === "enabled",
697
668
  sessionOverrides: { ...currentSessionOverrides },
698
- capabilityState: await readCapabilityState(ctx.cwd),
669
+ capabilityState: await readCapabilityState(ctx.cwd, ctx, pi?.host),
699
670
  };
700
671
  }
701
672
  export async function resetSessionOverrides(ctx, pi) {
702
673
  const hadCoreOverrides = Object.keys(currentSessionOverrides).length > 0;
703
- const reloadRequired =
704
- Object.hasOwn(currentSessionOverrides, "enabled") || Object.hasOwn(currentSessionOverrides, "skillsEnabled");
705
- const hadModeOverride = VALID_MODES.has(currentModeOverride);
674
+ const reloadRequired = Object.hasOwn(currentSessionOverrides, "enabled");
706
675
  if (hadCoreOverrides) {
707
676
  currentSessionOverrides = {};
708
677
  pi?.appendEntry?.(SESSION_OVERRIDES_ENTRY, { overrides: {} });
709
678
  }
710
- if (hadModeOverride) {
711
- await setSessionMode(null, ctx, pi);
712
- }
713
679
  return {
714
- changed: hadCoreOverrides || hadModeOverride,
680
+ changed: hadCoreOverrides,
715
681
  reloadRequired,
716
682
  sessionOverrides: { ...currentSessionOverrides },
717
- modeState: await readModeState(ctx.cwd),
683
+ capabilityState: await readCapabilityState(ctx.cwd, ctx, pi?.host),
718
684
  };
719
685
  }
720
- export async function setSessionMode(mode, ctx, pi) {
721
- const nextMode = mode === "default" || mode === "reset" || mode === null ? null : mode;
722
- if (nextMode !== null && !VALID_MODES.has(nextMode)) {
723
- throw new Error(`Invalid Freeflow mode: ${String(mode)}`);
724
- }
725
- currentModeOverride = nextMode;
726
- pi?.appendEntry?.(MODE_STATE_ENTRY, { currentMode: nextMode });
727
- return readModeState(ctx.cwd);
728
- }
729
- export async function handleModeCommand(args, ctx, pi) {
730
- const arg = args?.trim();
731
- const capabilityState = await readCapabilityState(ctx.cwd);
732
- const inactiveModeState = await readModeState(ctx.cwd);
733
- if (!capabilityState.configured) {
734
- setModeStatus(ctx, inactiveModeState, capabilityState);
735
- ctx.ui.notify(
736
- "Freeflow is installed but this repo is not set up. Run /setup-freeflow before changing mode.",
737
- "warning",
738
- );
739
- return { changed: false, error: "not_configured" };
740
- }
741
- if (!capabilityState.enabled) {
742
- setModeStatus(ctx, inactiveModeState, capabilityState);
743
- ctx.ui.notify(
744
- "Freeflow is disabled for this repo. Use /freeflow enable or /freeflow settings to re-enable it.",
745
- "warning",
746
- );
747
- return { changed: false, error: "freeflow_disabled" };
748
- }
749
- if (!capabilityState.skills.effective) {
750
- setModeStatus(ctx, inactiveModeState, capabilityState);
751
- ctx.ui.notify(
752
- `Freeflow modes are inactive because Skills are disabled. Configured default mode: ${inactiveModeState.defaultMode} (${modeSourceLabel(inactiveModeState.defaultModeSource)}). Enable Skills in /freeflow settings before changing mode.`,
753
- "warning",
754
- );
755
- return { changed: false, error: "skills_disabled" };
756
- }
757
- if (VALID_MODES.has(arg)) {
758
- if (inactiveModeState.effectiveMode === arg) {
759
- setModeStatus(ctx, inactiveModeState, capabilityState);
760
- const message = inactiveModeState.sessionMode
761
- ? `Freeflow is already in ${arg} mode for this Pi session. No session entry was added. Configured default remains ${inactiveModeState.defaultMode} (${modeSourceLabel(inactiveModeState.defaultModeSource)}).`
762
- : `Freeflow is already in ${arg} mode from the configured default (${modeSourceLabel(inactiveModeState.defaultModeSource)}). No session override was created.`;
763
- ctx.ui.notify(message, "info");
764
- return { changed: false, modeState: inactiveModeState };
765
- }
766
- const modeState = await setSessionMode(arg, ctx, pi);
767
- setModeStatus(ctx, modeState, capabilityState);
768
- ctx.ui.notify(
769
- `Freeflow mode is now ${modeState.effectiveMode} for this Pi session. Stored in Pi session history; .freeflow/local.json and .freeflow/config.json were not changed. Configured default remains ${modeState.defaultMode} (${modeSourceLabel(modeState.defaultModeSource)}).`,
770
- "info",
771
- );
772
- return { changed: true, modeState };
773
- }
774
- if (RESET_MODE_ARGS.has(arg) || arg === "default") {
775
- if (!inactiveModeState.sessionMode) {
776
- setModeStatus(ctx, inactiveModeState, capabilityState);
777
- ctx.ui.notify(
778
- `Freeflow is already using the configured default: ${inactiveModeState.defaultMode} (${modeSourceLabel(inactiveModeState.defaultModeSource)}). No session override is active.`,
779
- "info",
780
- );
781
- return { changed: false, modeState: inactiveModeState };
782
- }
783
- const modeState = await setSessionMode(null, ctx, pi);
784
- setModeStatus(ctx, modeState, capabilityState);
785
- ctx.ui.notify(
786
- `Freeflow mode reset to configured default: ${modeState.defaultMode} (${modeSourceLabel(modeState.defaultModeSource)}). Session override cleared; .freeflow/local.json and .freeflow/config.json were not changed.`,
787
- "info",
788
- );
789
- return { changed: true, modeState };
790
- }
791
- const modeState = await readModeState(ctx.cwd);
792
- setModeStatus(ctx, modeState, capabilityState);
793
- ctx.ui.notify(
794
- `Freeflow mode is ${modeState.effectiveMode} (${describeModeState(modeState)}). Use /freeflow mode conversation, /freeflow mode workflow, /freeflow mode strict-workflow, or /freeflow mode reset.`,
795
- "info",
796
- );
797
- return { changed: false, modeState, error: arg ? "invalid_mode" : undefined };
798
- }