@hassangameryt/freeflow 0.7.1 → 0.7.3

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 (147) 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/README.md +153 -128
  6. package/capabilities/cognitive-routing/SKILL.md +126 -274
  7. package/command-surface.json +7 -1
  8. package/gemini-extension.json +1 -1
  9. package/package.json +4 -4
  10. package/pi-extension/dist/cognitive-routing-v2/budget.js +42 -0
  11. package/pi-extension/dist/cognitive-routing-v2/config.js +136 -0
  12. package/pi-extension/dist/cognitive-routing-v2/projection.js +330 -0
  13. package/pi-extension/dist/cognitive-routing-v2/render.js +252 -0
  14. package/pi-extension/dist/cognitive-routing-v2/runtime.js +2181 -0
  15. package/pi-extension/dist/cognitive-routing-v2/schemas.js +173 -0
  16. package/pi-extension/dist/cognitive-routing-v2/state.js +828 -0
  17. package/pi-extension/dist/cognitive-routing-v2/tools.js +60 -0
  18. package/pi-extension/dist/cognitive-routing-v2/types.js +48 -0
  19. package/pi-extension/dist/context-virtualization/runtime.js +29 -7
  20. package/pi-extension/dist/freeflow-context/tool.js +12 -6
  21. package/pi-extension/dist/index.js +302 -626
  22. package/pi-extension/dist/provider-support/astra/adapter.js +110 -0
  23. package/pi-extension/dist/provider-support/astra/history.js +125 -0
  24. package/pi-extension/dist/provider-support/astra/index.js +11 -0
  25. package/pi-extension/dist/provider-support/astra/session-state.js +114 -0
  26. package/pi-extension/dist/provider-support/index.js +4 -0
  27. package/pi-extension/dist/runtime/request-history.js +216 -0
  28. package/pi-extension/dist/runtime/runtime-context.js +85 -16
  29. package/pi-extension/dist/session-sources/events.js +217 -0
  30. package/pi-extension/dist/session-sources/provenance.js +32 -0
  31. package/pi-extension/dist/session-sources/read-only-session.js +148 -0
  32. package/pi-extension/dist/session-sources/sources.js +277 -0
  33. package/pi-extension/dist/settings/settings-ui.js +274 -147
  34. package/pi-extension/src/cognitive-routing-v2/budget.ts +42 -0
  35. package/pi-extension/src/cognitive-routing-v2/config.ts +173 -0
  36. package/pi-extension/src/cognitive-routing-v2/projection.ts +375 -0
  37. package/pi-extension/src/cognitive-routing-v2/render.ts +277 -0
  38. package/pi-extension/src/cognitive-routing-v2/runtime.ts +2241 -0
  39. package/pi-extension/src/cognitive-routing-v2/schemas.ts +180 -0
  40. package/pi-extension/src/cognitive-routing-v2/state.ts +839 -0
  41. package/pi-extension/src/cognitive-routing-v2/tools.ts +62 -0
  42. package/pi-extension/src/cognitive-routing-v2/types.ts +254 -0
  43. package/pi-extension/src/context-virtualization/runtime.ts +30 -8
  44. package/pi-extension/src/freeflow-context/tool.ts +12 -6
  45. package/pi-extension/src/index.ts +308 -675
  46. package/pi-extension/src/provider-support/astra/adapter.ts +108 -0
  47. package/pi-extension/src/provider-support/astra/history.ts +145 -0
  48. package/pi-extension/src/provider-support/astra/index.ts +12 -0
  49. package/pi-extension/src/provider-support/astra/session-state.ts +113 -0
  50. package/pi-extension/src/provider-support/index.ts +5 -0
  51. package/pi-extension/src/runtime/request-history.ts +243 -0
  52. package/pi-extension/src/runtime/runtime-context.ts +100 -17
  53. package/pi-extension/src/session-sources/events.ts +230 -0
  54. package/pi-extension/src/session-sources/provenance.ts +39 -0
  55. package/pi-extension/src/session-sources/read-only-session.ts +170 -0
  56. package/pi-extension/src/session-sources/sources.ts +334 -0
  57. package/pi-extension/src/settings/settings-ui.ts +311 -158
  58. package/pi-extension/tests/cache-reuse/history.test.js +117 -0
  59. package/pi-extension/tests/cache-reuse/native.test.js +91 -0
  60. package/pi-extension/tests/cache-reuse/settings.test.js +211 -0
  61. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +577 -0
  62. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +183 -0
  63. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +557 -0
  64. package/pi-extension/tests/cognitive-routing-v2/native.test.js +474 -0
  65. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +87 -0
  66. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +300 -0
  67. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +241 -0
  68. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +1034 -0
  69. package/pi-extension/tests/cognitive-routing-v2/render.test.js +244 -0
  70. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +423 -0
  71. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +521 -0
  72. package/pi-extension/tests/cognitive-routing-v2/state.test.js +586 -0
  73. package/pi-extension/tests/context-virtualization/extension.test.js +33 -7
  74. package/pi-extension/tests/context-virtualization/runtime.test.js +17 -3
  75. package/pi-extension/tests/fixtures/routing-native.js +191 -0
  76. package/pi-extension/tests/integration/mode-free-core.test.js +6 -4
  77. package/pi-extension/tests/integration/pi-extension.test.js +655 -59
  78. package/pi-extension/tests/integration/prompt-architecture.test.js +127 -30
  79. package/pi-extension/tests/integration/unaffected-regressions.test.js +78 -11
  80. package/pi-extension/tests/provider-support/astra-boundaries.test.js +75 -0
  81. package/pi-extension/tests/provider-support/astra.test.js +306 -0
  82. package/pi-extension/tests/provider-support/cli.test.js +92 -0
  83. package/pi-extension/tests/provider-support/native.test.js +183 -0
  84. package/plugin.json +1 -1
  85. package/runtime/prompts/cognitive-routing.md +4 -5
  86. package/runtime/prompts/core.md +51 -45
  87. package/runtime/prompts/interaction-contract.md +5 -5
  88. package/skills/action-selection/SKILL.md +57 -97
  89. package/skills/design-for-depth/SKILL.md +58 -193
  90. package/skills/design-for-depth/references/implementation-decisions.md +76 -0
  91. package/skills/diagnose-failure/SKILL.md +64 -196
  92. package/skills/discuss/SKILL.md +64 -123
  93. package/skills/discuss/references/discussion-continuity.md +7 -3
  94. package/skills/execute-work/SKILL.md +69 -88
  95. package/skills/execute-work/references/execute-work-edges.md +9 -3
  96. package/skills/handoff/SKILL.md +44 -54
  97. package/skills/handoff/references/handoff-templates.md +15 -6
  98. package/skills/review-artifact/SKILL.md +80 -127
  99. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  100. package/skills/review-work/SKILL.md +65 -115
  101. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  102. package/skills/simplify-code/SKILL.md +47 -100
  103. package/skills/track-work/SKILL.md +128 -119
  104. package/skills/track-work/references/working-record-format.md +14 -2
  105. package/skills/verify-work/SKILL.md +58 -62
  106. package/skills/verify-work/references/integration-evidence.md +4 -2
  107. package/skills/verify-work/references/test-design.md +89 -0
  108. package/skills/workflow/SKILL.md +73 -119
  109. package/skills/write-plan/SKILL.md +46 -77
  110. package/skills/write-plan/references/plan-shapes.md +5 -5
  111. package/skills/write-spec/SKILL.md +51 -63
  112. package/skills/write-spec/references/artifact-standards.md +2 -2
  113. package/skills/write-spec/references/decision-records.md +3 -2
  114. package/skills/write-spec/references/spec-shapes.md +15 -0
  115. package/pi-extension/dist/cognitive-routing/commands.js +0 -96
  116. package/pi-extension/dist/cognitive-routing/config.js +0 -232
  117. package/pi-extension/dist/cognitive-routing/controller.js +0 -1292
  118. package/pi-extension/dist/cognitive-routing/history.js +0 -451
  119. package/pi-extension/dist/cognitive-routing/host.js +0 -188
  120. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +0 -414
  121. package/pi-extension/dist/cognitive-routing/pi-session-control.js +0 -320
  122. package/pi-extension/dist/cognitive-routing/runtime.js +0 -43
  123. package/pi-extension/dist/cognitive-routing/tool.js +0 -212
  124. package/pi-extension/dist/cognitive-routing/types.js +0 -6
  125. package/pi-extension/src/cognitive-routing/commands.ts +0 -129
  126. package/pi-extension/src/cognitive-routing/config.ts +0 -303
  127. package/pi-extension/src/cognitive-routing/controller.ts +0 -1628
  128. package/pi-extension/src/cognitive-routing/history.ts +0 -650
  129. package/pi-extension/src/cognitive-routing/host.ts +0 -266
  130. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +0 -538
  131. package/pi-extension/src/cognitive-routing/pi-session-control.ts +0 -444
  132. package/pi-extension/src/cognitive-routing/runtime.ts +0 -57
  133. package/pi-extension/src/cognitive-routing/tool.ts +0 -273
  134. package/pi-extension/src/cognitive-routing/types.ts +0 -89
  135. package/pi-extension/tests/cognitive-routing/commands.test.js +0 -134
  136. package/pi-extension/tests/cognitive-routing/controller.test.js +0 -1128
  137. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +0 -1142
  138. package/pi-extension/tests/cognitive-routing/history.test.js +0 -274
  139. package/pi-extension/tests/cognitive-routing/host-identity.test.js +0 -63
  140. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +0 -434
  141. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +0 -174
  142. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +0 -91
  143. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +0 -279
  144. package/pi-extension/tests/cognitive-routing/tool.test.js +0 -230
  145. package/skills/tdd/SKILL.md +0 -143
  146. package/skills/tdd/references/test-design.md +0 -96
  147. /package/pi-extension/tests/{cognitive-routing/host-fixture.js → fixtures/pi-host.js} +0 -0
