@hassangameryt/freeflow 0.7.2 → 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 (79) 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 +147 -127
  6. package/capabilities/cognitive-routing/SKILL.md +162 -93
  7. package/gemini-extension.json +1 -1
  8. package/package.json +1 -1
  9. package/pi-extension/dist/cognitive-routing-v2/config.js +35 -19
  10. package/pi-extension/dist/cognitive-routing-v2/projection.js +53 -44
  11. package/pi-extension/dist/cognitive-routing-v2/render.js +31 -10
  12. package/pi-extension/dist/cognitive-routing-v2/runtime.js +701 -105
  13. package/pi-extension/dist/cognitive-routing-v2/schemas.js +90 -24
  14. package/pi-extension/dist/cognitive-routing-v2/state.js +243 -27
  15. package/pi-extension/dist/cognitive-routing-v2/tools.js +19 -50
  16. package/pi-extension/dist/cognitive-routing-v2/types.js +5 -1
  17. package/pi-extension/dist/context-virtualization/runtime.js +29 -7
  18. package/pi-extension/dist/freeflow-context/tool.js +7 -6
  19. package/pi-extension/dist/index.js +36 -15
  20. package/pi-extension/dist/provider-support/astra/adapter.js +110 -0
  21. package/pi-extension/dist/provider-support/astra/history.js +125 -0
  22. package/pi-extension/dist/provider-support/astra/index.js +11 -0
  23. package/pi-extension/dist/provider-support/astra/session-state.js +114 -0
  24. package/pi-extension/dist/provider-support/index.js +4 -0
  25. package/pi-extension/dist/runtime/request-history.js +216 -0
  26. package/pi-extension/dist/runtime/runtime-context.js +29 -10
  27. package/pi-extension/dist/session-sources/events.js +22 -0
  28. package/pi-extension/dist/session-sources/provenance.js +32 -0
  29. package/pi-extension/dist/session-sources/sources.js +51 -7
  30. package/pi-extension/dist/settings/settings-ui.js +247 -47
  31. package/pi-extension/src/cognitive-routing-v2/config.ts +49 -19
  32. package/pi-extension/src/cognitive-routing-v2/projection.ts +53 -43
  33. package/pi-extension/src/cognitive-routing-v2/render.ts +31 -10
  34. package/pi-extension/src/cognitive-routing-v2/runtime.ts +712 -105
  35. package/pi-extension/src/cognitive-routing-v2/schemas.ts +95 -24
  36. package/pi-extension/src/cognitive-routing-v2/state.ts +244 -27
  37. package/pi-extension/src/cognitive-routing-v2/tools.ts +19 -50
  38. package/pi-extension/src/cognitive-routing-v2/types.ts +35 -3
  39. package/pi-extension/src/context-virtualization/runtime.ts +30 -8
  40. package/pi-extension/src/freeflow-context/tool.ts +7 -6
  41. package/pi-extension/src/index.ts +36 -14
  42. package/pi-extension/src/provider-support/astra/adapter.ts +108 -0
  43. package/pi-extension/src/provider-support/astra/history.ts +145 -0
  44. package/pi-extension/src/provider-support/astra/index.ts +12 -0
  45. package/pi-extension/src/provider-support/astra/session-state.ts +113 -0
  46. package/pi-extension/src/provider-support/index.ts +5 -0
  47. package/pi-extension/src/runtime/request-history.ts +243 -0
  48. package/pi-extension/src/runtime/runtime-context.ts +32 -10
  49. package/pi-extension/src/session-sources/events.ts +22 -0
  50. package/pi-extension/src/session-sources/provenance.ts +39 -0
  51. package/pi-extension/src/session-sources/sources.ts +77 -6
  52. package/pi-extension/src/settings/settings-ui.ts +276 -46
  53. package/pi-extension/tests/cache-reuse/history.test.js +117 -0
  54. package/pi-extension/tests/cache-reuse/native.test.js +91 -0
  55. package/pi-extension/tests/cache-reuse/settings.test.js +211 -0
  56. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +94 -1
  57. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +557 -0
  58. package/pi-extension/tests/cognitive-routing-v2/native.test.js +34 -1
  59. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +55 -0
  60. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +800 -2
  61. package/pi-extension/tests/cognitive-routing-v2/render.test.js +15 -1
  62. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +273 -3
  63. package/pi-extension/tests/cognitive-routing-v2/state.test.js +312 -1
  64. package/pi-extension/tests/context-virtualization/extension.test.js +20 -10
  65. package/pi-extension/tests/context-virtualization/runtime.test.js +17 -3
  66. package/pi-extension/tests/fixtures/routing-native.js +4 -1
  67. package/pi-extension/tests/integration/mode-free-core.test.js +4 -3
  68. package/pi-extension/tests/integration/pi-extension.test.js +508 -29
  69. package/pi-extension/tests/integration/prompt-architecture.test.js +56 -40
  70. package/pi-extension/tests/integration/unaffected-regressions.test.js +3 -3
  71. package/pi-extension/tests/provider-support/astra-boundaries.test.js +75 -0
  72. package/pi-extension/tests/provider-support/astra.test.js +306 -0
  73. package/pi-extension/tests/provider-support/cli.test.js +92 -0
  74. package/pi-extension/tests/provider-support/native.test.js +183 -0
  75. package/plugin.json +1 -1
  76. package/runtime/prompts/cognitive-routing.md +4 -4
  77. package/skills/design-for-depth/SKILL.md +1 -1
  78. package/skills/design-for-depth/references/implementation-decisions.md +3 -3
  79. package/skills/discuss/SKILL.md +2 -2
