@hassangameryt/freeflow 0.7.3 → 0.8.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 (276) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/NOTICE +14 -0
  6. package/README.md +26 -31
  7. package/capabilities/cognitive-routing/SKILL.md +106 -161
  8. package/capabilities/cognitive-routing/references/both-mode.md +36 -0
  9. package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
  10. package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +239 -0
  13. package/command-surface.json +4 -0
  14. package/gemini-extension.json +1 -1
  15. package/hooks/shared/runtime-context.mjs +28 -30
  16. package/licenses/Apache-2.0.txt +55 -0
  17. package/package.json +22 -9
  18. package/pi-extension/README.md +41 -0
  19. package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
  20. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
  21. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  22. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  23. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
  24. package/pi-extension/dist/cognitive-routing/gate.js +241 -0
  25. package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
  26. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  27. package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
  28. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
  29. package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
  30. package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
  31. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
  32. package/pi-extension/dist/cognitive-routing/session.js +221 -0
  33. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
  34. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
  35. package/pi-extension/dist/cognitive-routing/status.js +80 -0
  36. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
  37. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
  38. package/pi-extension/dist/compaction/carry.js +191 -0
  39. package/pi-extension/dist/compaction/config.js +22 -0
  40. package/pi-extension/dist/compaction/controller.js +471 -0
  41. package/pi-extension/dist/compaction/harness.js +77 -0
  42. package/pi-extension/dist/compaction/results.js +158 -0
  43. package/pi-extension/dist/compaction/thresholds.js +35 -0
  44. package/pi-extension/dist/compaction/tool.js +57 -0
  45. package/pi-extension/dist/host/branch.js +13 -0
  46. package/pi-extension/dist/host/canonical.js +12 -0
  47. package/pi-extension/dist/host/catalog.js +77 -0
  48. package/pi-extension/dist/host/config.js +374 -0
  49. package/pi-extension/dist/host/projection-tags.js +60 -0
  50. package/pi-extension/dist/host/prompts.js +82 -0
  51. package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
  52. package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
  53. package/pi-extension/dist/host/runtime-state.js +189 -0
  54. package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
  55. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
  56. package/pi-extension/dist/host/staging.js +28 -0
  57. package/pi-extension/dist/host/status.js +101 -0
  58. package/pi-extension/dist/index.js +423 -173
  59. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  60. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  61. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  62. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  63. package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
  64. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  65. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  66. package/pi-extension/dist/provider-support/effort.js +13 -0
  67. package/pi-extension/dist/provider-support/index.js +44 -3
  68. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  69. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  70. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  71. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  72. package/pi-extension/dist/provider-support/routes.js +17 -0
  73. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  74. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  76. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  77. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  78. package/pi-extension/dist/tool-execution/background.js +238 -0
  79. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  80. package/pi-extension/dist/tool-execution/config.js +22 -0
  81. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  82. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  83. package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
  84. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  85. package/pi-extension/dist/tool-execution/tools.js +16 -0
  86. package/pi-extension/freeflow/README.md +9 -0
  87. package/plugin.json +1 -1
  88. package/runtime/prompts/cognitive-routing.md +4 -4
  89. package/runtime/prompts/core.md +22 -75
  90. package/runtime/prompts/interaction-contract.md +15 -8
  91. package/runtime/prompts/tool-execution.md +38 -0
  92. package/runtime/prompts/working-method.md +108 -0
  93. package/skills/action-selection/SKILL.md +39 -47
  94. package/skills/action-selection/references/trajectory-stalls.md +2 -0
  95. package/skills/bypass/SKILL.md +2 -2
  96. package/skills/commit-work/SKILL.md +31 -83
  97. package/skills/commit-work/references/git-state-edges.md +1 -1
  98. package/skills/commit-work/references/simple-push.md +2 -0
  99. package/skills/commit-work/references/staging-decisions.md +1 -1
  100. package/skills/decision-gate/SKILL.md +31 -50
  101. package/skills/design-for-depth/SKILL.md +44 -53
  102. package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
  103. package/skills/design-for-depth/references/implementation-decisions.md +1 -1
  104. package/skills/design-for-depth/references/interface-design-loop.md +5 -36
  105. package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
  106. package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
  107. package/skills/diagnose-failure/SKILL.md +39 -24
  108. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
  109. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
  110. package/skills/discuss/SKILL.md +35 -11
  111. package/skills/discuss/references/discussion-continuity.md +7 -27
  112. package/skills/evaluate-skill/SKILL.md +49 -80
  113. package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
  114. package/skills/evaluate-skill/references/evaluation-design.md +14 -14
  115. package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
  116. package/skills/evaluate-skill/references/review-and-revision.md +3 -1
  117. package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
  118. package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
  119. package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
  120. package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
  121. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
  122. package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
  123. package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
  124. package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
  125. package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
  126. package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
  127. package/skills/execute-work/SKILL.md +41 -9
  128. package/skills/execute-work/references/code-practices.md +2 -0
  129. package/skills/execute-work/references/execute-work-edges.md +11 -1
  130. package/skills/handoff/SKILL.md +1 -1
  131. package/skills/handoff/references/handoff-templates.md +1 -1
  132. package/skills/review-artifact/SKILL.md +42 -41
  133. package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
  134. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  135. package/skills/review-work/SKILL.md +38 -32
  136. package/skills/review-work/references/adjudicate-work-review.md +1 -1
  137. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  138. package/skills/review-work/references/security-risk-lens.md +2 -4
  139. package/skills/setup-freeflow/SKILL.md +3 -1
  140. package/skills/setup-freeflow/references/activation-contract.md +7 -13
  141. package/skills/setup-freeflow/references/host-setup.md +3 -3
  142. package/skills/track-work/SKILL.md +113 -161
  143. package/skills/track-work/references/working-record-format.md +189 -350
  144. package/skills/track-work/scripts/lib/document.mjs +62 -6
  145. package/skills/track-work/scripts/lib/format.mjs +16 -1
  146. package/skills/track-work/scripts/lib/store.mjs +9 -8
  147. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  148. package/skills/track-work/scripts/working-record.mjs +34 -12
  149. package/skills/verify-work/SKILL.md +29 -20
  150. package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
  151. package/skills/verify-work/references/integration-evidence.md +2 -0
  152. package/skills/verify-work/references/performance-evidence.md +3 -1
  153. package/skills/verify-work/references/test-design.md +3 -1
  154. package/skills/workflow/SKILL.md +65 -21
  155. package/skills/workflow/references/domain-skill-composition.md +5 -3
  156. package/skills/write-docs/SKILL.md +83 -0
  157. package/skills/write-docs/references/documentation-shapes.md +119 -0
  158. package/skills/write-docs/scripts/docs-check.mjs +139 -0
  159. package/skills/write-plan/SKILL.md +40 -40
  160. package/skills/write-plan/references/plan-shapes.md +1 -1
  161. package/skills/write-skill/SKILL.md +107 -147
  162. package/skills/write-skill/references/activation-boundaries.md +2 -0
  163. package/skills/write-skill/references/agent-first-instructions.md +22 -10
  164. package/skills/write-skill/references/progressive-disclosure.md +4 -2
  165. package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
  166. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  167. package/skills/write-skill/scripts/skill-author.mjs +18 -2
  168. package/skills/write-spec/SKILL.md +34 -41
  169. package/skills/write-spec/references/artifact-standards.md +1 -1
  170. package/skills/write-spec/references/decision-records.md +2 -0
  171. package/skills/write-spec/references/spec-shapes.md +1 -1
  172. package/capabilities/context-virtualization/SKILL.md +0 -103
  173. package/capabilities/conversation-history/SKILL.md +0 -53
  174. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  175. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  176. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  177. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  178. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  179. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  180. package/pi-extension/dist/context-virtualization/state.js +0 -57
  181. package/pi-extension/dist/context-virtualization/types.js +0 -10
  182. package/pi-extension/dist/conversation-history/passages.js +0 -66
  183. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  184. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  185. package/pi-extension/dist/conversation-history/search.js +0 -56
  186. package/pi-extension/dist/conversation-history/types.js +0 -1
  187. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  188. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  189. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  190. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  191. package/pi-extension/dist/freeflow-context/types.js +0 -12
  192. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  193. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  194. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  195. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  196. package/pi-extension/dist/session-sources/provenance.js +0 -32
  197. package/pi-extension/dist/ui/text-component.js +0 -57
  198. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  199. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  200. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  201. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  202. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  203. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  204. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  205. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  206. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  207. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  208. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  209. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  210. package/pi-extension/src/context-virtualization/state.ts +0 -79
  211. package/pi-extension/src/context-virtualization/types.ts +0 -92
  212. package/pi-extension/src/conversation-history/passages.ts +0 -81
  213. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  214. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  215. package/pi-extension/src/conversation-history/search.ts +0 -79
  216. package/pi-extension/src/conversation-history/types.ts +0 -31
  217. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  218. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  219. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  220. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  221. package/pi-extension/src/freeflow-context/types.ts +0 -70
  222. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  223. package/pi-extension/src/index.ts +0 -390
  224. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  225. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  226. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  227. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  228. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  229. package/pi-extension/src/provider-support/index.ts +0 -5
  230. package/pi-extension/src/runtime/request-history.ts +0 -243
  231. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  232. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  233. package/pi-extension/src/session-sources/events.ts +0 -230
  234. package/pi-extension/src/session-sources/provenance.ts +0 -39
  235. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  236. package/pi-extension/src/session-sources/sources.ts +0 -334
  237. package/pi-extension/src/settings/settings-tui.ts +0 -501
  238. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  239. package/pi-extension/src/ui/text-component.ts +0 -66
  240. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  241. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  242. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  243. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  244. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  245. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  246. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  247. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  248. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  249. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  250. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  251. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  252. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  253. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  254. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  255. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  256. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  257. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  258. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  259. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  260. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  261. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  262. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  263. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  264. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  265. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  266. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  267. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  268. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  269. package/pi-extension/tests/provider-support/native.test.js +0 -183
  270. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  271. package/pi-extension/tsconfig.json +0 -16
  272. package/runtime/prompts/context-virtualization.md +0 -3
  273. package/runtime/prompts/conversation-history.md +0 -3
  274. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  275. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  276. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +0 -0
