@hassangameryt/freeflow 0.7.2 → 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 (267) 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 +151 -136
  7. package/capabilities/cognitive-routing/SKILL.md +110 -96
  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 +40 -21
  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/cognitive-routing/event-store.js +280 -0
  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/projection.js +503 -0
  29. package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
  30. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +31 -10
  31. package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
  32. package/pi-extension/dist/cognitive-routing/schemas.js +209 -0
  33. package/pi-extension/dist/cognitive-routing/session.js +221 -0
  34. package/pi-extension/dist/cognitive-routing/sources.js +291 -0
  35. package/pi-extension/dist/cognitive-routing/state.js +487 -0
  36. package/pi-extension/dist/cognitive-routing/status.js +80 -0
  37. package/pi-extension/dist/cognitive-routing/tools.js +60 -0
  38. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +10 -12
  39. package/pi-extension/dist/compaction/carry.js +191 -0
  40. package/pi-extension/dist/compaction/config.js +22 -0
  41. package/pi-extension/dist/compaction/controller.js +471 -0
  42. package/pi-extension/dist/compaction/harness.js +77 -0
  43. package/pi-extension/dist/compaction/results.js +158 -0
  44. package/pi-extension/dist/compaction/thresholds.js +35 -0
  45. package/pi-extension/dist/compaction/tool.js +57 -0
  46. package/pi-extension/dist/host/branch.js +13 -0
  47. package/pi-extension/dist/host/canonical.js +12 -0
  48. package/pi-extension/dist/host/catalog.js +77 -0
  49. package/pi-extension/dist/host/config.js +374 -0
  50. package/pi-extension/dist/host/projection-tags.js +60 -0
  51. package/pi-extension/dist/host/prompts.js +82 -0
  52. package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
  53. package/pi-extension/dist/host/request-history.js +298 -0
  54. package/pi-extension/dist/host/runtime-state.js +189 -0
  55. package/pi-extension/dist/host/settings/freeflow-command.js +1570 -0
  56. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
  57. package/pi-extension/dist/host/staging.js +28 -0
  58. package/pi-extension/dist/host/status.js +101 -0
  59. package/pi-extension/dist/index.js +441 -170
  60. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  61. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  62. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  63. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  64. package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
  65. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  66. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  67. package/pi-extension/dist/provider-support/effort.js +13 -0
  68. package/pi-extension/dist/provider-support/index.js +45 -0
  69. package/pi-extension/dist/provider-support/openai/adapter.js +127 -0
  70. package/pi-extension/dist/provider-support/openai/history.js +127 -0
  71. package/pi-extension/dist/provider-support/openai/index.js +15 -0
  72. package/pi-extension/dist/provider-support/openai/session-state.js +105 -0
  73. package/pi-extension/dist/provider-support/routes.js +17 -0
  74. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  76. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  77. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  78. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  79. package/pi-extension/dist/tool-execution/background.js +238 -0
  80. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  81. package/pi-extension/dist/tool-execution/config.js +22 -0
  82. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  83. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  84. package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
  85. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  86. package/pi-extension/dist/tool-execution/tools.js +16 -0
  87. package/pi-extension/freeflow/README.md +9 -0
  88. package/plugin.json +1 -1
  89. package/runtime/prompts/cognitive-routing.md +4 -4
  90. package/runtime/prompts/core.md +22 -75
  91. package/runtime/prompts/interaction-contract.md +15 -8
  92. package/runtime/prompts/tool-execution.md +38 -0
  93. package/runtime/prompts/working-method.md +108 -0
  94. package/skills/action-selection/SKILL.md +39 -47
  95. package/skills/action-selection/references/trajectory-stalls.md +2 -0
  96. package/skills/bypass/SKILL.md +2 -2
  97. package/skills/commit-work/SKILL.md +31 -83
  98. package/skills/commit-work/references/git-state-edges.md +1 -1
  99. package/skills/commit-work/references/simple-push.md +2 -0
  100. package/skills/commit-work/references/staging-decisions.md +1 -1
  101. package/skills/decision-gate/SKILL.md +31 -50
  102. package/skills/design-for-depth/SKILL.md +44 -53
  103. package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
  104. package/skills/design-for-depth/references/implementation-decisions.md +4 -4
  105. package/skills/design-for-depth/references/interface-design-loop.md +5 -36
  106. package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
  107. package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
  108. package/skills/diagnose-failure/SKILL.md +39 -24
  109. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
  110. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
  111. package/skills/discuss/SKILL.md +37 -13
  112. package/skills/discuss/references/discussion-continuity.md +7 -27
  113. package/skills/evaluate-skill/SKILL.md +49 -80
  114. package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
  115. package/skills/evaluate-skill/references/evaluation-design.md +14 -14
  116. package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
  117. package/skills/evaluate-skill/references/review-and-revision.md +3 -1
  118. package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
  119. package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
  120. package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
  121. package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
  122. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
  123. package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
  124. package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
  125. package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
  126. package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
  127. package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
  128. package/skills/execute-work/SKILL.md +41 -9
  129. package/skills/execute-work/references/code-practices.md +2 -0
  130. package/skills/execute-work/references/execute-work-edges.md +11 -1
  131. package/skills/handoff/SKILL.md +1 -1
  132. package/skills/handoff/references/handoff-templates.md +1 -1
  133. package/skills/review-artifact/SKILL.md +42 -41
  134. package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
  135. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  136. package/skills/review-work/SKILL.md +38 -32
  137. package/skills/review-work/references/adjudicate-work-review.md +1 -1
  138. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  139. package/skills/review-work/references/security-risk-lens.md +2 -4
  140. package/skills/setup-freeflow/SKILL.md +3 -1
  141. package/skills/setup-freeflow/references/activation-contract.md +7 -13
  142. package/skills/setup-freeflow/references/host-setup.md +3 -3
  143. package/skills/track-work/SKILL.md +113 -161
  144. package/skills/track-work/references/working-record-format.md +189 -350
  145. package/skills/track-work/scripts/lib/document.mjs +62 -6
  146. package/skills/track-work/scripts/lib/format.mjs +16 -1
  147. package/skills/track-work/scripts/lib/store.mjs +9 -8
  148. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  149. package/skills/track-work/scripts/working-record.mjs +34 -12
  150. package/skills/verify-work/SKILL.md +29 -20
  151. package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
  152. package/skills/verify-work/references/integration-evidence.md +2 -0
  153. package/skills/verify-work/references/performance-evidence.md +3 -1
  154. package/skills/verify-work/references/test-design.md +3 -1
  155. package/skills/workflow/SKILL.md +65 -21
  156. package/skills/workflow/references/domain-skill-composition.md +5 -3
  157. package/skills/write-docs/SKILL.md +83 -0
  158. package/skills/write-docs/references/documentation-shapes.md +119 -0
  159. package/skills/write-docs/scripts/docs-check.mjs +139 -0
  160. package/skills/write-plan/SKILL.md +40 -40
  161. package/skills/write-plan/references/plan-shapes.md +1 -1
  162. package/skills/write-skill/SKILL.md +107 -147
  163. package/skills/write-skill/references/activation-boundaries.md +2 -0
  164. package/skills/write-skill/references/agent-first-instructions.md +22 -10
  165. package/skills/write-skill/references/progressive-disclosure.md +4 -2
  166. package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
  167. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  168. package/skills/write-skill/scripts/skill-author.mjs +18 -2
  169. package/skills/write-spec/SKILL.md +34 -41
  170. package/skills/write-spec/references/artifact-standards.md +1 -1
  171. package/skills/write-spec/references/decision-records.md +2 -0
  172. package/skills/write-spec/references/spec-shapes.md +1 -1
  173. package/capabilities/context-virtualization/SKILL.md +0 -103
  174. package/capabilities/conversation-history/SKILL.md +0 -53
  175. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  176. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  177. package/pi-extension/dist/cognitive-routing-v2/projection.js +0 -321
  178. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -1585
  179. package/pi-extension/dist/cognitive-routing-v2/schemas.js +0 -107
  180. package/pi-extension/dist/cognitive-routing-v2/state.js +0 -612
  181. package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -91
  182. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  183. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  184. package/pi-extension/dist/context-virtualization/runtime.js +0 -356
  185. package/pi-extension/dist/context-virtualization/state.js +0 -57
  186. package/pi-extension/dist/context-virtualization/types.js +0 -10
  187. package/pi-extension/dist/conversation-history/passages.js +0 -66
  188. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  189. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  190. package/pi-extension/dist/conversation-history/search.js +0 -56
  191. package/pi-extension/dist/conversation-history/types.js +0 -1
  192. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  193. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  194. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  195. package/pi-extension/dist/freeflow-context/tool.js +0 -313
  196. package/pi-extension/dist/freeflow-context/types.js +0 -12
  197. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  198. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  199. package/pi-extension/dist/runtime/runtime-context.js +0 -735
  200. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  201. package/pi-extension/dist/session-sources/events.js +0 -195
  202. package/pi-extension/dist/session-sources/sources.js +0 -233
  203. package/pi-extension/dist/settings/settings-ui.js +0 -1160
  204. package/pi-extension/dist/ui/text-component.js +0 -57
  205. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  206. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -143
  207. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -365
  208. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -256
  209. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -1634
  210. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -109
  211. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -622
  212. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -93
  213. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -222
  214. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  215. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  216. package/pi-extension/src/context-virtualization/runtime.ts +0 -406
  217. package/pi-extension/src/context-virtualization/state.ts +0 -79
  218. package/pi-extension/src/context-virtualization/types.ts +0 -92
  219. package/pi-extension/src/conversation-history/passages.ts +0 -81
  220. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  221. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  222. package/pi-extension/src/conversation-history/search.ts +0 -79
  223. package/pi-extension/src/conversation-history/types.ts +0 -31
  224. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  225. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  226. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  227. package/pi-extension/src/freeflow-context/tool.ts +0 -365
  228. package/pi-extension/src/freeflow-context/types.ts +0 -70
  229. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  230. package/pi-extension/src/index.ts +0 -368
  231. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  232. package/pi-extension/src/runtime/runtime-context.ts +0 -838
  233. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  234. package/pi-extension/src/session-sources/events.ts +0 -208
  235. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  236. package/pi-extension/src/session-sources/sources.ts +0 -263
  237. package/pi-extension/src/settings/settings-tui.ts +0 -501
  238. package/pi-extension/src/settings/settings-ui.ts +0 -1422
  239. package/pi-extension/src/ui/text-component.ts +0 -66
  240. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -484
  241. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  242. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -441
  243. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  244. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -245
  245. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  246. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -236
  247. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -230
  248. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -153
  249. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  250. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -275
  251. package/pi-extension/tests/context-virtualization/extension.test.js +0 -765
  252. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  253. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -216
  254. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  255. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  256. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  257. package/pi-extension/tests/fixtures/routing-native.js +0 -188
  258. package/pi-extension/tests/integration/mode-free-core.test.js +0 -219
  259. package/pi-extension/tests/integration/pi-extension.test.js +0 -968
  260. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -431
  261. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  262. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  263. package/pi-extension/tsconfig.json +0 -16
  264. package/runtime/prompts/context-virtualization.md +0 -3
  265. package/runtime/prompts/conversation-history.md +0 -3
  266. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  267. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.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
+ }