@@ -169,7 +169,7 @@ function renderComponent(component) {
169
169
  return component.render(120).join("\\n");
170
170
  }
171
171
 
172
- test("disabled Freeflow Context does not publish an empty oneOf schema", async () => {
172
+ test("disabled Freeflow Context retains valid full operation schemas", async () => {
173
173
  const cwd = await mkdtemp(join(tmpdir(), "freeflow-context-disabled-schema-"));
174
174
  try {
175
175
  await writeConfig(cwd, {});
@@ -178,8 +178,17 @@ test("disabled Freeflow Context does not publish an empty oneOf schema", async (
178
178
 
179
179
  const contextTool = harness.tools.find((tool) => tool.name === "freeflow_context");
180
180
  assert.ok(contextTool);
181
- assert.deepEqual(contextTool.parameters.properties, {});
182
- assert.equal(Object.hasOwn(contextTool.parameters, "oneOf"), false);
181
+ assert.equal(contextTool.parameters.type, "object");
182
+ assert.deepEqual(
183
+ contextTool.parameters.oneOf.map((v) => v.properties.operation.const),
184
+ ["archive", "restore", "search", "retrieve"],
185
+ );
186
+ const denied = await contextTool.execute(
187
+ "disabled",
188
+ { operation: "archive", targets: [{ ref: "ctx:tool-1" }] },
189
+ undefined,
190
+ );
191
+ assert.equal(denied.details.result.status, "rejected");
183
192
  } finally {
184
193
  await rm(cwd, { recursive: true, force: true });
185
194
  }
@@ -205,7 +214,7 @@ test("enabled Context Virtualization registers, projects, archives, restores, an
205
214
 
206
215
  const contextTool = harness.tools.find((tool) => tool.name === "freeflow_context");
207
216
  assert.ok(contextTool);
208
- assert.doesNotMatch(contextTool.description, /conversation history/i);
217
+ assert.match(contextTool.description, /conversation history/i);
209
218
  const staleSearch = await contextTool.execute(
210
219
  "context-stale-search",
211
220
  { operation: "search", query: "not enabled" },
@@ -291,7 +300,7 @@ test("enabled Context Virtualization registers, projects, archives, restores, an
291
300
  }
292
301
  });
293
302
 
294
- test("enabled Conversation History exposes only search and retrieve operations", async () => {
303
+ test("enabled Conversation History permits search and retrieve within the stable catalog", async () => {
295
304
  const cwd = await mkdtemp(join(tmpdir(), "freeflow-conversation-history-extension-"));
296
305
  try {
297
306
  await writeConfig(cwd, { conversationHistory: true });
@@ -303,7 +312,7 @@ test("enabled Conversation History exposes only search and retrieve operations",
303
312
  assert.ok(contextTool);
304
313
  assert.deepEqual(
305
314
  contextTool.parameters.oneOf.map((variant) => variant.properties.operation.const),
306
- ["search", "retrieve"],
315
+ ["archive", "restore", "search", "retrieve"],
307
316
  );
308
317
  assert.equal(typeof contextTool.renderCall, "function");
309
318
  assert.equal(typeof contextTool.renderResult, "function");
@@ -365,7 +374,7 @@ test("enabled Conversation History exposes only search and retrieve operations",
365
374
  assert.match(renderComponent(expandedResult), /ctx:render-source/);
366
375
  assert.match(renderComponent(expandedResult), /database timeout was recovered/);
367
376
  assert.match(contextTool.description, /hidden conversation history/i);
368
- assert.doesNotMatch(contextTool.description, /future context projections/i);
377
+ assert.match(contextTool.description, /future context projections/i);
369
378
  const before = await harness.handlers.get("before_agent_start")({ systemPrompt: "base" }, harness.ctx);
370
379
  assert.match(before.systemPrompt, /## Conversation History Cue/);
371
380
  assert.doesNotMatch(before.systemPrompt, /# Conversation History$/m);
@@ -744,20 +753,21 @@ test("visible invalid sources do not make an empty hidden corpus partial", async
744
753
  }
745
754
  });
746
755
 
747
- test("disabled Context Virtualization hides the tool and leaves tool results unchanged", async () => {
756
+ test("disabled Context Virtualization blocks execution while preserving definitions and reference labels", async () => {
748
757
  const cwd = await mkdtemp(join(tmpdir(), "freeflow-context-disabled-"));
749
758
  try {
750
759
  await writeConfig(cwd, false);
751
760
  const harness = createHarness(cwd, [toolResultEntry()]);
752
761
  await harness.handlers.get("session_start")({ reason: "startup" }, harness.ctx);
753
762
 
754
- assert.ok(!harness.activeToolNames().includes("freeflow_context"));
763
+ assert.ok(harness.activeToolNames().includes("freeflow_context"));
755
764
  const contextResult = await harness.handlers.get("context")(
756
765
  { messages: [harness.entries[0].message] },
757
766
  harness.ctx,
758
767
  );
759
768
  assert.ok(contextResult);
760
- assert.deepEqual(contextResult.messages[0], harness.entries[0].message);
769
+ assert.deepEqual(contextResult.messages[0].content.slice(0, -1), harness.entries[0].message.content);
770
+ assert.match(contextResult.messages[0].content.at(-1).text, /context-ref:/);
761
771
  assert.equal(contextResult.messages.at(-1).customType, "freeflow-runtime-state");
762
772
  } finally {
763
773
  await rm(cwd, { recursive: true, force: true });
@@ -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
  });
@@ -69,7 +69,7 @@ export async function fixture(script, projection = true, after, withUI = true, o
69
69
  await writeFile(
70
70
  join(cwd, ".freeflow/config.json"),
71
71
  JSON.stringify({
72
- cognitiveRouting: {
72
+ cognitiveRouting: options.cognitiveRouting ?? {
73
73
  enabled: true,
74
74
  projection,
75
75
  profiles: {
@@ -81,6 +81,8 @@ export async function fixture(script, projection = true, after, withUI = true, o
81
81
  );
82
82
  await writeFile(join(cwd, "evidence.txt"), "EXACT_EVIDENCE_BODY_81");
83
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");
84
86
  await writeFile(
85
87
  join(agentDir, "models.json"),
86
88
  JSON.stringify({ providers: { openai: { baseUrl: "https://fixture.invalid/v1" } } }),
@@ -166,6 +168,7 @@ export async function fixture(script, projection = true, after, withUI = true, o
166
168
  ...(withUI ? { uiContext: { notify: (...args) => notices.push(args), setStatus: () => {} } } : {}),
167
169
  });
168
170
  await options.onSession?.(session);
171
+ await options.beforePrompt?.({ session, manager, requests, contexts, cwd, notices });
169
172
  await session.prompt("Complete the fixture assignment.");
170
173
  await session.waitForIdle();
171
174
  assert.deepEqual(errors, [], "extension lifecycle errors");
@@ -138,7 +138,7 @@ 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, 24);
141
+ assert.equal(resources.skillPaths.length, 27);
142
142
  assert.ok(resources.skillPaths.some((path) => path.endsWith("/skills/workflow/SKILL.md")));
143
143
  assert.ok(!resources.skillPaths.some((path) => path.endsWith("/skills/tdd/SKILL.md")));
144
144
  assert.ok(!resources.skillPaths.some((path) => path.endsWith("/skills/mode-contract/SKILL.md")));
@@ -181,9 +181,10 @@ test("mandatory prompt readiness gates Runtime State, discovery, and direct skil
181
181
  const { handlers, commands, sentMessages } = loadExtension(extension);
182
182
  const ctx = context(cwd);
183
183
  const before = await handlers.get("before_agent_start")({ systemPrompt: "base prompt" }, ctx);
184
- assert.equal(before.systemPrompt, "base prompt");
184
+ assert.ok(before.systemPrompt.startsWith("base prompt\n\n"));
185
+ assert.match(before.systemPrompt, /guidance is dormant/);
185
186
  const resources = await handlers.get("resources_discover")({ cwd }, ctx);
186
- assert.deepEqual(resources.skillPaths, []);
187
+ assert.equal(resources.skillPaths.length, 27);
187
188
 
188
189
  const discuss = commands.find((command) => command.name === "discuss");
189
190
  assert.ok(discuss);