@hassangameryt/freeflow 0.4.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 (285) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.codex-plugin/plugin.json +2 -1
  4. package/README.md +150 -155
  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 +17 -13
  9. package/hooks/freeflow-runtime-context.mjs +393 -159
  10. package/hooks/hooks.json +26 -1
  11. package/hooks/tests/check-runtime-context-hook.sh +223 -54
  12. package/package.json +25 -16
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +236 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +862 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +439 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +126 -0
  18. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  19. package/pi-extension/dist/cognitive-routing/tool.js +211 -0
  20. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  21. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  22. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  23. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  24. package/pi-extension/dist/context-virtualization/state.js +57 -0
  25. package/pi-extension/dist/context-virtualization/types.js +10 -0
  26. package/pi-extension/dist/conversation-history/passages.js +66 -0
  27. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  28. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  29. package/pi-extension/dist/conversation-history/search.js +56 -0
  30. package/pi-extension/dist/conversation-history/types.js +1 -0
  31. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  32. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  33. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  34. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  35. package/pi-extension/dist/freeflow-context/types.js +12 -0
  36. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  37. package/pi-extension/dist/index.js +516 -98
  38. package/pi-extension/dist/runtime/runtime-context.js +318 -271
  39. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  40. package/pi-extension/dist/settings/settings-tui.js +78 -0
  41. package/pi-extension/dist/settings/settings-ui.js +593 -563
  42. package/pi-extension/dist/ui/text-component.js +57 -0
  43. package/pi-extension/src/cognitive-routing/commands.ts +119 -0
  44. package/pi-extension/src/cognitive-routing/config.ts +307 -0
  45. package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
  46. package/pi-extension/src/cognitive-routing/history.ts +638 -0
  47. package/pi-extension/src/cognitive-routing/host.ts +172 -0
  48. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  49. package/pi-extension/src/cognitive-routing/tool.ts +272 -0
  50. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  51. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  52. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  53. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  54. package/pi-extension/src/context-virtualization/state.ts +79 -0
  55. package/pi-extension/src/context-virtualization/types.ts +92 -0
  56. package/pi-extension/src/conversation-history/passages.ts +81 -0
  57. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  58. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  59. package/pi-extension/src/conversation-history/search.ts +79 -0
  60. package/pi-extension/src/conversation-history/types.ts +31 -0
  61. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  62. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  63. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  64. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  65. package/pi-extension/src/freeflow-context/types.ts +70 -0
  66. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  67. package/pi-extension/src/index.ts +553 -101
  68. package/pi-extension/src/runtime/runtime-context.ts +339 -281
  69. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  70. package/pi-extension/src/settings/settings-tui.ts +93 -0
  71. package/pi-extension/src/settings/settings-ui.ts +700 -613
  72. package/pi-extension/src/ui/text-component.ts +66 -0
  73. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  74. package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
  75. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
  76. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  77. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  78. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  79. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
  80. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  81. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  82. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  83. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  84. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  85. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  86. package/pi-extension/tests/integration/pi-extension.test.js +928 -2047
  87. package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
  88. package/runtime/prompts/cognitive-routing.md +9 -0
  89. package/runtime/prompts/context-virtualization.md +3 -0
  90. package/runtime/prompts/conversation-history.md +3 -0
  91. package/runtime/prompts/core.md +13 -0
  92. package/runtime/prompts/interaction-contract.md +7 -0
  93. package/runtime/prompts/skills.md +65 -0
  94. package/skills/action-selection/SKILL.md +91 -0
  95. package/skills/bypass/SKILL.md +3 -2
  96. package/skills/commit-work/SKILL.md +1 -1
  97. package/skills/commit-work/references/staging-decisions.md +1 -1
  98. package/skills/decision-gate/SKILL.md +1 -1
  99. package/skills/design-for-depth/SKILL.md +14 -33
  100. package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
  101. package/skills/design-for-depth/references/interface-design-loop.md +1 -1
  102. package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
  103. package/skills/diagnose-failure/SKILL.md +20 -14
  104. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
  105. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
  106. package/skills/discuss/SKILL.md +34 -22
  107. package/skills/discuss/references/discussion-continuity.md +71 -0
  108. package/skills/evaluate-skill/SKILL.md +5 -5
  109. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
  110. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  111. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  112. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  113. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  114. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  115. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  116. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  117. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  118. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  119. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  120. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  121. package/skills/execute-work/SKILL.md +18 -15
  122. package/skills/execute-work/references/code-practices.md +1 -1
  123. package/skills/execute-work/references/execute-work-edges.md +42 -0
  124. package/skills/finish-branch/SKILL.md +2 -2
  125. package/skills/finish-branch/references/integration-options.md +1 -1
  126. package/skills/handoff/SKILL.md +3 -3
  127. package/skills/launch-work/SKILL.md +1 -1
  128. package/skills/launch-work/references/launch-readiness.md +2 -2
  129. package/skills/migration-work/SKILL.md +2 -2
  130. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  131. package/skills/mode-contract/SKILL.md +32 -10
  132. package/skills/release-work/SKILL.md +1 -1
  133. package/skills/release-work/references/release-evidence.md +1 -1
  134. package/skills/review-artifact/SKILL.md +28 -26
  135. package/skills/review-work/SKILL.md +28 -26
  136. package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
  137. package/skills/review-work/references/security-risk-lens.md +1 -1
  138. package/skills/setup-freeflow/SKILL.md +6 -10
  139. package/skills/setup-freeflow/references/activation-contract.md +5 -10
  140. package/skills/setup-freeflow/references/host-setup.md +3 -7
  141. package/skills/simplify-code/SKILL.md +1 -1
  142. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  143. package/skills/tdd/SKILL.md +18 -38
  144. package/skills/tdd/references/test-design.md +2 -2
  145. package/skills/track-work/SKILL.md +362 -58
  146. package/skills/track-work/scripts/lib/codec.mjs +570 -0
  147. package/skills/track-work/scripts/lib/commands.mjs +568 -0
  148. package/skills/track-work/scripts/lib/contract.mjs +303 -0
  149. package/skills/track-work/scripts/lib/model.mjs +506 -0
  150. package/skills/track-work/scripts/lib/operations.mjs +965 -0
  151. package/skills/track-work/scripts/lib/record-store.mjs +264 -0
  152. package/skills/track-work/scripts/lib/result.mjs +37 -0
  153. package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
  154. package/skills/track-work/scripts/lib/schema.mjs +277 -0
  155. package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
  156. package/skills/track-work/scripts/lib/views.mjs +443 -0
  157. package/skills/track-work/scripts/lib/workspace.mjs +134 -0
  158. package/skills/track-work/scripts/working-record.mjs +122 -0
  159. package/skills/verify-work/SKILL.md +14 -2
  160. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  161. package/skills/verify-work/references/integration-evidence.md +1 -1
  162. package/skills/verify-work/references/performance-evidence.md +1 -1
  163. package/skills/workflow/SKILL.md +105 -53
  164. package/skills/workflow/references/domain-skill-composition.md +22 -42
  165. package/skills/write-plan/SKILL.md +1 -1
  166. package/skills/write-skill/SKILL.md +3 -2
  167. package/skills/write-skill/references/progressive-disclosure.md +17 -3
  168. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  169. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  170. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  171. package/pi-extension/dist/output-router/renderers.js +0 -1023
  172. package/pi-extension/dist/output-router/router-tools.js +0 -554
  173. package/pi-extension/dist/output-router/schemas.js +0 -383
  174. package/pi-extension/dist/output-router/status.js +0 -744
  175. package/pi-extension/dist/output-router/utils.js +0 -502
  176. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  177. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  178. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  179. package/pi-extension/src/output-router/renderers.ts +0 -1108
  180. package/pi-extension/src/output-router/router-tools.ts +0 -579
  181. package/pi-extension/src/output-router/schemas.ts +0 -387
  182. package/pi-extension/src/output-router/status.ts +0 -791
  183. package/pi-extension/src/output-router/utils.ts +0 -554
  184. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  185. package/pi-extension/tests/output-router/search.test.js +0 -722
  186. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  187. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  188. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  189. package/router/dist/benchmarks/benchmarks.js +0 -979
  190. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  191. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  192. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  193. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  194. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  195. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  196. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  197. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  198. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  199. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  200. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  201. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  202. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  203. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  204. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  205. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  206. package/router/dist/config/config.d.ts +0 -103
  207. package/router/dist/config/config.js +0 -632
  208. package/router/dist/config/router-contract.d.ts +0 -10
  209. package/router/dist/config/router-contract.js +0 -54
  210. package/router/dist/config/schema.d.ts +0 -27
  211. package/router/dist/config/schema.js +0 -648
  212. package/router/dist/config/types.d.ts +0 -573
  213. package/router/dist/config/types.js +0 -41
  214. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  215. package/router/dist/evidence/bounded-evidence.js +0 -323
  216. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  217. package/router/dist/evidence/evidence-range-selector.js +0 -232
  218. package/router/dist/evidence/evidence-search.d.ts +0 -27
  219. package/router/dist/evidence/evidence-search.js +0 -490
  220. package/router/dist/evidence/evidence.d.ts +0 -38
  221. package/router/dist/evidence/evidence.js +0 -200
  222. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  223. package/router/dist/evidence/failure-contracts.js +0 -166
  224. package/router/dist/evidence/line-ranges.d.ts +0 -24
  225. package/router/dist/evidence/line-ranges.js +0 -22
  226. package/router/dist/experiments/local-index.d.ts +0 -64
  227. package/router/dist/experiments/local-index.js +0 -581
  228. package/router/dist/index.d.ts +0 -30
  229. package/router/dist/index.js +0 -32
  230. package/router/dist/local/local-traversal.d.ts +0 -18
  231. package/router/dist/local/local-traversal.js +0 -334
  232. package/router/dist/processing/engine.d.ts +0 -132
  233. package/router/dist/processing/engine.js +0 -533
  234. package/router/dist/processing/reducers.d.ts +0 -280
  235. package/router/dist/processing/reducers.js +0 -1673
  236. package/router/dist/processing/renderers.d.ts +0 -31
  237. package/router/dist/processing/renderers.js +0 -195
  238. package/router/dist/processing/scripts.d.ts +0 -91
  239. package/router/dist/processing/scripts.js +0 -386
  240. package/router/dist/repo/repo-traversal.d.ts +0 -18
  241. package/router/dist/repo/repo-traversal.js +0 -245
  242. package/router/dist/routing/observed-reducers.d.ts +0 -41
  243. package/router/dist/routing/observed-reducers.js +0 -661
  244. package/router/dist/routing/observed-routing.d.ts +0 -26
  245. package/router/dist/routing/observed-routing.js +0 -378
  246. package/router/dist/routing/parsers.d.ts +0 -16
  247. package/router/dist/routing/parsers.js +0 -521
  248. package/router/dist/routing/run-filters.d.ts +0 -60
  249. package/router/dist/routing/run-filters.js +0 -238
  250. package/router/dist/routing/run-reducers.d.ts +0 -49
  251. package/router/dist/routing/run-reducers.js +0 -122
  252. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  253. package/router/dist/sandbox/adapter-roots.js +0 -66
  254. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  255. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  256. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  257. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  258. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  259. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  260. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  261. package/router/dist/sandbox/script-sandbox.js +0 -337
  262. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  263. package/router/dist/setup/script-transform-adapters.js +0 -278
  264. package/router/dist/tools/batch.d.ts +0 -33
  265. package/router/dist/tools/batch.js +0 -586
  266. package/router/dist/tools/run.d.ts +0 -62
  267. package/router/dist/tools/run.js +0 -1304
  268. package/router/dist/tools/search.d.ts +0 -66
  269. package/router/dist/tools/search.js +0 -1364
  270. package/router/dist/transform/engine.d.ts +0 -168
  271. package/router/dist/transform/engine.js +0 -2072
  272. package/router/dist/vault/vault-index.d.ts +0 -125
  273. package/router/dist/vault/vault-index.js +0 -416
  274. package/router/dist/vault/vault.d.ts +0 -138
  275. package/router/dist/vault/vault.js +0 -630
  276. package/runtime/interaction-contract.md +0 -16
  277. package/skills/discuss/references/checkpoints.md +0 -66
  278. package/skills/execute-work/references/execution-loop.md +0 -77
  279. package/skills/output-router/SKILL.md +0 -206
  280. package/skills/output-router/references/safety-policy.md +0 -40
  281. package/skills/setup-freeflow/references/output-router-setup.md +0 -110
  282. package/skills/track-work/references/working-record-schema.md +0 -378
  283. package/skills/workflow/references/workflow-loop.md +0 -92
  284. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
  285. /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
