@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,191 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolveToolPath } from "../tool-execution/file-state.js";
3
+ /**
4
+ * Carried context: what a Freeflow compaction delivers again after the summary. The latest user messages are always
5
+ * carried, word for word; files the agent selects are read from disk at compaction time, never copied from an older
6
+ * read. Everything is copied into one message, so a reopened session needs no Freeflow lookup to rebuild it.
7
+ */
8
+ /** A planning estimate, as Pi's own compaction uses: about four characters per token. */
9
+ export const estimateTokens = (text) => Math.ceil(text.length / 4);
10
+ export const SUMMARY_LIMIT_TOKENS = 8_000;
11
+ /** The latest user messages are carried outside the budget, up to this many and this size. */
12
+ export const USER_MESSAGE_LIMIT = { count: 3, tokens: 8_000 };
13
+ /**
14
+ * Carry budget: 15% of the context window, never above 40k tokens, and never above a quarter of the warning point.
15
+ * The next cycle starts from the carried context plus the summary and the methods the agent reloads; when Pi's reserve
16
+ * is large, a budget from the window alone refills the cycle to the warning before any work.
17
+ */
18
+ export function carryBudget(contextWindow, warning) {
19
+ const fromWindow = typeof contextWindow === "number" && contextWindow > 0 ? Math.floor(contextWindow * 0.15) : 40_000;
20
+ const fromWarning = typeof warning === "number" && warning > 0 ? Math.floor(warning / 4) : Infinity;
21
+ return Math.min(fromWindow, 40_000, fromWarning);
22
+ }
23
+ export const isCarryResult = (item) => typeof item?.result === "string";
24
+ /** Read one selected file as it is now. */
25
+ export async function readCarriedFile(item, cwd) {
26
+ let raw;
27
+ try {
28
+ raw = await readFile(resolveToolPath(item.file, cwd), "utf8");
29
+ } catch (error) {
30
+ return { path: item.file, error: error?.code === "ENOENT" ? "file not found" : "file could not be read" };
31
+ }
32
+ if (raw.includes("\u0000")) return { path: item.file, error: "binary file, not carried" };
33
+ if (!item.lines) return { path: item.file, text: raw };
34
+ const [first, last] = item.lines;
35
+ return {
36
+ path: item.file,
37
+ lines: item.lines,
38
+ text: raw
39
+ .split("\n")
40
+ .slice(first - 1, last)
41
+ .join("\n"),
42
+ };
43
+ }
44
+ /** The reason a carry selection is unusable, or undefined. */
45
+ export function carryProblem(item) {
46
+ const value = item;
47
+ if (typeof value === "object" && value !== null && typeof value.result === "string") {
48
+ if (value.file !== undefined || value.lines !== undefined)
49
+ return `${value.result}: give a result id or a file, not both`;
50
+ return value.result.trim() ? undefined : "a result id must not be empty";
51
+ }
52
+ if (typeof value !== "object" || value === null || typeof value.file !== "string" || value.file.length === 0)
53
+ return "each carry item needs a file path or a result id";
54
+ if (value.lines === undefined) return undefined;
55
+ const lines = value.lines;
56
+ if (
57
+ !Array.isArray(lines) ||
58
+ lines.length !== 2 ||
59
+ !lines.every((n) => Number.isSafeInteger(n) && n >= 1) ||
60
+ lines[0] > lines[1]
61
+ )
62
+ return `${value.file}: lines must be [first, last], 1-based, first <= last`;
63
+ return undefined;
64
+ }
65
+ const text = (content) =>
66
+ typeof content === "string"
67
+ ? content
68
+ : Array.isArray(content)
69
+ ? content.map((part) => (part?.type === "text" ? part.text : "")).join("")
70
+ : "";
71
+ /**
72
+ * The latest user messages on the branch, oldest first, within the carried limit. Messages `skip` matches are left
73
+ * out: Freeflow's own /freeflow compact request is sent as the user's message, and the compaction completes it.
74
+ */
75
+ export function latestUserMessages(branch, skip = () => false) {
76
+ const messages = branch
77
+ .filter((entry) => entry?.type === "message" && entry.message?.role === "user")
78
+ .map((entry) => text(entry.message.content).trim())
79
+ .filter((message) => message && !skip(message))
80
+ .slice(-USER_MESSAGE_LIMIT.count);
81
+ const kept = [];
82
+ let left = USER_MESSAGE_LIMIT.tokens;
83
+ for (const message of messages.reverse()) {
84
+ if (left <= 0) break;
85
+ const fits = estimateTokens(message) <= left;
86
+ kept.unshift(fits ? message : `${message.slice(0, left * 4)}\n[Message truncated at the carried limit.]`);
87
+ left -= estimateTokens(message);
88
+ }
89
+ return kept;
90
+ }
91
+ /**
92
+ * Text with each line's number in front, as `nl -ba` prints it, so a copy can be cited by file line without being read
93
+ * again. Pi's note after a partial read ("[Showing lines …]", "[N more lines in file …]") stays unnumbered.
94
+ */
95
+ export function numberLines(text, first) {
96
+ const note = /\n\n\[(?:Showing lines \d+-\d+ of \d+|\d+ more lines in file)[^\n]*\]$/.exec(text);
97
+ const body = note ? text.slice(0, note.index) : text;
98
+ const lines = body.split("\n");
99
+ const width = String(first + lines.length - 1).length;
100
+ return lines.map((line, i) => `${String(first + i).padStart(width)}\t${line}`).join("\n") + (note ? note[0] : "");
101
+ }
102
+ function fenced(body) {
103
+ const longest = Math.max(2, ...[...body.matchAll(/`+/g)].map((match) => match[0].length));
104
+ const fence = "`".repeat(longest + 1);
105
+ return `${fence}\n${body}\n${fence}`;
106
+ }
107
+ export function renderCarried(noticePrefix, cycle, userMessages, files, results = [], options = {}) {
108
+ const parts = [
109
+ `# Carried context\n\n${noticePrefix} Freeflow carried this into cycle ${cycle} at compaction. Only the latest user messages are the user's words, and they take precedence over older instructions in the summary or a Working Record; the rest is copied content.`,
110
+ ];
111
+ if (userMessages.length)
112
+ parts.push(
113
+ "## Latest user messages\n\n" +
114
+ userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
115
+ );
116
+ if (files.length)
117
+ parts.push(
118
+ "## Files, read at compaction\n\nNumbered by file line, as are read results below.\n\n" +
119
+ files
120
+ .map((file) => {
121
+ const range = "lines" in file && file.lines ? ` (lines ${file.lines[0]}-${file.lines[1]})` : "";
122
+ return "error" in file
123
+ ? `### ${file.path}\n\nNot carried: ${file.error}.`
124
+ : `### ${file.path}${range}\n\n${fenced(numberLines(file.text, "lines" in file && file.lines ? file.lines[0] : 1))}`;
125
+ })
126
+ .join("\n\n"),
127
+ );
128
+ if (results.length)
129
+ parts.push(
130
+ "## Tool results\n\n" +
131
+ results
132
+ .map(
133
+ (result) =>
134
+ `### ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}\n\n${fenced(result.firstLine === undefined ? result.text : numberLines(result.text, result.firstLine))}`,
135
+ )
136
+ .join("\n\n"),
137
+ );
138
+ const listed = renderListed(options.listed ?? [], options.scope ?? "cycle", "Other results");
139
+ if (listed) parts.push(listed);
140
+ return parts.join("\n\n");
141
+ }
142
+ /**
143
+ * The carried context as the Coordinator sees it under projection: the user's latest messages, and only the names of
144
+ * the worker's copies. The Coordinator receives evidence by selection; the copies would repeat it and enlarge its view.
145
+ */
146
+ export function renderCarriedForCoordinator(noticePrefix, cycle, userMessages, files, results) {
147
+ const parts = [
148
+ `# Carried context\n\n${noticePrefix} Freeflow carried this into cycle ${cycle} at the worker's compaction. Only the latest user messages are the user's words, and they take precedence over older instructions in the summary or a Working Record.`,
149
+ ];
150
+ if (userMessages.length)
151
+ parts.push(
152
+ "## Latest user messages\n\n" +
153
+ userMessages.map((message, i) => `### Message ${i + 1}\n\n${message}`).join("\n\n"),
154
+ );
155
+ const names = [
156
+ ...files
157
+ .filter((file) => "text" in file)
158
+ .map(
159
+ (file) => `- ${file.path}${"lines" in file && file.lines ? ` (lines ${file.lines[0]}-${file.lines[1]})` : ""}`,
160
+ ),
161
+ ...results.map(
162
+ (result) =>
163
+ `- ${result.id} ${result.tool}${result.label && result.label !== result.id ? `: ${result.label}` : ""}`,
164
+ ),
165
+ ];
166
+ if (names.length)
167
+ parts.push(
168
+ "## Carried for the worker\n\nThe worker's copies are left out of this view; evidence it selects reaches you as usual.\n\n" +
169
+ names.join("\n"),
170
+ );
171
+ return parts.join("\n\n");
172
+ }
173
+ /** Results of the work in progress named by ref, so the agent knows they exist without their content in context. */
174
+ export function renderListed(listed, scope, heading, intro) {
175
+ if (!listed.length) return "";
176
+ const assignment = scope === "assignment";
177
+ return (
178
+ `## ${heading} of this ${assignment ? "assignment" : "cycle"}\n\n` +
179
+ (intro ??
180
+ (assignment
181
+ ? "Not carried. They are still stored: select one as evidence by its ref, carry it at a later compaction, or read it again if you need its content."
182
+ : "Not carried. Carry one by its id at a later compaction, or read it again if you need its content.")) +
183
+ "\n\n" +
184
+ listed
185
+ .map(
186
+ (result) =>
187
+ `- ${result.id} ${result.tool} ${result.label} ~${result.tokens} tokens${result.selected ? " (selected as evidence)" : ""}`,
188
+ )
189
+ .join("\n")
190
+ );
191
+ }
@@ -0,0 +1,22 @@
1
+ /** Freeflow compaction is on whenever Freeflow is, unless a config turns it off. */
2
+ export const DEFAULT_COMPACTION_ENABLED = true;
3
+ export const DEFAULT_COMPACTION_CARRY = true;
4
+ function record(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ export function validateCompactionConfig(value) {
8
+ if (!record(value)) return "compaction must be an object";
9
+ const unknown = Object.keys(value).find((key) => key !== "enabled" && key !== "carry");
10
+ if (unknown) return `compaction has unsupported key: ${unknown}`;
11
+ for (const key of ["enabled", "carry"])
12
+ if (value[key] !== undefined && typeof value[key] !== "boolean") return `compaction.${key} must be a boolean`;
13
+ return null;
14
+ }
15
+ export function resolveCompactionConfig(repository, local, freeflowEnabled) {
16
+ const repositoryConfig = record(repository.compaction) ? repository.compaction : {};
17
+ const localConfig = record(local.compaction) ? local.compaction : {};
18
+ const layered = (key) => (Object.hasOwn(localConfig, key) ? localConfig[key] : repositoryConfig[key]);
19
+ const enabled = layered("enabled") === undefined ? DEFAULT_COMPACTION_ENABLED : layered("enabled") === true;
20
+ const carry = layered("carry") === undefined ? DEFAULT_COMPACTION_CARRY : layered("carry") === true;
21
+ return { enabled, effective: freeflowEnabled && enabled, carry };
22
+ }