@@ -208,9 +208,23 @@ test("mutation persistence failure returns an error without publishing projectio
208
208
  assert.match(next.messages[0].content.at(-1).text, /ctx:tool-1/);
209
209
  });
210
210
 
211
- test("disabled projection bypasses stored state and adds no marker", async () => {
211
+ test("disabled projection keeps original content and the stable reference marker", async () => {
212
212
  const { runtime } = createFixture();
213
213
  const result = await runtime.project([toolMessage()], false);
214
- assert.equal(result.changed, false);
215
- assert.deepEqual(result.messages[0].content, [{ type: "text", text: "secret raw output" }]);
214
+ assert.equal(result.changed, true);
215
+ assert.deepEqual(result.messages[0].content.slice(0, -1), [{ type: "text", text: "secret raw output" }]);
216
+ assert.match(result.messages[0].content.at(-1).text, /context-ref: ctx:tool-1/);
217
+ });
218
+
219
+ test("unavailable decoration leaves the already projected view intact", () => {
220
+ const { runtime } = createFixture();
221
+ runtime.setContext({
222
+ sessionManager: {
223
+ getBranch() {
224
+ throw Error("unavailable");
225
+ },
226
+ },
227
+ });
228
+ const current = [{ role: "custom", customType: "blocked", content: "Only this safe view" }];
229
+ assert.equal(runtime.decorate(current), current);
216
230
  });
@@ -0,0 +1,191 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ createAgentSession,
7
+ DefaultResourceLoader,
8
+ ModelRuntime,
9
+ SessionManager,
10
+ SettingsManager,
11
+ } from "@earendil-works/pi-coding-agent";
12
+ import freeflow from "../../dist/index.js";
13
+ import { replay } from "../../dist/cognitive-routing-v2/state.js";
14
+
15
+ export function response(n, calls = [], text = "fixture response") {
16
+ const output = [
17
+ {
18
+ type: "message",
19
+ id: `msg-${n}`,
20
+ role: "assistant",
21
+ status: "completed",
22
+ content: [{ type: "output_text", text, annotations: [] }],
23
+ },
24
+ ...calls.map((c, i) => ({
25
+ type: "function_call",
26
+ id: `fc_${n}_${i}`,
27
+ call_id: `call-${n}-${i}`,
28
+ name: c.name,
29
+ arguments: JSON.stringify(c.args),
30
+ })),
31
+ ];
32
+ const events = [{ type: "response.created", response: { id: `r-${n}`, status: "in_progress", output: [] } }];
33
+ output.forEach((item, i) => {
34
+ events.push({
35
+ type: "response.output_item.added",
36
+ output_index: i,
37
+ item: item.type === "message" ? { ...item, content: [] } : { ...item, arguments: "" },
38
+ });
39
+ if (item.type === "message")
40
+ events.push({ type: "response.output_text.delta", output_index: i, content_index: 0, delta: text });
41
+ else
42
+ events.push(
43
+ { type: "response.function_call_arguments.delta", output_index: i, delta: item.arguments },
44
+ { type: "response.function_call_arguments.done", output_index: i, arguments: item.arguments },
45
+ );
46
+ events.push({ type: "response.output_item.done", output_index: i, item });
47
+ });
48
+ events.push({
49
+ type: "response.completed",
50
+ response: {
51
+ id: `r-${n}`,
52
+ status: "completed",
53
+ output,
54
+ usage: { input_tokens: 10, output_tokens: 10, total_tokens: 20 },
55
+ },
56
+ });
57
+ return new Response(events.map((e) => `data: ${JSON.stringify(e)}\n\n`).join("") + "data: [DONE]\n\n", {
58
+ status: 200,
59
+ headers: { "content-type": "text/event-stream" },
60
+ });
61
+ }
62
+
63
+ export async function fixture(script, projection = true, after, withUI = true, options = {}) {
64
+ const root = await mkdtemp(join(tmpdir(), "freeflow-v2-native-"));
65
+ const cwd = join(root, "cwd"),
66
+ agentDir = join(root, "agent");
67
+ await mkdir(join(cwd, ".freeflow"), { recursive: true });
68
+ await mkdir(agentDir);
69
+ await writeFile(
70
+ join(cwd, ".freeflow/config.json"),
71
+ JSON.stringify({
72
+ cognitiveRouting: options.cognitiveRouting ?? {
73
+ enabled: true,
74
+ projection,
75
+ profiles: {
76
+ coordinator: { provider: "openai", model: "gpt-4o", thinking: "off" },
77
+ executor: { provider: "openai", model: "gpt-4.1-mini", thinking: "off" },
78
+ },
79
+ },
80
+ }),
81
+ );
82
+ await writeFile(join(cwd, "evidence.txt"), "EXACT_EVIDENCE_BODY_81");
83
+ await writeFile(join(cwd, "unselected.txt"), "UNSELECTED_PRIVATE_BODY_93");
84
+ await writeFile(join(cwd, "recovery.txt"), "FRESH_RECOVERY_BODY_57");
85
+ await writeFile(join(cwd, "@recovery.txt"), "LITERAL_AT_FILE_BODY_68");
86
+ await writeFile(
87
+ join(agentDir, "models.json"),
88
+ JSON.stringify({ providers: { openai: { baseUrl: "https://fixture.invalid/v1" } } }),
89
+ );
90
+ const priorFetch = globalThis.fetch,
91
+ offline = process.env.PI_OFFLINE;
92
+ process.env.PI_OFFLINE = "1";
93
+ const requests = [],
94
+ errors = [],
95
+ contexts = [],
96
+ transportFailures = [],
97
+ notices = [];
98
+ let session;
99
+ const manager = SessionManager.create(cwd, join(root, "sessions"));
100
+ globalThis.fetch = async (url, init) => {
101
+ assert.match(String(url), /^https:\/\/fixture\.invalid\//, "no unexpected network destination");
102
+ const body = JSON.parse(String(init.body));
103
+ requests.push(body);
104
+ assert.ok(requests.length <= (options.maxRequests ?? 15), "bounded fixture request count");
105
+ try {
106
+ const calls = await script(requests.length, body, manager, requests);
107
+ return options.response ? options.response(requests.length, calls) : response(requests.length, calls);
108
+ } catch (error) {
109
+ transportFailures.push(error);
110
+ throw error;
111
+ }
112
+ };
113
+ try {
114
+ const modelRuntime = await ModelRuntime.create({
115
+ authPath: join(agentDir, "auth.json"),
116
+ modelsPath: join(agentDir, "models.json"),
117
+ modelsStorePath: join(agentDir, "models-store.json"),
118
+ allowModelNetwork: false,
119
+ });
120
+ await modelRuntime.setRuntimeApiKey("openai", "fixture-not-a-real-key");
121
+ const settingsManager = SettingsManager.inMemory({
122
+ compaction: { enabled: false, keepRecentTokens: 1, reserveTokens: 1 },
123
+ retry: { enabled: false, maxRetries: 0, provider: { maxRetries: 0 } },
124
+ ...options.settings,
125
+ });
126
+ const loader = new DefaultResourceLoader({
127
+ cwd,
128
+ agentDir,
129
+ settingsManager,
130
+ noExtensions: true,
131
+ noSkills: true,
132
+ noPromptTemplates: true,
133
+ noThemes: true,
134
+ noContextFiles: true,
135
+ extensionFactories: [
136
+ freeflow,
137
+ ...(options.extensions ?? []),
138
+ (pi) => {
139
+ pi.on("context", (event) => {
140
+ contexts.push(structuredClone(event.messages));
141
+ });
142
+ pi.on("session_before_compact", () => ({
143
+ compaction: {
144
+ summary: "Fixture compacted prior work; evidence must come from canonical sources.",
145
+ firstKeptEntryId: manager.getLeafId(),
146
+ tokensBefore: 100,
147
+ },
148
+ }));
149
+ },
150
+ ],
151
+ });
152
+ await loader.reload();
153
+ assert.deepEqual(loader.getExtensions().errors, []);
154
+ ({ session } = await createAgentSession({
155
+ cwd,
156
+ agentDir,
157
+ modelRuntime,
158
+ model: modelRuntime.getModel("openai", "gpt-4o"),
159
+ thinkingLevel: "off",
160
+ settingsManager,
161
+ sessionManager: manager,
162
+ resourceLoader: loader,
163
+ }));
164
+ // Public retained bindings make Pi emit session_start again on SDK reload, including headless mode.
165
+ await session.bindExtensions({
166
+ mode: "print",
167
+ onError: (e) => errors.push(e),
168
+ ...(withUI ? { uiContext: { notify: (...args) => notices.push(args), setStatus: () => {} } } : {}),
169
+ });
170
+ await options.onSession?.(session);
171
+ await options.beforePrompt?.({ session, manager, requests, contexts, cwd, notices });
172
+ await session.prompt("Complete the fixture assignment.");
173
+ await session.waitForIdle();
174
+ assert.deepEqual(errors, [], "extension lifecycle errors");
175
+ if (after) await after({ session, manager, requests, contexts, cwd, notices });
176
+ assert.deepEqual(errors, [], "post-lifecycle errors");
177
+ assert.deepEqual(transportFailures, [], "scripted provider assertions must not be swallowed as provider errors");
178
+ assert.notEqual(
179
+ session.messages.at(-1)?.stopReason,
180
+ "error",
181
+ `fixture completes without provider failure: ${session.messages.at(-1)?.errorMessage ?? ""}`,
182
+ );
183
+ return { requests, state: replay(manager.getBranch()), entries: manager.getEntries(), contexts };
184
+ } finally {
185
+ session?.dispose();
186
+ globalThis.fetch = priorFetch;
187
+ if (offline === undefined) delete process.env.PI_OFFLINE;
188
+ else process.env.PI_OFFLINE = offline;
189
+ await rm(root, { recursive: true, force: true });
190
+ }
191
+ }
@@ -14,7 +14,7 @@ import {
14
14
  runtimeContext,
15
15
  setSessionCoreOverride,
16
16
  } from "../../dist/runtime/runtime-context.js";
17
- import { PIFLOW_HOST } from "../cognitive-routing/host-fixture.js";
17
+ import { PIFLOW_HOST } from "../fixtures/pi-host.js";
18
18
 
19
19
  function context(cwd) {
20
20
  const notifications = [];
@@ -138,8 +138,9 @@ test("Pi exposes base skills without mode or Skills controls", async () => {
138
138
  const { handlers, commands } = loadExtension();
139
139
  const ctx = context(cwd);
140
140
  const resources = await handlers.get("resources_discover")({ cwd }, ctx);
141
- assert.equal(resources.skillPaths.length, 25);
141
+ assert.equal(resources.skillPaths.length, 27);
142
142
  assert.ok(resources.skillPaths.some((path) => path.endsWith("/skills/workflow/SKILL.md")));
143
+ assert.ok(!resources.skillPaths.some((path) => path.endsWith("/skills/tdd/SKILL.md")));
143
144
  assert.ok(!resources.skillPaths.some((path) => path.endsWith("/skills/mode-contract/SKILL.md")));
144
145
 
145
146
  const freeflowCommand = commands.find((command) => command.name === "freeflow");
@@ -180,9 +181,10 @@ test("mandatory prompt readiness gates Runtime State, discovery, and direct skil
180
181
  const { handlers, commands, sentMessages } = loadExtension(extension);
181
182
  const ctx = context(cwd);
182
183
  const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
183
- assert.equal(before.systemPrompt, "base prompt");
184
+ assert.ok(before.systemPrompt.startsWith("base prompt\n\n"));
185
+ assert.match(before.systemPrompt, /guidance is dormant/);
184
186
  const resources = await handlers.get("resources_discover")({ cwd }, ctx);
185
- assert.deepEqual(resources.skillPaths, []);
187
+ assert.equal(resources.skillPaths.length, 27);
186
188
 
187
189
  const discuss = commands.find((command) => command.name === "discuss");
188
190
  assert.ok(discuss);