@@ -1,17 +1,17 @@
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";
4
+ import { resolveCognitiveRoutingState } from "../cognitive-routing/runtime.js";
5
+ import { isPiFlowHost } from "./runtime-identity.js";
5
6
  export const VALID_MODES = new Set(["conversation", "workflow", "strict-workflow"]);
6
7
  export const WORKFLOW_COMMANDS = [
7
8
  { command: "discuss", skill: "discuss" },
8
- { command: "discover", skill: "discuss" },
9
+ { command: "action-selection", skill: "action-selection" },
9
10
  { command: "track-work", skill: "track-work" },
10
11
  { command: "write-spec", skill: "write-spec" },
11
12
  { command: "review-artifact", skill: "review-artifact" },
12
13
  { command: "write-plan", skill: "write-plan" },
13
14
  { command: "execute-work", skill: "execute-work" },
14
- { command: "execute-plan", skill: "execute-work" },
15
15
  { command: "simplify-code", skill: "simplify-code" },
16
16
  { command: "migration-work", skill: "migration-work" },
17
17
  { command: "diagnose-failure", skill: "diagnose-failure" },
@@ -26,6 +26,7 @@ export const WORKFLOW_COMMANDS = [
26
26
  ];
27
27
  export const CONTRIBUTOR_COMMANDS = ["setup-freeflow", "write-skill", "evaluate-skill"];
28
28
  export const FREEFLOW_MODEL_SKILL_NAMES = [
29
+ "action-selection",
29
30
  "bypass",
30
31
  "commit-work",
31
32
  "decision-gate",
@@ -52,54 +53,107 @@ export const FREEFLOW_MODEL_SKILL_NAMES = [
52
53
  "write-skill",
53
54
  "write-spec",
54
55
  ];
56
+ export const FREEFLOW_CAPABILITY_SKILL_NAMES = ["cognitive-routing", "context-virtualization", "conversation-history"];
55
57
  export function freeflowSkillPath(skillName) {
56
58
  return fileURLToPath(new URL(`../../../skills/${skillName}/SKILL.md`, import.meta.url));
57
59
  }
58
- export function freeflowModelSkillPaths() {
59
- return FREEFLOW_MODEL_SKILL_NAMES.map((skillName) => freeflowSkillPath(skillName));
60
+ export function freeflowCapabilitySkillPath(skillName) {
61
+ return fileURLToPath(new URL(`../../../capabilities/${skillName}/SKILL.md`, import.meta.url));
62
+ }
63
+ export function freeflowModelSkillPaths(capabilityState = undefined) {
64
+ const paths = FREEFLOW_MODEL_SKILL_NAMES.map((skillName) => freeflowSkillPath(skillName));
65
+ const capabilityStates = {
66
+ "cognitive-routing": capabilityState?.cognitiveRouting,
67
+ "context-virtualization": capabilityState?.contextVirtualization,
68
+ "conversation-history": capabilityState?.conversationHistory,
69
+ };
70
+ for (const skillName of FREEFLOW_CAPABILITY_SKILL_NAMES) {
71
+ if (capabilityStates[skillName]?.effective === true) {
72
+ paths.push(freeflowCapabilitySkillPath(skillName));
73
+ }
74
+ }
75
+ return paths;
60
76
  }
61
77
  const MODE_STATE_ENTRY = "freeflow-mode";
62
78
  const SESSION_OVERRIDES_ENTRY = "freeflow-session-overrides";
63
- const SESSION_CORE_KEYS = new Set(["enabled", "interactionContract", "skillsEnabled"]);
79
+ const SESSION_CORE_KEYS = new Set([
80
+ "enabled",
81
+ "interactionContract",
82
+ "skillsEnabled",
83
+ "contextVirtualization",
84
+ "conversationHistory",
85
+ ]);
64
86
  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"];
87
+ export const COGNITIVE_ROUTING_SWITCH_TOOL_NAME = "freeflow_switch_profile";
88
+ export const FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE = "freeflow-runtime-state";
89
+ export const COGNITIVE_ROUTING_RUNTIME_STATE_MESSAGE_TYPE = "freeflow-cognitive-routing-runtime-state";
67
90
  export const WORKFLOW_BOOTSTRAP_MESSAGE_TYPE = "freeflow-workflow-bootstrap";
91
+ export const COGNITIVE_ROUTING_BOOTSTRAP_MESSAGE_TYPE = "freeflow-cognitive-routing-bootstrap";
92
+ export const FREEFLOW_BOOTSTRAP_MESSAGE_TYPE = "freeflow-bootstrap";
68
93
  let runtimeContextCache = null;
69
94
  let currentModeOverride = null;
70
95
  let currentSessionOverrides = {};
71
- let lastRouterConfigWarningKey = null;
96
+ async function readPromptFile(url) {
97
+ try {
98
+ return await readFile(url, "utf8");
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
72
103
  async function loadRuntimeContext(capabilityState = undefined) {
104
+ const freeflowEnabled = capabilityState?.enabled === true;
73
105
  const interactionContractEnabled = capabilityState?.interactionContract?.effective === true;
74
106
  const skillsEnabled = capabilityState?.skills?.effective === true;
75
- const outputRouterEnabled = capabilityState?.outputRouter?.enabled === true;
76
- const [interactionContract, workflowSkill, outputRouterSkill] = await Promise.all([
107
+ const contextVirtualizationEnabled = capabilityState?.contextVirtualization?.effective === true;
108
+ const conversationHistoryEnabled = capabilityState?.conversationHistory?.effective === true;
109
+ const cognitiveRoutingEnabled = capabilityState?.cognitiveRouting?.effective === true;
110
+ const [
111
+ corePrompt,
112
+ interactionContractPrompt,
113
+ skillsPrompt,
114
+ cognitiveRoutingPrompt,
115
+ contextVirtualizationPrompt,
116
+ conversationHistoryPrompt,
117
+ ] = await Promise.all([
118
+ freeflowEnabled
119
+ ? readPromptFile(new URL("../../../runtime/prompts/core.md", import.meta.url))
120
+ : Promise.resolve(null),
77
121
  interactionContractEnabled
78
- ? readFile(new URL("../../../runtime/interaction-contract.md", import.meta.url), "utf8")
122
+ ? readPromptFile(new URL("../../../runtime/prompts/interaction-contract.md", import.meta.url))
79
123
  : Promise.resolve(null),
80
124
  skillsEnabled
81
- ? readFile(new URL("../../../skills/workflow/SKILL.md", import.meta.url), "utf8")
125
+ ? readPromptFile(new URL("../../../runtime/prompts/skills.md", import.meta.url))
126
+ : Promise.resolve(null),
127
+ cognitiveRoutingEnabled
128
+ ? readPromptFile(new URL("../../../runtime/prompts/cognitive-routing.md", import.meta.url))
82
129
  : Promise.resolve(null),
83
- outputRouterEnabled
84
- ? readFile(new URL("../../../skills/output-router/SKILL.md", import.meta.url), "utf8")
130
+ contextVirtualizationEnabled
131
+ ? readPromptFile(new URL("../../../runtime/prompts/context-virtualization.md", import.meta.url))
132
+ : Promise.resolve(null),
133
+ conversationHistoryEnabled
134
+ ? readPromptFile(new URL("../../../runtime/prompts/conversation-history.md", import.meta.url))
85
135
  : Promise.resolve(null),
86
136
  ]);
87
- return { interactionContract, workflowSkill, outputRouterSkill };
137
+ return {
138
+ corePrompt,
139
+ interactionContractPrompt,
140
+ skillsPrompt,
141
+ cognitiveRoutingPrompt,
142
+ contextVirtualizationPrompt,
143
+ conversationHistoryPrompt,
144
+ };
88
145
  }
89
146
  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;
147
+ if (!runtimeContextCache) return false;
148
+ const expected = {
149
+ corePrompt: capabilityState?.enabled === true,
150
+ interactionContractPrompt: capabilityState?.interactionContract?.effective === true,
151
+ skillsPrompt: capabilityState?.skills?.effective === true,
152
+ cognitiveRoutingPrompt: capabilityState?.cognitiveRouting?.effective === true,
153
+ contextVirtualizationPrompt: capabilityState?.contextVirtualization?.effective === true,
154
+ conversationHistoryPrompt: capabilityState?.conversationHistory?.effective === true,
155
+ };
156
+ return Object.entries(expected).every(([key, required]) => !required || Boolean(runtimeContextCache[key]));
103
157
  }
104
158
  export async function refreshRuntimeContext(capabilityState = undefined) {
105
159
  runtimeContextCache = await loadRuntimeContext(capabilityState);
@@ -111,41 +165,32 @@ export async function getRuntimeContext(capabilityState = undefined) {
111
165
  }
112
166
  return refreshRuntimeContext(capabilityState);
113
167
  }
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.
168
+ const BOOTSTRAP_COMPONENTS = Object.freeze({
169
+ workflow: "workflow",
170
+ cognitiveRouting: "cognitive-routing",
171
+ });
172
+ function bootstrapEntryComponents(entry) {
173
+ const isCustomMessage = entry?.role === "custom" || entry?.type === "custom_message" || entry?.type === "custom";
174
+ if (!isCustomMessage) return [];
175
+ if (entry.customType === FREEFLOW_BOOTSTRAP_MESSAGE_TYPE) {
176
+ if (Array.isArray(entry.details?.components)) return entry.details.components;
177
+ return [
178
+ ...(entry.content?.includes("<!-- freeflow-bootstrap:workflow -->") ? [BOOTSTRAP_COMPONENTS.workflow] : []),
179
+ ...(entry.content?.includes("<!-- freeflow-bootstrap:cognitive-routing -->")
180
+ ? [BOOTSTRAP_COMPONENTS.cognitiveRouting]
181
+ : []),
182
+ ];
183
+ }
184
+ if (entry.customType === WORKFLOW_BOOTSTRAP_MESSAGE_TYPE) {
185
+ return [BOOTSTRAP_COMPONENTS.workflow];
186
+ }
187
+ if (entry.customType === COGNITIVE_ROUTING_BOOTSTRAP_MESSAGE_TYPE) {
188
+ return [BOOTSTRAP_COMPONENTS.cognitiveRouting];
130
189
  }
131
190
  return [];
132
191
  }
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
- };
192
+ export function filterBootstrapMessage(message) {
193
+ return bootstrapEntryComponents(message).length === 0 ? message : undefined;
149
194
  }
150
195
  function isRecord(value) {
151
196
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -157,6 +202,12 @@ function validateCoreConfigFields(value) {
157
202
  if (value.enabled !== undefined && typeof value.enabled !== "boolean") {
158
203
  return "enabled must be a boolean";
159
204
  }
205
+ if (value.contextVirtualization !== undefined && typeof value.contextVirtualization !== "boolean") {
206
+ return "contextVirtualization must be a boolean";
207
+ }
208
+ if (value.conversationHistory !== undefined && typeof value.conversationHistory !== "boolean") {
209
+ return "conversationHistory must be a boolean";
210
+ }
160
211
  if (value.interactionContract !== undefined && typeof value.interactionContract !== "boolean") {
161
212
  return "interactionContract must be a boolean";
162
213
  }
@@ -179,6 +230,7 @@ function validateFreeflowConfigShape(value) {
179
230
  if (!isRecord(value)) {
180
231
  return "config must be a JSON object";
181
232
  }
233
+ // Keep retired capability keys shape-tolerated so existing repositories remain activated; runtime behavior ignores them.
182
234
  const allowedKeys = new Set([
183
235
  "enabled",
184
236
  "defaultMode",
@@ -187,6 +239,9 @@ function validateFreeflowConfigShape(value) {
187
239
  "outputRouter",
188
240
  "observedRouting",
189
241
  "scriptTransform",
242
+ "cognitiveRouting",
243
+ "contextVirtualization",
244
+ "conversationHistory",
190
245
  ]);
191
246
  for (const key of Object.keys(value)) {
192
247
  if (!allowedKeys.has(key)) {
@@ -206,7 +261,16 @@ function validateFreeflowLocalConfigShape(value) {
206
261
  if (!isRecord(value)) {
207
262
  return "local config must be a JSON object";
208
263
  }
209
- const allowedKeys = new Set(["enabled", "defaultMode", "interactionContract", "skills", "processing"]);
264
+ const allowedKeys = new Set([
265
+ "enabled",
266
+ "defaultMode",
267
+ "interactionContract",
268
+ "skills",
269
+ "processing",
270
+ "cognitiveRouting",
271
+ "contextVirtualization",
272
+ "conversationHistory",
273
+ ]);
210
274
  for (const key of Object.keys(value)) {
211
275
  if (!allowedKeys.has(key)) {
212
276
  return `unsupported top-level local config key: ${key}`;
@@ -276,6 +340,8 @@ function resolveLayeredValue(repository, local, key, fallback) {
276
340
  function resolveCoreConfig(repository, local) {
277
341
  const enabled = resolveLayeredValue(repository, local, "enabled", true);
278
342
  const interactionContract = resolveLayeredValue(repository, local, "interactionContract", true);
343
+ const contextVirtualization = resolveLayeredValue(repository, local, "contextVirtualization", false);
344
+ const conversationHistory = resolveLayeredValue(repository, local, "conversationHistory", false);
279
345
  const defaultMode = resolveLayeredValue(repository, local, "defaultMode", "workflow");
280
346
  const repositorySkills = isRecord(repository.skills) ? repository.skills : {};
281
347
  const localSkills = isRecord(local.skills) ? local.skills : {};
@@ -284,12 +350,16 @@ function resolveCoreConfig(repository, local) {
284
350
  config: {
285
351
  enabled: enabled.value,
286
352
  interactionContract: interactionContract.value,
353
+ contextVirtualization: contextVirtualization.value,
354
+ conversationHistory: conversationHistory.value,
287
355
  skills: { enabled: skillsEnabled.value },
288
356
  defaultMode: defaultMode.value,
289
357
  },
290
358
  sources: {
291
359
  enabled: enabled.source,
292
360
  interactionContract: interactionContract.source,
361
+ contextVirtualization: contextVirtualization.source,
362
+ conversationHistory: conversationHistory.source,
293
363
  skillsEnabled: skillsEnabled.source,
294
364
  defaultMode: defaultMode.source,
295
365
  },
@@ -310,18 +380,26 @@ function resolveSessionCoreConfig(layers) {
310
380
  const sources = layers.sources;
311
381
  const enabled = currentSessionOverrides.enabled;
312
382
  const interactionContract = currentSessionOverrides.interactionContract;
383
+ const contextVirtualization = currentSessionOverrides.contextVirtualization;
384
+ const conversationHistory = currentSessionOverrides.conversationHistory;
313
385
  const skillsEnabled = currentSessionOverrides.skillsEnabled;
314
386
  return {
315
387
  config: {
316
388
  enabled: typeof enabled === "boolean" ? enabled : configured.enabled,
317
389
  interactionContract:
318
390
  typeof interactionContract === "boolean" ? interactionContract : configured.interactionContract,
391
+ contextVirtualization:
392
+ typeof contextVirtualization === "boolean" ? contextVirtualization : configured.contextVirtualization,
393
+ conversationHistory:
394
+ typeof conversationHistory === "boolean" ? conversationHistory : configured.conversationHistory,
319
395
  skills: { enabled: typeof skillsEnabled === "boolean" ? skillsEnabled : configured.skills.enabled },
320
396
  defaultMode: configured.defaultMode,
321
397
  },
322
398
  sources: {
323
399
  enabled: typeof enabled === "boolean" ? "session" : sources.enabled,
324
400
  interactionContract: typeof interactionContract === "boolean" ? "session" : sources.interactionContract,
401
+ contextVirtualization: typeof contextVirtualization === "boolean" ? "session" : sources.contextVirtualization,
402
+ conversationHistory: typeof conversationHistory === "boolean" ? "session" : sources.conversationHistory,
325
403
  skillsEnabled: typeof skillsEnabled === "boolean" ? "session" : sources.skillsEnabled,
326
404
  defaultMode: sources.defaultMode,
327
405
  },
@@ -351,15 +429,60 @@ export async function readFreeflowConfigLayers(cwd) {
351
429
  parseError: blockingState?.parseError ?? null,
352
430
  };
353
431
  }
354
- export async function readCapabilityState(cwd) {
432
+ export async function readCapabilityState(cwd, host = undefined, extensionHost = undefined) {
355
433
  const layers = await readFreeflowConfigLayers(cwd);
356
- const parsed = layers.repository.valid ? layers.repository.parsed : {};
357
- const normalized = normalizeFreeflowConfig(parsed);
358
434
  const effectiveCore = resolveSessionCoreConfig(layers);
359
435
  const enabled = layers.configured && effectiveCore.config.enabled;
360
436
  const interactionContractConfigEnabled = effectiveCore.config.interactionContract;
361
437
  const skillsConfigEnabled = effectiveCore.config.skills.enabled;
362
- const outputRouterConfigEnabled = normalized.config.outputRouter.enabled;
438
+ const contextVirtualizationConfigEnabled = effectiveCore.config.contextVirtualization;
439
+ const conversationHistoryConfigEnabled = effectiveCore.config.conversationHistory;
440
+ const skillsEffective = enabled && skillsConfigEnabled;
441
+ const hostSupportsCognitiveRouting = isPiFlowHost(extensionHost);
442
+ const configuredCognitiveRouting = await resolveCognitiveRoutingState(
443
+ layers.repository.parsed,
444
+ layers.local.parsed,
445
+ hostSupportsCognitiveRouting ? host : undefined,
446
+ );
447
+ const disabledReason = enabled
448
+ ? skillsEffective
449
+ ? undefined
450
+ : { code: "disabled", message: "Skills are disabled" }
451
+ : { code: "disabled", message: "Freeflow is disabled" };
452
+ const childCapability = (configuredEnabled) => ({
453
+ enabled: configuredEnabled,
454
+ effective: skillsEffective && configuredEnabled,
455
+ ...(disabledReason ? { blockingReason: disabledReason } : {}),
456
+ });
457
+ const cognitiveRouting = enabled
458
+ ? skillsEffective
459
+ ? !hostSupportsCognitiveRouting && configuredCognitiveRouting.configValid
460
+ ? {
461
+ ...configuredCognitiveRouting,
462
+ effective: false,
463
+ blockingReason: {
464
+ code: "runtime_disabled",
465
+ message: "Cognitive Routing is disabled outside the PiFlow runtime",
466
+ },
467
+ }
468
+ : configuredCognitiveRouting
469
+ : {
470
+ ...configuredCognitiveRouting,
471
+ effective: false,
472
+ blockingReason: {
473
+ code: "disabled",
474
+ message: "Skills are disabled",
475
+ },
476
+ }
477
+ : {
478
+ ...configuredCognitiveRouting,
479
+ enabled: false,
480
+ effective: false,
481
+ blockingReason: {
482
+ code: "disabled",
483
+ message: "Freeflow is disabled",
484
+ },
485
+ };
363
486
  return {
364
487
  configured: layers.configured,
365
488
  repositoryConfigured: layers.repositoryConfigured,
@@ -385,63 +508,13 @@ export async function readCapabilityState(cwd) {
385
508
  enabled: skillsConfigEnabled,
386
509
  effective: enabled && skillsConfigEnabled,
387
510
  },
388
- outputRouter: {
389
- enabled: enabled && outputRouterConfigEnabled,
390
- configEnabled: outputRouterConfigEnabled,
391
- },
511
+ contextVirtualization: childCapability(contextVirtualizationConfigEnabled),
512
+ conversationHistory: childCapability(conversationHistoryConfigEnabled),
513
+ hostSupportsCognitiveRouting,
514
+ cognitiveRouting,
392
515
  };
393
516
  }
394
517
  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
518
  export function restoreModeOverride(ctx) {
446
519
  currentModeOverride = null;
447
520
  currentSessionOverrides = {};
@@ -479,7 +552,13 @@ export async function readModeState(cwd) {
479
552
  effectiveMode: active ? resolvedMode : null,
480
553
  };
481
554
  }
482
- export function setModeStatus(ctx, modeState, capabilityState = undefined) {
555
+ export function setModeStatus(
556
+ ctx,
557
+ modeState,
558
+ capabilityState = undefined,
559
+ cognitiveRoutingRuntime = undefined,
560
+ options = {},
561
+ ) {
483
562
  if (capabilityState && !capabilityState.configured) {
484
563
  ctx.ui.setStatus("freeflow", capabilityState.configExists ? "freeflow: config error" : "freeflow: setup needed");
485
564
  return;
@@ -500,8 +579,39 @@ export function setModeStatus(ctx, modeState, capabilityState = undefined) {
500
579
  } else if (capabilityState?.configSources?.skillsEnabled === "session") {
501
580
  active.push("skills off (session)");
502
581
  }
503
- if (capabilityState?.outputRouter.enabled) {
504
- active.push("router");
582
+ const cognitiveRouting = capabilityState?.cognitiveRouting;
583
+ const cognitiveRoutingActive = cognitiveRouting?.effective === true && cognitiveRoutingRuntime?.effective === true;
584
+ const startupSelectionSuppressesCognitiveRouting =
585
+ ctx?.modelStateProvenance?.explicitModel === true || ctx?.modelStateProvenance?.explicitThinking === true;
586
+ if (cognitiveRoutingActive) {
587
+ const profile = cognitiveRoutingRuntime.activeProfile;
588
+ const control =
589
+ cognitiveRoutingRuntime.controlMode === "manual-standard" ||
590
+ cognitiveRoutingRuntime.controlMode === "manual-reasoning"
591
+ ? "manual hold"
592
+ : "automatic";
593
+ active.push(`${profile} · ${control}`);
594
+ } else if (cognitiveRouting?.enabled === true) {
595
+ if (cognitiveRouting.blockingReason?.code === "runtime_disabled") {
596
+ active.push("cognitive disabled · PiFlow only");
597
+ } else if (
598
+ cognitiveRouting.effective === true &&
599
+ cognitiveRoutingRuntime === undefined &&
600
+ options.cognitiveRoutingStartupPending === true &&
601
+ !startupSelectionSuppressesCognitiveRouting
602
+ ) {
603
+ const startupProfile = cognitiveRouting.sessionStart?.profile ?? "standard";
604
+ active.push(`${startupProfile} · pending`);
605
+ } else {
606
+ const reason =
607
+ cognitiveRouting?.effective === true
608
+ ? (cognitiveRoutingRuntime?.blockingReason?.code ?? "runtime_inactive")
609
+ : (cognitiveRouting.blockingReason?.code ?? "unavailable");
610
+ active.push(`cognitive blocked · ${reason}`);
611
+ }
612
+ }
613
+ if (capabilityState?.contextVirtualization?.effective || capabilityState?.conversationHistory?.effective) {
614
+ active.push("context");
505
615
  }
506
616
  ctx.ui.setStatus("freeflow", `freeflow: ${active.length > 0 ? active.join(" · ") : "idle"}`);
507
617
  }
@@ -521,156 +631,93 @@ export function skillPrompt(skill, args) {
521
631
  const trimmed = args?.trim();
522
632
  return trimmed ? `/skill:${skill}\n\n${trimmed}` : `/skill:${skill}`;
523
633
  }
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
634
+ function publicCognitiveRoutingControl(controlMode) {
635
+ if (controlMode === "automatic") return "automatic";
636
+ if (controlMode === "manual-standard" || controlMode === "manual-reasoning") return "manual";
637
+ return "unavailable";
638
+ }
639
+ function publicCognitiveRoutingProfile(activeProfile, effective) {
640
+ if (effective !== true) return "unavailable";
641
+ return activeProfile === "standard" || activeProfile === "reasoning" ? activeProfile : "unavailable";
642
+ }
643
+ function publicCapabilityStatus(capability) {
644
+ if (capability?.effective === true) return "active";
645
+ if (!capability) return "unavailable";
646
+ const blockingCode = capability.blockingReason?.code;
647
+ if (blockingCode && blockingCode !== "disabled") return "unavailable";
648
+ return "inactive";
649
+ }
650
+ export function freeflowRuntimeStateMessage(modeState, capabilityState, cognitiveRoutingRuntime = undefined) {
651
+ const cognitiveRoutingEffective = capabilityState?.cognitiveRouting?.effective === true;
652
+ const profile = publicCognitiveRoutingProfile(
653
+ cognitiveRoutingRuntime?.activeProfile,
654
+ cognitiveRoutingEffective && cognitiveRoutingRuntime?.effective === true,
567
655
  );
656
+ const control =
657
+ profile === "unavailable" ? "unavailable" : publicCognitiveRoutingControl(cognitiveRoutingRuntime?.controlMode);
658
+ const defaultMode = modeState?.defaultMode ?? "workflow";
659
+ const activeMode = modeState?.effectiveMode ?? "inactive";
660
+ return {
661
+ role: "custom",
662
+ customType: FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE,
663
+ content: [
664
+ "# Freeflow Runtime State",
665
+ "",
666
+ "This is extension-generated runtime state. Use it to interpret the stable Freeflow guidance.",
667
+ "",
668
+ `Default mode: \`${defaultMode}\``,
669
+ `Active mode: \`${activeMode}\``,
670
+ "",
671
+ "Capabilities:",
672
+ `- Interaction Contract: ${publicCapabilityStatus(capabilityState?.interactionContract)}`,
673
+ `- Skills: ${publicCapabilityStatus(capabilityState?.skills)}`,
674
+ `- Context Virtualization: ${publicCapabilityStatus(capabilityState?.contextVirtualization)}`,
675
+ `- Conversation History: ${publicCapabilityStatus(capabilityState?.conversationHistory)}`,
676
+ `- Cognitive Routing: ${publicCapabilityStatus(capabilityState?.cognitiveRouting)}`,
677
+ "",
678
+ "Cognitive Routing:",
679
+ `- Control: \`${control}\``,
680
+ `- Profile: \`${profile}\``,
681
+ ].join("\n"),
682
+ display: false,
683
+ details: { source: "provider-request-runtime-state" },
684
+ };
568
685
  }
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) {
686
+ export function withoutFreeflowRuntimeState(messages) {
687
+ return (Array.isArray(messages) ? messages : []).filter(
688
+ (message) =>
689
+ message?.customType !== FREEFLOW_RUNTIME_STATE_MESSAGE_TYPE &&
690
+ message?.customType !== COGNITIVE_ROUTING_RUNTIME_STATE_MESSAGE_TYPE,
691
+ );
692
+ }
693
+ export function withFreeflowRuntimeState(messages, modeState, capabilityState, cognitiveRoutingRuntime = undefined) {
694
+ return [
695
+ ...withoutFreeflowRuntimeState(messages),
696
+ freeflowRuntimeStateMessage(modeState, capabilityState, cognitiveRoutingRuntime),
697
+ ];
698
+ }
699
+ export function runtimeContext(freeflowContext, capabilityState) {
700
+ if (capabilityState?.configured !== true || capabilityState?.enabled !== true || !freeflowContext?.corePrompt) {
643
701
  return "";
644
702
  }
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.`;
703
+ const blocks = [];
704
+ if (freeflowContext?.corePrompt) blocks.push(freeflowContext.corePrompt.trim());
705
+ if (capabilityState.interactionContract?.effective === true && freeflowContext?.interactionContractPrompt) {
706
+ blocks.push(freeflowContext.interactionContractPrompt.trim());
707
+ }
708
+ if (capabilityState.skills?.effective === true && freeflowContext?.skillsPrompt) {
709
+ blocks.push(freeflowContext.skillsPrompt.trim());
710
+ }
711
+ if (capabilityState.cognitiveRouting?.effective === true && freeflowContext?.cognitiveRoutingPrompt) {
712
+ blocks.push(freeflowContext.cognitiveRoutingPrompt.trim());
713
+ }
714
+ if (capabilityState.contextVirtualization?.effective === true && freeflowContext?.contextVirtualizationPrompt) {
715
+ blocks.push(freeflowContext.contextVirtualizationPrompt.trim());
716
+ }
717
+ if (capabilityState.conversationHistory?.effective === true && freeflowContext?.conversationHistoryPrompt) {
718
+ blocks.push(freeflowContext.conversationHistoryPrompt.trim());
719
+ }
720
+ return blocks.filter(Boolean).join("\n\n");
674
721
  }
675
722
  export async function setSessionCoreOverride(key, value, ctx, pi) {
676
723
  if (!SESSION_CORE_KEYS.has(key)) {
@@ -695,7 +742,7 @@ export async function setSessionCoreOverride(key, value, ctx, pi) {
695
742
  changed: true,
696
743
  reloadRequired: key === "enabled" || key === "skillsEnabled",
697
744
  sessionOverrides: { ...currentSessionOverrides },
698
- capabilityState: await readCapabilityState(ctx.cwd),
745
+ capabilityState: await readCapabilityState(ctx.cwd, ctx, pi?.host),
699
746
  };
700
747
  }
701
748
  export async function resetSessionOverrides(ctx, pi) {
@@ -728,7 +775,7 @@ export async function setSessionMode(mode, ctx, pi) {
728
775
  }
729
776
  export async function handleModeCommand(args, ctx, pi) {
730
777
  const arg = args?.trim();
731
- const capabilityState = await readCapabilityState(ctx.cwd);
778
+ const capabilityState = await readCapabilityState(ctx.cwd, ctx, pi?.host);
732
779
  const inactiveModeState = await readModeState(ctx.cwd);
733
780
  if (!capabilityState.configured) {
734
781
  setModeStatus(ctx, inactiveModeState, capabilityState);