@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
@@ -1,306 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { mkdtemp, rm, readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { SessionManager } from "@earendil-works/pi-coding-agent";
7
- import { AstraAdapter, requestKey } from "../../dist/provider-support/astra/adapter.js";
8
- import { ENTRY_TYPE, assemble } from "../../dist/provider-support/astra/history.js";
9
-
10
- const model = {
11
- id: "gpt-6-astra",
12
- provider: "openai-codex",
13
- api: "openai-codex-responses",
14
- baseUrl: "https://chatgpt.com/backend-api",
15
- };
16
- const u = (text) => ({ role: "user", content: text });
17
- const a = (text) => ({ role: "assistant", content: text });
18
- const request = (input, effort = "low") => ({
19
- model: model.id,
20
- store: false,
21
- input,
22
- instructions: "stable",
23
- reasoning: { effort, summary: "auto" },
24
- prompt_cache_key: "unchanged-key",
25
- tools: [],
26
- });
27
- function fixture(manager = SessionManager.inMemory("/tmp/astra-fixture")) {
28
- const status = [];
29
- const pi = { appendEntry: (type, data) => manager.appendCustomEntry(type, data) };
30
- const ctx = { model, sessionManager: manager, ui: { setStatus: (...args) => status.push(args) } };
31
- return { pi, ctx, manager, status, adapter: new AstraAdapter(pi) };
32
- }
33
- const updates = (p) =>
34
- p.input.flatMap((item, index) =>
35
- item.type === "configuration_update" ? [{ index, effort: item.reasoning.effort }] : [],
36
- );
37
-
38
- test("native ancestry records replay Low High Low with original anchors and unchanged selected effort", async () => {
39
- const f = fixture(),
40
- h = [u("one")];
41
- await f.adapter.adapt(request(h), f.ctx);
42
- h.push(a("one"), u("two"));
43
- const high = await f.adapter.adapt(request(h, "high"), f.ctx);
44
- assert.equal(high.reasoning.effort, "low");
45
- assert.deepEqual(updates(high), [{ index: 2, effort: "high" }]);
46
- h.push(a("two"), u("three"));
47
- const low = await new AstraAdapter(f.pi).adapt(request(h), f.ctx);
48
- assert.deepEqual(low.input.slice(0, high.input.length), high.input);
49
- assert.deepEqual(updates(low), [
50
- { index: 2, effort: "high" },
51
- { index: 5, effort: "low" },
52
- ]);
53
- const count = f.manager.getEntries().length;
54
- assert.deepEqual(await f.adapter.adapt(request(h), f.ctx), low);
55
- assert.equal(f.manager.getEntries().length, count, "retry writes no duplicate record");
56
- assert.equal(low.prompt_cache_key, "unchanged-key");
57
- assert.ok(f.manager.getEntries().every((e) => !JSON.stringify(e.data).includes('"content"')));
58
- });
59
-
60
- test("first High is baseline; tool-loop switch occurs after completed results, not before an old user", async () => {
61
- const f = fixture(),
62
- h = [u("one")];
63
- await f.adapter.adapt(request(h, "high"), f.ctx);
64
- h.push(
65
- { type: "reasoning", encrypted_content: "opaque-signed-value", summary: [] },
66
- { type: "function_call", call_id: "call_1", name: "read", arguments: "{}" },
67
- { type: "function_call_output", call_id: "call_1", output: "body" },
68
- );
69
- const p = await f.adapter.adapt(request(h, "low"), f.ctx);
70
- assert.equal(p.reasoning.effort, "high");
71
- assert.deepEqual(updates(p), [{ index: 4, effort: "low" }]);
72
- assert.deepEqual(p.input.slice(0, 4), h);
73
- });
74
-
75
- test("changed/omitted/expanded input safely rebases; restored compatible branch recovers its own updates", async () => {
76
- const f = fixture();
77
- const h = [u("source")];
78
- await f.adapter.adapt(request(h), f.ctx);
79
- h.push(a("one"), u("two"));
80
- const high = await f.adapter.adapt(request(h, "high"), f.ctx);
81
- const changed = [u("different")];
82
- const p = await f.adapter.adapt(request(changed, "high"), f.ctx);
83
- assert.equal(p.reasoning.effort, "low");
84
- assert.deepEqual(updates(p), [{ index: 0, effort: "high" }]);
85
- const restored = await f.adapter.adapt(request([...h, a("two"), u("three")], "high"), f.ctx);
86
- assert.deepEqual(restored.input.slice(0, high.input.length), high.input);
87
- });
88
-
89
- test("same payload effort toggles never manufacture adjacent updates; fallback retains desired effort", async () => {
90
- const f = fixture(),
91
- h = [u("one")];
92
- await f.adapter.adapt(request(h), f.ctx);
93
- const high = await f.adapter.adapt(request(h, "high"), f.ctx);
94
- assert.deepEqual(updates(high), [{ index: 1, effort: "high" }]);
95
- const original = request(h, "low");
96
- assert.equal(await f.adapter.adapt(original, f.ctx), original);
97
- assert.equal(original.reasoning.effort, "low");
98
- assert.deepEqual(updates(original), []);
99
- });
100
-
101
- test("unsupported requests and existing native updates pass through without persistence", async () => {
102
- const f = fixture();
103
- for (const change of [
104
- (p) => (p.model = "gpt-5.6-luna"),
105
- (p) => (p.previous_response_id = "resp_x"),
106
- (p) => (p.conversation = "c"),
107
- (p) => (p.context_management = []),
108
- (p) => (p.truncation = "auto"),
109
- (p) => (p.reasoning.mode = "pro"),
110
- (p) => p.input.push({ type: "configuration_update", reasoning: { effort: "high" } }),
111
- (p) => (p.reasoning.effort = "off"),
112
- (p) => (p.store = true),
113
- ]) {
114
- const p = request([u("one")]);
115
- change(p);
116
- assert.equal(await f.adapter.adapt(p, f.ctx), p);
117
- }
118
- assert.equal(requestKey(request([]), { ...model, provider: "proxy" }), undefined);
119
- assert.equal(requestKey(request([]), { ...model, baseUrl: "https://example.com" }), undefined);
120
- assert.ok(
121
- requestKey(request([]), {
122
- ...model,
123
- provider: "openai",
124
- api: "openai-responses",
125
- baseUrl: "https://api.openai.com/v1",
126
- }),
127
- );
128
- assert.equal(f.manager.getEntries().length, 0);
129
- });
130
-
131
- test("append failure before or after mutation keeps original request and latches fallback", async () => {
132
- for (const after of [false, true]) {
133
- const f = fixture();
134
- await f.adapter.adapt(request([u("one")]), f.ctx);
135
- const real = f.pi.appendEntry;
136
- f.pi.appendEntry = (...args) => {
137
- if (after) real(...args);
138
- throw Error("disk fault");
139
- };
140
- const original = request([u("one"), a("one"), u("two")], "high");
141
- assert.equal(await f.adapter.adapt(original, f.ctx), original);
142
- f.pi.appendEntry = real;
143
- assert.equal(await f.adapter.adapt(original, f.ctx), original);
144
- assert.ok(f.status.some((x) => x[1]?.includes("native effort")));
145
- }
146
- });
147
-
148
- test("corrupt effective-effort metadata cannot silently pin the wrong effort", async () => {
149
- const f = fixture();
150
- await f.adapter.adapt(request([u("one")]), f.ctx);
151
- f.manager.getEntries().find((e) => e.customType === ENTRY_TYPE).data.effective = "high";
152
- const original = request([u("one"), a("one"), u("two")], "high");
153
- assert.equal(await f.adapter.adapt(original, f.ctx), original);
154
- });
155
-
156
- test("selected ancestry, new sessions, model changes and compaction isolate effort histories", async () => {
157
- const f = fixture();
158
- f.manager.appendModelChange(model.provider, model.id);
159
- await f.adapter.adapt(request([u("one")]), f.ctx);
160
- const baseLeaf = f.manager.getLeafId();
161
- await f.adapter.adapt(request([u("one"), a("one"), u("two")], "high"), f.ctx);
162
- f.manager.branch(baseLeaf);
163
- const branch = await f.adapter.adapt(request([u("one"), a("other"), u("sibling")], "medium"), f.ctx);
164
- assert.deepEqual(updates(branch), [{ index: 2, effort: "medium" }]);
165
- f.manager.appendModelChange("openai-codex", "gpt-5.6-luna");
166
- f.manager.appendModelChange(model.provider, model.id);
167
- const reset = await f.adapter.adapt(request([u("new")], "high"), f.ctx);
168
- assert.equal(reset.reasoning.effort, "low");
169
- assert.deepEqual(updates(reset), [{ index: 0, effort: "high" }]);
170
- assert.deepEqual(
171
- reset.input.filter((x) => x.type !== "configuration_update"),
172
- [u("new")],
173
- );
174
- f.adapter.setCompacting(true);
175
- const summary = request([u("summary")], "low");
176
- assert.equal(await f.adapter.adapt(summary, f.ctx), summary);
177
- f.adapter.setCompacting(false);
178
- const other = fixture();
179
- assert.equal((await f.adapter.adapt(request([u("new")], "medium"), other.ctx)).reasoning.effort, "medium");
180
- });
181
-
182
- test("persisted labeled fork and reload preserve an anchored effort transition", async () => {
183
- const root = await mkdtemp(join(tmpdir(), "astra-persist-"));
184
- try {
185
- const manager = SessionManager.create(root, root),
186
- f = fixture(manager);
187
- manager.appendMessage({ role: "user", content: "one", timestamp: 1 });
188
- await f.adapter.adapt(request([u("one")]), f.ctx);
189
- manager.appendMessage({
190
- role: "assistant",
191
- content: [{ type: "text", text: "one" }],
192
- api: model.api,
193
- provider: model.provider,
194
- model: model.id,
195
- usage: {
196
- input: 1,
197
- output: 1,
198
- cacheRead: 0,
199
- cacheWrite: 0,
200
- totalTokens: 2,
201
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
202
- },
203
- stopReason: "stop",
204
- timestamp: 2,
205
- });
206
- manager.appendLabelChange(manager.getLeafId(), "before effort change");
207
- const h = [u("one"), a("one"), u("two")];
208
- const high = await f.adapter.adapt(request(h, "high"), f.ctx);
209
- assert.ok((await readFile(manager.getSessionFile(), "utf8")).includes(ENTRY_TYPE));
210
- const forkFile = manager.createBranchedSession(manager.getLeafId());
211
- assert.ok(forkFile, "native fork file created");
212
- const reloaded = fixture(SessionManager.open(forkFile));
213
- const p = await reloaded.adapter.adapt(request([...h, a("two"), u("three")], "low"), reloaded.ctx);
214
- assert.deepEqual(p.input.slice(0, high.input.length), high.input);
215
- assert.equal(p.reasoning.effort, "low");
216
- } finally {
217
- await rm(root, { recursive: true, force: true });
218
- }
219
- });
220
-
221
- test("queued request from a replaced session passes through without a native append", async () => {
222
- const f = fixture(),
223
- p = request([u("old")], "high");
224
- const pending = f.adapter.adapt(p, f.ctx);
225
- f.adapter.reset();
226
- assert.equal(await pending, p);
227
- assert.equal(f.manager.getEntries().length, 0);
228
- });
229
-
230
- for (const returnEffort of ["high", "low"]) {
231
- test(`model round trip preserves original Low anchor through ${returnEffort} return and reload`, async () => {
232
- const f = fixture();
233
- f.manager.appendModelChange(model.provider, model.id);
234
- const h = [u("start")];
235
- await f.adapter.adapt(request(h, "high"), f.ctx);
236
- h.push(a("first"), u("low"));
237
- const low = await f.adapter.adapt(request(h), f.ctx);
238
- f.manager.appendModelChange(model.provider, "gpt-5.6-luna");
239
- const luna = request(h);
240
- luna.model = "gpt-5.6-luna";
241
- assert.equal(await f.adapter.adapt(luna, { ...f.ctx, model: { ...model, id: luna.model } }), luna);
242
- f.manager.appendModelChange(model.provider, model.id);
243
- h.push(a("second"), u("return"));
244
- const resumed = await new AstraAdapter(f.pi).adapt(request(h, returnEffort), f.ctx);
245
- assert.equal(resumed.reasoning.effort, "high");
246
- assert.deepEqual(resumed.input.slice(0, low.input.length), low.input);
247
- h.push(a("third"), u("low again"));
248
- const again = await f.adapter.adapt(request(h), f.ctx);
249
- assert.deepEqual(again.input.slice(0, resumed.input.length), resumed.input);
250
- assert.equal(updates(again)[0].index, 2);
251
- });
252
- }
253
-
254
- test("legacy model-generation records survive a round trip without rewriting persisted metadata", async () => {
255
- const root = await mkdtemp(join(tmpdir(), "astra-legacy-"));
256
- try {
257
- const f = fixture(SessionManager.create(root, root));
258
- f.manager.appendMessage({ role: "user", content: "legacy", timestamp: 1 });
259
- f.manager.appendMessage({
260
- role: "assistant",
261
- content: [{ type: "text", text: "legacy" }],
262
- api: model.api,
263
- provider: model.provider,
264
- model: model.id,
265
- usage: {
266
- input: 1,
267
- output: 1,
268
- cacheRead: 0,
269
- cacheWrite: 0,
270
- totalTokens: 2,
271
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
272
- },
273
- stopReason: "stop",
274
- timestamp: 2,
275
- });
276
- const oldGeneration = f.manager.appendModelChange(model.provider, model.id);
277
- const key = requestKey(request([]), model);
278
- const h = [u("legacy")];
279
- const first = assemble(request(h, "high"), key, oldGeneration, f.manager.getLeafId(), []);
280
- f.manager.appendCustomEntry(ENTRY_TYPE, first.record);
281
- h.push(a("one"), u("two"));
282
- const second = assemble(request(h), key, oldGeneration, f.manager.getLeafId(), [first.record]);
283
- f.manager.appendCustomEntry(ENTRY_TYPE, second.record);
284
- const saved = JSON.stringify(f.manager.getEntries());
285
- f.manager.appendModelChange(model.provider, "gpt-5.6-luna");
286
- f.manager.appendModelChange(model.provider, model.id);
287
- const loaded = fixture(SessionManager.open(f.manager.getSessionFile()));
288
- const resumed = await loaded.adapter.adapt(request([...h, a("two"), u("three")]), loaded.ctx);
289
- assert.equal(resumed.reasoning.effort, "high");
290
- assert.deepEqual(resumed.input.slice(0, second.payload.input.length), second.payload.input);
291
- assert.equal(JSON.stringify(f.manager.getEntries().slice(0, JSON.parse(saved).length)), saved);
292
- const again = fixture(SessionManager.open(loaded.manager.getSessionFile()));
293
- const reloaded = await again.adapter.adapt(request([...h, a("two"), u("three"), a("three"), u("four")]), again.ctx);
294
- assert.deepEqual(reloaded.input.slice(0, resumed.input.length), resumed.input);
295
- assert.equal(JSON.stringify(again.manager.getEntries().slice(0, JSON.parse(saved).length)), saved);
296
- } finally {
297
- await rm(root, { recursive: true, force: true });
298
- }
299
- });
300
-
301
- test("session reset clears adapter status without changing the next requested effort", async () => {
302
- const f = fixture();
303
- await f.adapter.adapt(request([u("one")]), f.ctx);
304
- f.adapter.reset(f.ctx);
305
- assert.deepEqual(f.status.at(-1), ["freeflow-astra-effort", undefined]);
306
- });
@@ -1,92 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
- import { spawn } from "node:child_process";
8
- import { response } from "../fixtures/routing-native.js";
9
-
10
- test(
11
- "bundled Pi CLI loads Freeflow adapter and changes effort inside a native tool loop",
12
- { timeout: 30000 },
13
- async () => {
14
- const root = await mkdtemp(join(tmpdir(), "astra-cli-")),
15
- agentDir = join(root, "agent");
16
- try {
17
- await mkdir(agentDir);
18
- await writeFile(
19
- join(agentDir, "auth.json"),
20
- JSON.stringify({ openai: { type: "api_key", key: "fixture-not-real" } }),
21
- );
22
- await writeFile(
23
- join(agentDir, "settings.json"),
24
- JSON.stringify({
25
- defaultProvider: "openai",
26
- defaultModel: "gpt-6-astra",
27
- defaultThinkingLevel: "low",
28
- transport: "sse",
29
- compaction: { enabled: false },
30
- retry: { enabled: false, maxRetries: 0, provider: { maxRetries: 0 } },
31
- }),
32
- );
33
- const capture = join(root, "requests.json"),
34
- extension = join(root, "fixture.mjs");
35
- await writeFile(
36
- extension,
37
- `import {writeFileSync} from 'node:fs';\n${response.toString()}\nexport default function(pi){pi.registerTool({name:'probe_raise_effort',label:'Probe effort',description:'Fixture only',parameters:{type:'object',properties:{}},execute:async()=>{pi.setThinkingLevel('high');return {content:[{type:'text',text:'Changed desired effort.'}]};}});const bodies=[];globalThis.fetch=async(url,init)=>{if(String(url)!=='https://api.openai.com/v1/responses')throw Error('Unexpected network');const body=JSON.parse(String(init.body));bodies.push(body);writeFileSync(${JSON.stringify(capture)},JSON.stringify(bodies));if(bodies.length>2)throw Error('Unexpected extra request');return response(bodies.length,bodies.length===1?[{name:'probe_raise_effort',args:{}}]:[],'OK');};}`,
38
- );
39
- const cli = fileURLToPath(new URL("./bundle/cli.js", import.meta.resolve("@earendil-works/pi-coding-agent")));
40
- const entry = fileURLToPath(new URL("../../freeflow/index.js", import.meta.url));
41
- const child = spawn(
42
- process.execPath,
43
- [
44
- cli,
45
- "-p",
46
- "--mode",
47
- "json",
48
- "--no-extensions",
49
- "--no-skills",
50
- "--no-context-files",
51
- "--no-prompt-templates",
52
- "--no-themes",
53
- "-e",
54
- extension,
55
- "-e",
56
- entry,
57
- "Run the effort fixture.",
58
- ],
59
- {
60
- cwd: root,
61
- env: {
62
- PATH: process.env.PATH,
63
- PI_CODING_AGENT_DIR: agentDir,
64
- PI_OFFLINE: "1",
65
- PI_TELEMETRY: "0",
66
- PI_SKIP_VERSION_CHECK: "1",
67
- },
68
- stdio: ["ignore", "pipe", "pipe"],
69
- },
70
- );
71
- let output = "";
72
- child.stdout.on("data", (d) => (output += d));
73
- child.stderr.on("data", (d) => (output += d));
74
- const timer = setTimeout(() => child.kill("SIGKILL"), 20000);
75
- const code = await new Promise((resolve, reject) => {
76
- child.on("error", reject);
77
- child.on("close", resolve);
78
- });
79
- clearTimeout(timer);
80
- assert.equal(code, 0, output.slice(-4000));
81
- const bodies = JSON.parse(await readFile(capture, "utf8"));
82
- assert.equal(bodies.length, 2);
83
- assert.equal(bodies[1].reasoning.effort, "low");
84
- const updates = bodies[1].input.filter((i) => i.type === "configuration_update");
85
- assert.deepEqual(updates, [{ type: "configuration_update", reasoning: { effort: "high" } }]);
86
- assert.ok(bodies[1].input.some((i) => i.type === "function_call_output"));
87
- assert.match(output, /OK/);
88
- } finally {
89
- await rm(root, { recursive: true, force: true });
90
- }
91
- },
92
- );
@@ -1,183 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { mkdtemp, mkdir, rm, readFile } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
- import {
7
- createAgentSession,
8
- DefaultResourceLoader,
9
- ModelRuntime,
10
- SessionManager,
11
- SettingsManager,
12
- } from "@earendil-works/pi-coding-agent";
13
- import freeflow from "../../dist/index.js";
14
- import { registerProviderSupport } from "../../dist/provider-support/index.js";
15
- import { ENTRY_TYPE } from "../../dist/provider-support/astra/history.js";
16
- import { response } from "../fixtures/routing-native.js";
17
-
18
- async function native(extension, run) {
19
- const root = await mkdtemp(join(tmpdir(), "astra-native-")),
20
- agentDir = join(root, "agent");
21
- await mkdir(agentDir);
22
- const previousFetch = globalThis.fetch,
23
- requests = [],
24
- errors = [];
25
- let session;
26
- globalThis.fetch = async (url, init) => {
27
- assert.match(String(url), /^https:\/\/api\.openai\.com\/v1\/responses$/);
28
- requests.push(JSON.parse(String(init.body)));
29
- assert.ok(requests.length <= 12);
30
- return response(requests.length, [], "OK");
31
- };
32
- try {
33
- const runtime = await ModelRuntime.create({
34
- authPath: join(agentDir, "auth.json"),
35
- modelsPath: null,
36
- modelsStorePath: join(agentDir, "models.json"),
37
- allowModelNetwork: false,
38
- });
39
- await runtime.setRuntimeApiKey("openai", "fixture-only-key");
40
- const settings = SettingsManager.inMemory({
41
- transport: "sse",
42
- compaction: { enabled: false, keepRecentTokens: 1, reserveTokens: 1 },
43
- retry: { enabled: false, maxRetries: 0, provider: { maxRetries: 0 } },
44
- });
45
- const manager = SessionManager.create(root, join(root, "sessions"));
46
- let cancelCompaction = false;
47
- const loader = new DefaultResourceLoader({
48
- cwd: root,
49
- agentDir,
50
- settingsManager: settings,
51
- noExtensions: true,
52
- noSkills: true,
53
- noPromptTemplates: true,
54
- noThemes: true,
55
- noContextFiles: true,
56
- systemPrompt: "Reply OK.",
57
- extensionFactories: [
58
- extension,
59
- (pi) =>
60
- pi.on("session_before_compact", () =>
61
- cancelCompaction
62
- ? { cancel: true }
63
- : {
64
- compaction: {
65
- summary: "Older material summarized.",
66
- firstKeptEntryId: manager.getLeafId(),
67
- tokensBefore: 100,
68
- },
69
- },
70
- ),
71
- ],
72
- });
73
- await loader.reload();
74
- assert.deepEqual(loader.getExtensions().errors, []);
75
- ({ session } = await createAgentSession({
76
- cwd: root,
77
- agentDir,
78
- modelRuntime: runtime,
79
- model: runtime.getModel("openai", "gpt-6-astra"),
80
- thinkingLevel: "low",
81
- settingsManager: settings,
82
- sessionManager: manager,
83
- resourceLoader: loader,
84
- tools: [],
85
- }));
86
- await session.bindExtensions({ mode: "print", onError: (e) => errors.push(e) });
87
- const prompt = async (text, effort) => {
88
- if (effort) session.setThinkingLevel(effort);
89
- await session.prompt(text);
90
- await session.waitForIdle();
91
- assert.equal(session.messages.at(-1)?.stopReason, "stop");
92
- assert.deepEqual(errors, []);
93
- return requests.at(-1);
94
- };
95
- await run({
96
- session,
97
- manager,
98
- requests,
99
- prompt,
100
- root,
101
- cancelCompaction: () => {
102
- cancelCompaction = true;
103
- },
104
- });
105
- assert.deepEqual(errors, []);
106
- } finally {
107
- session?.dispose();
108
- globalThis.fetch = previousFetch;
109
- await rm(root, { recursive: true, force: true });
110
- }
111
- }
112
- const updates = (p) =>
113
- p.input.flatMap((x, i) => (x.type === "configuration_update" ? [{ index: i, effort: x.reasoning.effort }] : []));
114
-
115
- test("real Pi request dispatch, reload, native tree navigation and compaction", { timeout: 30000 }, async () => {
116
- await native(registerProviderSupport, async ({ session, manager, requests, prompt }) => {
117
- await prompt("first", "low");
118
- const high = await prompt("second", "high");
119
- assert.equal(high.reasoning.effort, "low");
120
- assert.equal(session.thinkingLevel, "high");
121
- assert.deepEqual(updates(high), [{ index: 3, effort: "high" }]);
122
- await session.reload();
123
- const low = await prompt("third", "low");
124
- assert.deepEqual(low.input.slice(0, high.input.length), high.input);
125
- assert.deepEqual(updates(low), [
126
- { index: 3, effort: "high" },
127
- { index: 6, effort: "low" },
128
- ]);
129
- const branchPoint = manager
130
- .getBranch()
131
- .find((e) => e.message?.role === "user" && e.message.content?.some?.((b) => b.text === "second"));
132
- assert.ok(branchPoint);
133
- // Native tree navigation selects real ancestry; the discarded later Low update must not survive.
134
- await session.navigateTree(branchPoint.id, { summarize: false });
135
- const sibling = await prompt("sibling", "medium");
136
- assert.ok(updates(sibling).every((x) => x.effort !== "low"));
137
- await session.compact();
138
- const compacted = await prompt("after summary", "high");
139
- assert.equal(compacted.reasoning.effort, "high");
140
- assert.deepEqual(updates(compacted), []);
141
- const persisted = await readFile(manager.getSessionFile(), "utf8");
142
- assert.ok(persisted.includes(ENTRY_TYPE));
143
- assert.ok(!manager.getBranch().some((e) => e.customType === "freeflow-routing-v2"));
144
- assert.equal(requests.length, 5);
145
- });
146
- });
147
-
148
- test(
149
- "the Freeflow extension adapts Astra without repository activation or Cognitive Routing",
150
- { timeout: 30000 },
151
- async () => {
152
- await native(freeflow, async ({ session, manager, prompt }) => {
153
- await prompt("one", "low");
154
- const p = await prompt("two", "high");
155
- assert.equal(p.reasoning.effort, "low");
156
- assert.equal(session.thinkingLevel, "high");
157
- assert.ok(updates(p).some((x) => x.effort === "high"));
158
- assert.ok(manager.getBranch().some((e) => e.customType === ENTRY_TYPE));
159
- assert.ok(!manager.getBranch().some((e) => e.customType === "freeflow-routing-v2"));
160
- assert.ok(!(p.tools ?? []).some((t) => t.name.startsWith("freeflow_")));
161
- });
162
- },
163
- );
164
-
165
- test(
166
- "rapid native effort changes coalesce and cancelled compaction releases adaptation",
167
- { timeout: 30000 },
168
- async () => {
169
- await native(registerProviderSupport, async ({ session, prompt, cancelCompaction }) => {
170
- await prompt("first", "low");
171
- session.setThinkingLevel("high");
172
- session.setThinkingLevel("max");
173
- session.setThinkingLevel("medium");
174
- const medium = await prompt("second");
175
- assert.deepEqual(updates(medium), [{ index: 3, effort: "medium" }]);
176
- cancelCompaction();
177
- await assert.rejects(session.compact(), /cancelled/);
178
- const high = await prompt("third", "high");
179
- assert.equal(high.reasoning.effort, "low");
180
- assert.equal(updates(high).at(-1).effort, "high");
181
- });
182
- },
183
- );