@@ -0,0 +1,158 @@
1
+ import { resolve, sep } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { estimateTokens } from "./carry.js";
4
+ import { currentCycle } from "./harness.js";
5
+ /**
6
+ * The result index: this cycle's larger tool results, each with an id the agent can carry by. Ids number tool results
7
+ * in branch order (r1, r2, ...), so they stay stable while the branch grows, and a result from an earlier cycle still
8
+ * resolves because its entry stays stored after compaction.
9
+ */
10
+ const SIZE_FLOOR = 500;
11
+ const MAX_ROWS = 40;
12
+ /**
13
+ * The most tokens the rendered index can take: 40 rows of at most about 140 characters (an 80-character label plus
14
+ * id, tool, size and carried note) and a heading. Below the warning by more than this, the index cannot reach it.
15
+ */
16
+ export const INDEX_TOKEN_BOUND = 2_000;
17
+ /** Freeflow's own control calls: no evidence to carry, as routing's evidence selection also excludes them. */
18
+ const CONTROL_TOOLS = new Set([
19
+ "freeflow_delegate",
20
+ "freeflow_return",
21
+ "freeflow_project",
22
+ "freeflow_unit",
23
+ "freeflow_compact",
24
+ ]);
25
+ const resultText = (message) =>
26
+ (message?.content ?? [])
27
+ .map((part) => (part?.type === "text" ? part.text : part?.type === "image" ? "[image]" : ""))
28
+ .join("");
29
+ function label(name, input) {
30
+ const firstLine = (text) =>
31
+ text
32
+ .split("\n")
33
+ .map((line) => line.trim())
34
+ .find(Boolean) ?? "";
35
+ const value =
36
+ typeof input?.command === "string"
37
+ ? input.command
38
+ : typeof input?.path === "string"
39
+ ? input.path
40
+ : typeof input?.url === "string"
41
+ ? input.url
42
+ : typeof input?.code === "string"
43
+ ? `script: ${firstLine(input.code)}`
44
+ : Array.isArray(input?.queries)
45
+ ? input.queries.join("; ")
46
+ : typeof input?.query === "string"
47
+ ? input.query
48
+ : name;
49
+ const line = firstLine(String(value));
50
+ return line.length > 80 ? `${line.slice(0, 77)}...` : line;
51
+ }
52
+ /** The Freeflow package root: reads of its skills, references and prompts are instructions, not work to carry. */
53
+ const FREEFLOW_ROOT = fileURLToPath(new URL("../../../", import.meta.url));
54
+ const INSTRUCTION_DIRS = ["capabilities", "skills", "runtime"].map((dir) => resolve(FREEFLOW_ROOT, dir) + sep);
55
+ function isFreeflowInstruction(tool, input) {
56
+ if (tool !== "read" || typeof input?.path !== "string") return false;
57
+ const path = resolve(input.path);
58
+ return INSTRUCTION_DIRS.some((dir) => path.startsWith(dir));
59
+ }
60
+ /** Every tool result on the branch with its id, oldest first. */
61
+ function allResults(branch) {
62
+ const calls = new Map();
63
+ const results = [];
64
+ for (const entry of branch) {
65
+ const message = entry?.type === "message" ? entry.message : undefined;
66
+ if (message?.role === "assistant")
67
+ for (const block of message.content ?? [])
68
+ if (block?.type === "toolCall") calls.set(block.id, { name: block.name, input: block.arguments });
69
+ if (message?.role !== "toolResult") continue;
70
+ const call = calls.get(message.toolCallId);
71
+ const text = resultText(message);
72
+ const tool = call?.name ?? message.toolName ?? "tool";
73
+ results.push({
74
+ id: `r${results.length + 1}`,
75
+ tool,
76
+ label: label(tool, call?.input),
77
+ tokens: estimateTokens(text),
78
+ text,
79
+ ...firstLineOf(tool, call?.input, message),
80
+ entry,
81
+ input: call?.input,
82
+ });
83
+ }
84
+ return results;
85
+ }
86
+ /** A text read of a file starts at its offset (Pi's read is 1-based); an image or failed read has no lines. */
87
+ function firstLineOf(tool, input, message) {
88
+ if (tool !== "read" || typeof input?.path !== "string" || message?.isError) return {};
89
+ if ((message?.content ?? []).some((part) => part?.type === "image")) return {};
90
+ return { firstLine: Number.isInteger(input?.offset) && input.offset > 0 ? input.offset : 1 };
91
+ }
92
+ /** The first line of the read result in a session entry, for a result carried by routing ref. */
93
+ export function resultFirstLine(branch, entryId) {
94
+ return allResults(branch).find((result) => result.entry?.id === entryId)?.firstLine;
95
+ }
96
+ export function resolveResult(branch, id) {
97
+ const found = allResults(branch).find((result) => result.id === id);
98
+ if (!found) return undefined;
99
+ const { entry: _entry, input: _input, ...result } = found;
100
+ return result;
101
+ }
102
+ /**
103
+ * The tool results of the work in progress, newest first: the current assignment's under Cognitive Routing (by
104
+ * routing ref), otherwise this cycle's (by r-id). Freeflow's control calls and reads of its own instructions are left
105
+ * out: they are how the agent works, not what it worked on.
106
+ */
107
+ export function scopeResults(branch, assignment) {
108
+ const cycle = new Set(currentCycle(branch));
109
+ return allResults(branch)
110
+ .filter((result) => (assignment ? assignment.entryIds.has(result.entry.id) : cycle.has(result.entry)))
111
+ .filter((result) => !CONTROL_TOOLS.has(result.tool) && !isFreeflowInstruction(result.tool, result.input))
112
+ .reverse()
113
+ .map(({ entry, input, ...result }) => ({
114
+ ...result,
115
+ id: assignment ? `ctx:${entry.id}` : result.id,
116
+ entryId: entry.id,
117
+ ...(typeof input?.path === "string" ? { path: input.path } : {}),
118
+ }));
119
+ }
120
+ /** This cycle's results above the size floor, newest first, then results the last Freeflow compaction carried. */
121
+ export function resultIndex(branch) {
122
+ const cycle = new Set(currentCycle(branch));
123
+ const results = allResults(branch);
124
+ const rows = results
125
+ .filter(
126
+ (result) =>
127
+ cycle.has(result.entry) &&
128
+ result.tokens >= SIZE_FLOOR &&
129
+ !CONTROL_TOOLS.has(result.tool) &&
130
+ !isFreeflowInstruction(result.tool, result.input),
131
+ )
132
+ .reverse()
133
+ .map(({ id, tool, label, tokens }) => ({ id, tool, label, tokens }));
134
+ const latest = [...branch].reverse().find((entry) => entry?.type === "compaction" && entry.details?.freeflow);
135
+ for (const item of latest?.details?.freeflow?.carried ?? []) {
136
+ if (item?.kind !== "result" || rows.some((row) => row.id === item.ref)) continue;
137
+ const result = results.find((each) => each.id === item.ref);
138
+ if (result)
139
+ rows.push({
140
+ id: result.id,
141
+ tool: result.tool,
142
+ label: result.label,
143
+ tokens: result.tokens,
144
+ firstCycle: item.firstCycle,
145
+ });
146
+ }
147
+ return rows.slice(0, MAX_ROWS);
148
+ }
149
+ export function renderIndex(rows) {
150
+ if (!rows.length) return "No tool results large enough to list; carry files by path.";
151
+ return [
152
+ "Results you can carry by id:",
153
+ ...rows.map(
154
+ (row) =>
155
+ `- ${row.id} ${row.tool} ${row.label} ~${row.tokens} tokens${row.firstCycle ? ` (carried since cycle ${row.firstCycle})` : ""}`,
156
+ ),
157
+ ].join("\n");
158
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * When compaction is due. Pi compacts on its own once context passes the window minus its reserve, and stays as the
3
+ * fallback for a full context. Freeflow says "compact now" 25k tokens before that, leaving room to prepare after a
4
+ * safe point, and warns earlier still: at 70% of the window, and at least 20k tokens before "compact now", so the
5
+ * agent has room to reach a safe point first.
6
+ */
7
+ export const DEFAULT_RESERVE_TOKENS = 16_384;
8
+ const WARNING_SHARE = 0.7;
9
+ const WARNING_LEAD = 20_000;
10
+ const COMPACT_NOW_HEADROOM = 25_000;
11
+ /** Pi's effective reserve for a model: its per-model override, else the ordinary setting, else Pi's default. */
12
+ export function reserveTokens(settings, model) {
13
+ const compaction = settings?.compaction ?? {};
14
+ const override = model ? compaction.modelOverrides?.[`${model.provider}/${model.id}`] : undefined;
15
+ const value = override?.reserveTokens ?? compaction.reserveTokens;
16
+ return Number.isSafeInteger(value) && value >= 0 ? value : DEFAULT_RESERVE_TOKENS;
17
+ }
18
+ export function thresholds(window, reserve) {
19
+ const trigger = Math.max(0, window - reserve);
20
+ const compactNow = Math.max(0, trigger - COMPACT_NOW_HEADROOM);
21
+ return {
22
+ window,
23
+ trigger,
24
+ warning: Math.max(0, Math.min(Math.floor(window * WARNING_SHARE), compactNow - WARNING_LEAD)),
25
+ compactNow,
26
+ };
27
+ }
28
+ /** The strictest thresholds across models that may receive the full history (the active model and routing workers). */
29
+ export function strictest(models, settings) {
30
+ const all = models
31
+ .filter((model) => typeof model?.contextWindow === "number" && model.contextWindow > 0)
32
+ .map((model) => thresholds(model.contextWindow, reserveTokens(settings, model)));
33
+ if (!all.length) return undefined;
34
+ return all.reduce((a, b) => (b.warning < a.warning ? b : a));
35
+ }
@@ -0,0 +1,57 @@
1
+ export const FREEFLOW_COMPACT = "freeflow_compact";
2
+ const DESCRIPTION =
3
+ "Compact the conversation into a new cycle. Use only when Freeflow says compaction is due or the user asks, and read the compaction skill first. Pass your summary and what to carry; compaction happens at the end of this turn, and the run continues from the summary, the carried context and a recovery message.";
4
+ /** Registered once, declared only while Freeflow compaction is effective (see applyCompactionTools). */
5
+ export function registerCompactionTool(pi, controller) {
6
+ pi.registerTool({
7
+ name: FREEFLOW_COMPACT,
8
+ label: "Compact",
9
+ description: DESCRIPTION,
10
+ parameters: {
11
+ type: "object",
12
+ additionalProperties: false,
13
+ properties: {
14
+ summary: {
15
+ type: "string",
16
+ description:
17
+ "The next cycle's working state, for a reader with none of this conversation. The compaction skill says what it must keep.",
18
+ },
19
+ carry: {
20
+ type: "array",
21
+ description:
22
+ "What to carry into the next cycle, within the budget the notice gives: files by path (read fresh at compaction, optional line ranges) and tool results by id from the notice's index.",
23
+ items: {
24
+ type: "object",
25
+ additionalProperties: false,
26
+ properties: {
27
+ file: { type: "string" },
28
+ lines: { type: "array", items: { type: "integer" }, description: "[first, last], 1-based" },
29
+ result: {
30
+ type: "string",
31
+ description:
32
+ "A tool result: its routing ref (such as ctx:1a2b3c4d) when Cognitive Routing is on, otherwise its id from the notice's list (such as r12)",
33
+ },
34
+ },
35
+ },
36
+ },
37
+ },
38
+ required: ["summary"],
39
+ },
40
+ // Callable from codemode scripts as well: agents that look for tools there first concluded, when it was missing,
41
+ // that they could not compact. The compaction still happens at the end of the turn, after the script returns.
42
+ exposure: "direct",
43
+ defaultActive: false,
44
+ executionMode: "sequential",
45
+ async execute(_id, params, _signal, _update, ctx) {
46
+ return { content: [{ type: "text", text: await controller.request(params, ctx) }] };
47
+ },
48
+ });
49
+ }
50
+ /** Declare or withdraw freeflow_compact when compaction's effective state changes, keeping other tools in place. */
51
+ export function applyCompactionTools(pi, effective) {
52
+ if (typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function") return;
53
+ const current = pi.getActiveTools();
54
+ const present = current.includes(FREEFLOW_COMPACT);
55
+ if (effective && !present) pi.setActiveTools([...current, FREEFLOW_COMPACT]);
56
+ if (!effective && present) pi.setActiveTools(current.filter((name) => name !== FREEFLOW_COMPACT));
57
+ }
@@ -0,0 +1,13 @@
1
+ // Keyed by the reader and its getBranch function, so a reader whose source is swapped is walked again.
2
+ const cache = new WeakMap();
3
+ export function cachedBranch(reader) {
4
+ if (!reader?.getBranch) return [];
5
+ // Without a leaf id the branch cannot be keyed; fall back to Pi's walk.
6
+ if (!reader.getLeafId) return reader.getBranch();
7
+ const leaf = reader.getLeafId();
8
+ const hit = cache.get(reader);
9
+ if (hit && hit.leaf === leaf && hit.source === reader.getBranch) return hit.branch;
10
+ const branch = Object.freeze([...reader.getBranch()]);
11
+ cache.set(reader, { leaf, source: reader.getBranch, branch });
12
+ return branch;
13
+ }
@@ -0,0 +1,12 @@
1
+ const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
2
+ /** Deterministic JSON: object keys sorted, undefined properties omitted, so equal values serialize equally. */
3
+ export function canonical(value) {
4
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
5
+ if (isObject(value))
6
+ return `{${Object.keys(value)
7
+ .filter((k) => value[k] !== undefined)
8
+ .sort()
9
+ .map((k) => `${JSON.stringify(k)}:${canonical(value[k])}`)
10
+ .join(",")}}`;
11
+ return JSON.stringify(value) ?? "null";
12
+ }
@@ -0,0 +1,77 @@
1
+ import { fileURLToPath } from "node:url";
2
+ /**
3
+ * Freeflow's fixed command and skill surface: the commands it registers, the skills it lists to the model, and where
4
+ * their files live.
5
+ */
6
+ export const WORKFLOW_COMMANDS = [
7
+ { command: "discuss", skill: "discuss" },
8
+ { command: "action-selection", skill: "action-selection" },
9
+ { command: "track-work", skill: "track-work" },
10
+ { command: "write-spec", skill: "write-spec" },
11
+ { command: "review-artifact", skill: "review-artifact" },
12
+ { command: "write-plan", skill: "write-plan" },
13
+ { command: "write-docs", skill: "write-docs" },
14
+ { command: "execute-work", skill: "execute-work" },
15
+ { command: "simplify-code", skill: "simplify-code" },
16
+ { command: "migration-work", skill: "migration-work" },
17
+ { command: "diagnose-failure", skill: "diagnose-failure" },
18
+ { command: "verify-work", skill: "verify-work" },
19
+ { command: "review-work", skill: "review-work" },
20
+ { command: "commit-work", skill: "commit-work" },
21
+ { command: "handoff", skill: "handoff" },
22
+ { command: "finish-branch", skill: "finish-branch" },
23
+ { command: "release-work", skill: "release-work" },
24
+ { command: "launch-work", skill: "launch-work" },
25
+ { command: "bypass", skill: "bypass" },
26
+ ];
27
+ export const CONTRIBUTOR_COMMANDS = ["setup-freeflow", "write-skill", "evaluate-skill"];
28
+ const FREEFLOW_MODEL_SKILL_NAMES = [
29
+ "action-selection",
30
+ "bypass",
31
+ "commit-work",
32
+ "decision-gate",
33
+ "migration-work",
34
+ "design-for-depth",
35
+ "diagnose-failure",
36
+ "discuss",
37
+ "evaluate-skill",
38
+ "execute-work",
39
+ "finish-branch",
40
+ "handoff",
41
+ "release-work",
42
+ "review-artifact",
43
+ "review-work",
44
+ "setup-freeflow",
45
+ "launch-work",
46
+ "simplify-code",
47
+ "track-work",
48
+ "verify-work",
49
+ "workflow",
50
+ "write-docs",
51
+ "write-plan",
52
+ "write-skill",
53
+ "write-spec",
54
+ ];
55
+ export const STABLE_FREEFLOW_SURFACE = Object.freeze({
56
+ enabled: true,
57
+ cognitiveRouting: { effective: true },
58
+ toolExecution: { effective: true },
59
+ compaction: { effective: true },
60
+ });
61
+ export function freeflowSkillPath(skillName) {
62
+ return fileURLToPath(new URL(`../../../skills/${skillName}/SKILL.md`, import.meta.url));
63
+ }
64
+ export function freeflowCapabilitySkillPath(skillName) {
65
+ return fileURLToPath(new URL(`../../../capabilities/${skillName}/SKILL.md`, import.meta.url));
66
+ }
67
+ export function freeflowModelSkillPaths(capabilityState = undefined) {
68
+ const paths = FREEFLOW_MODEL_SKILL_NAMES.map((skillName) => freeflowSkillPath(skillName));
69
+ if (capabilityState?.cognitiveRouting?.effective === true)
70
+ paths.push(freeflowCapabilitySkillPath("cognitive-routing"));
71
+ if (capabilityState?.compaction?.effective === true) paths.push(freeflowCapabilitySkillPath("compaction"));
72
+ return paths;
73
+ }
74
+ export function skillPrompt(skill, args) {
75
+ const trimmed = args?.trim();
76
+ return trimmed ? `/skill:${skill}\n\n${trimmed}` : `/skill:${skill}`;
77
+ }