@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
@@ -1,66 +0,0 @@
1
- export function textComponent(text: any) {
2
- return {
3
- render(width = 120) {
4
- const maxWidth = Number.isFinite(width) ? Math.max(1, width) : 120;
5
- return String(text)
6
- .split("\n")
7
- .map((line) => truncateAnsiToWidth(line, maxWidth));
8
- },
9
- invalidate() {},
10
- };
11
- }
12
-
13
- function truncateAnsiToWidth(input: any, width: number): string {
14
- const text = String(input);
15
- let output = "";
16
- let visible = 0;
17
-
18
- for (let index = 0; index < text.length;) {
19
- const ansi = readAnsiSequence(text, index);
20
- if (ansi) {
21
- output += ansi.sequence;
22
- index = ansi.end;
23
- continue;
24
- }
25
-
26
- if (visible >= width - 1) {
27
- output += "…";
28
- return output;
29
- }
30
-
31
- const codePoint = text.codePointAt(index) ?? 0;
32
- const character = String.fromCodePoint(codePoint);
33
- output += character;
34
- visible += 1;
35
- index += character.length;
36
- }
37
-
38
- return output;
39
- }
40
-
41
- function readAnsiSequence(text: string, index: number): { sequence: string; end: number } | null {
42
- if (text.charCodeAt(index) !== 0x1b) {
43
- return null;
44
- }
45
-
46
- const next = text[index + 1];
47
- if (next === "[") {
48
- let end = index + 2;
49
- while (end < text.length && !/[\x40-\x7e]/.test(text[end] ?? "")) {
50
- end += 1;
51
- }
52
- const boundedEnd = Math.min(end + 1, text.length);
53
- return { sequence: text.slice(index, boundedEnd), end: boundedEnd };
54
- }
55
-
56
- if (next === "]") {
57
- const bellEnd = text.indexOf("\x07", index + 2);
58
- const stEnd = text.indexOf("\x1b\\", index + 2);
59
- const candidates = [bellEnd, stEnd === -1 ? -1 : stEnd + 1].filter((value) => value !== -1);
60
- const end = candidates.length > 0 ? Math.min(...candidates) + 1 : text.length;
61
- return { sequence: text.slice(index, end), end };
62
- }
63
-
64
- const end = Math.min(index + 2, text.length);
65
- return { sequence: text.slice(index, end), end };
66
- }
@@ -1,484 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { fixture } from "../fixtures/routing-native.js";
4
- import { Sources } from "../../dist/session-sources/sources.js";
5
- import { initialState } from "../../dist/cognitive-routing-v2/state.js";
6
- import { prepareView, changeSelection } from "../../dist/cognitive-routing-v2/projection.js";
7
- import { RoutingRuntime } from "../../dist/cognitive-routing-v2/runtime.js";
8
-
9
- const call = (name, args) => [{ name, args }];
10
- const receipt = (m, name) =>
11
- m
12
- .getBranch()
13
- .filter((e) => e.message?.toolName === name)
14
- .at(-1).message.details;
15
- const model = {
16
- id: "receiver",
17
- provider: "fixture",
18
- api: "fixture",
19
- input: ["text"],
20
- contextWindow: 100000,
21
- maxTokens: 1000,
22
- };
23
- const assistant = (text, calls = []) => ({
24
- role: "assistant",
25
- content: [{ type: "text", text }, ...calls],
26
- model: "receiver",
27
- provider: "fixture",
28
- api: "fixture",
29
- timestamp: 1,
30
- stopReason: calls.length ? "toolUse" : "stop",
31
- });
32
- const entry = (id, parentId, message) => ({ type: "message", id, parentId, message });
33
- function prepare(state, entries, messages = entries.map((e) => e.message), view = "executor") {
34
- return prepareView({
35
- state,
36
- sources: new Sources(entries, state),
37
- messages,
38
- view,
39
- projection: true,
40
- model,
41
- pair: { provider: "fixture", modelId: "receiver", thinking: "off" },
42
- systemPrompt: "",
43
- tools: [],
44
- runtimeMessage: RoutingRuntime.prototype.runtimeMessage.call(
45
- { projectionEnabled: true, token: "test", evidenceFacts: () => ({ selected: [] }) },
46
- state,
47
- ),
48
- instance: "test",
49
- });
50
- }
51
-
52
- test("provenance covers old sources and complete exchanges, with stable prefixes and unchanged originals", () => {
53
- const state = initialState();
54
- const entries = Array.from({ length: 40 }, (_, i) =>
55
- entry(`m${i}`, i ? `m${i - 1}` : null, assistant(`Observation ${i}`)),
56
- );
57
- for (const [i, e] of entries.entries())
58
- state.authors.set(e.id, {
59
- profile: i % 2 ? "executor" : "coordinator",
60
- assignmentId: i % 2 ? "earlier-assignment" : undefined,
61
- });
62
- entries.push(entry("unknown", "m39", assistant("Imported observation")));
63
- entries.push(
64
- entry(
65
- "calls",
66
- "unknown",
67
- assistant("Read observations", [{ type: "toolCall", id: "c", name: "read", arguments: { path: "x" } }]),
68
- ),
69
- );
70
- entries.push(
71
- entry("result", "calls", {
72
- role: "toolResult",
73
- toolCallId: "c",
74
- toolName: "read",
75
- content: [{ type: "text", text: "FULL_RESULT" }],
76
- }),
77
- );
78
- state.authors.set("calls", { profile: "executor", assignmentId: "current" });
79
- state.authors.set("result", { profile: "executor", assignmentId: "current" });
80
- const before = JSON.stringify(entries);
81
- const prepared = prepare(state, entries);
82
- const annotations = prepared.messages.filter((m) => m.customType === "freeflow-routing-v2-refs");
83
- for (let i = 0; i < 40; i++)
84
- assert.ok(
85
- annotations.some((m) => m.content.includes(`ctx:m${i} | producer: ${i % 2 ? "executor" : "coordinator"}`)),
86
- );
87
- assert.ok(annotations.some((m) => m.content.includes("ctx:unknown | producer: unknown/common")));
88
- const callIndex = prepared.messages.findIndex((m) => m.role === "assistant" && m.content.some((b) => b.id === "c"));
89
- assert.equal(prepared.messages[callIndex + 1].toolCallId, "c", "no annotation splits a call/result exchange");
90
- assert.match(prepared.messages[callIndex - 1].content, /ctx:result.*producer: executor/);
91
- const extended = prepare(state, [...entries, entry("later", "result", assistant("Later observation"))]);
92
- assert.deepEqual(
93
- extended.messages.slice(0, prepared.messages.length - 1),
94
- prepared.messages.slice(0, -1),
95
- "appends leave old provenance stable",
96
- );
97
- assert.equal(JSON.stringify(entries), before);
98
- });
99
-
100
- test("new control selections are rejected while old selections and mixed assistant text remain usable", () => {
101
- const state = initialState();
102
- const entries = [
103
- entry(
104
- "control",
105
- null,
106
- assistant("Substantive executor finding", [
107
- { type: "toolCall", id: "c", name: "freeflow_project", arguments: { operation: "inspect" } },
108
- ]),
109
- ),
110
- entry("receipt", "control", {
111
- role: "toolResult",
112
- toolCallId: "c",
113
- toolName: "freeflow_project",
114
- content: [{ type: "text", text: '{"status":"unchanged"}' }],
115
- }),
116
- ];
117
- for (const e of entries) state.authors.set(e.id, { profile: "executor", assignmentId: "a" });
118
- const sources = new Sources(entries, state);
119
- for (const s of sources.byRef.values()) state.exposure.set(s.ref, s.hash);
120
- const empty = { revision: 0, selected: [], unresolved: [], withdrawals: [] };
121
- const attempted = changeSelection(
122
- empty,
123
- { operation: "add", refs: ["ctx:control", "ctx:receipt", "ctx:control#text"] },
124
- sources,
125
- state,
126
- );
127
- assert.deepEqual(attempted.selected, ["ctx:control#text"]);
128
- assert.deepEqual(
129
- attempted.unresolved.map((p) => p.code),
130
- ["routing_source", "routing_source"],
131
- );
132
- const prior = { ...empty, revision: 1, selected: ["ctx:receipt"] };
133
- assert.equal(changeSelection(prior, { operation: "add", refs: ["ctx:receipt"] }, sources, state), prior);
134
- state.assignmentId = "a";
135
- state.selections.set("a", attempted);
136
- const projected = prepare(
137
- state,
138
- entries,
139
- entries.map((e) => e.message),
140
- "coordinator",
141
- );
142
- assert.ok(
143
- projected.messages.some(
144
- (m) => m.customType === "freeflow-routing-v2-refs" && m.content.includes("ctx:control#text | producer: executor"),
145
- ),
146
- );
147
- assert.ok(projected.messages.some((m) => m.customType === "freeflow-assistant-text"));
148
- state.selections.set("a", prior);
149
- assert.ok(
150
- prepare(
151
- state,
152
- entries,
153
- entries.map((e) => e.message),
154
- "coordinator",
155
- ).messages.some((m) => m.toolName === "freeflow_project"),
156
- );
157
- });
158
-
159
- test("restoration preserves full report metadata and deduplicates by accepted occurrence", () => {
160
- const state = initialState();
161
- state.profile = "coordinator";
162
- state.assignmentId = "a";
163
- state.assignments.set("a", { id: "a", state: "returned", contract: "CONTRACT", delegateHandoffId: "d" });
164
- state.handoffs.set("d", { id: "d", kind: "delegate", toolCallId: "dc" });
165
- const h = {
166
- id: "h",
167
- assignmentId: "a",
168
- kind: "return",
169
- state: "configured",
170
- toolCallId: "rc",
171
- text: "SAME_REPORT",
172
- outcome: "partial",
173
- limitations: ["UNRUN_INTEGRATION"],
174
- reportRevision: 2,
175
- };
176
- state.handoffs.set("h", h);
177
- state.assessment = { handoffId: "h", view: "active", problems: [] };
178
- const restored = JSON.stringify(prepare(state, [], [], "coordinator").messages);
179
- for (const value of ["SAME_REPORT", "UNRUN_INTEGRATION", "partial", "report:h:2", "assignment:a"])
180
- assert.ok(restored.includes(value));
181
- const old = {
182
- role: "toolResult",
183
- toolName: "freeflow_return",
184
- toolCallId: "other",
185
- content: [{ type: "text", text: JSON.stringify({ report: h.text, outcome: "completed", limitations: [] }) }],
186
- };
187
- const oldResult = prepare(state, [], [old], "coordinator");
188
- assert.ok(
189
- oldResult.messages.some(
190
- (m) => m.customType === "freeflow-routing-communication" && m.content.includes("SAME_REPORT"),
191
- ),
192
- "equal text from another call cannot satisfy current report",
193
- );
194
- const incomplete = { ...old, toolCallId: "rc" };
195
- const corrected = prepare(state, [], [incomplete], "coordinator");
196
- assert.equal(JSON.stringify(corrected.messages).split("SAME_REPORT").length - 1, 1);
197
- assert.ok(JSON.stringify(corrected.messages).includes("UNRUN_INTEGRATION"));
198
- });
199
-
200
- for (const projection of [false, true])
201
- test(`native report arrives complete without inspection and survives close, projection ${projection}`, async () => {
202
- await fixture((n, body, m) => {
203
- if (n === 1) return call("freeflow_delegate", { operation: "assign", contract: "Return a partial report." });
204
- if (n === 2)
205
- return call("freeflow_return", {
206
- operation: "submit",
207
- report: "REPORT_DIRECT_91",
208
- outcome: "partial",
209
- limitations: ["LIMIT_DIRECT_92"],
210
- });
211
- if (n === 3) {
212
- const r = receipt(m, "freeflow_return");
213
- assert.equal(r.outcome, "partial");
214
- assert.deepEqual(r.limitations, ["LIMIT_DIRECT_92"]);
215
- assert.match(r.assignmentRef, /^assignment:/);
216
- assert.match(r.reportRef, /^report:.*:1$/);
217
- for (const text of ["REPORT_DIRECT_91", "LIMIT_DIRECT_92", r.reportRef])
218
- assert.ok(JSON.stringify(body).includes(text));
219
- return call("freeflow_unit", {
220
- operation: "close",
221
- outcome: "deferred",
222
- assessment: "Partial result acknowledged.",
223
- });
224
- }
225
- if (n === 4)
226
- for (const text of ["REPORT_DIRECT_91", "LIMIT_DIRECT_92"]) assert.ok(JSON.stringify(body).includes(text));
227
- return [];
228
- }, projection);
229
- });
230
-
231
- test("native inspection snapshots distinguish page/scope totals, exclude controls and explain invalid work refs", async () => {
232
- let first;
233
- await fixture((n, body, m) => {
234
- if (n === 1) return call("freeflow_delegate", { operation: "assign", contract: "Read observations." });
235
- if (n === 2) return Array.from({ length: 35 }, () => ({ name: "read", args: { path: "evidence.txt" } }));
236
- if (n === 3) return call("freeflow_project", { operation: "inspect", scope: "history" });
237
- if (n === 4) {
238
- first = receipt(m, "freeflow_project");
239
- assert.equal(first.pageCounts.candidates, 30);
240
- assert.ok(first.scopeCounts.candidates > 30);
241
- assert.equal(first.scopeCounts.eligible, first.scopeCounts.candidates);
242
- assert.ok(first.candidates.every((c) => c.producer === "executor" && !c.toolName?.startsWith("freeflow_")));
243
- return call("freeflow_project", { operation: "inspect", scope: "history", cursor: first.nextCursor });
244
- }
245
- if (n === 5) {
246
- const second = receipt(m, "freeflow_project");
247
- assert.deepEqual(second.scopeCounts, first.scopeCounts);
248
- assert.equal(
249
- new Set([...first.candidates, ...second.candidates].map((c) => c.ref)).size,
250
- first.scopeCounts.candidates,
251
- );
252
- return call("freeflow_return", { operation: "submit", report: "Done", outcome: "completed" });
253
- }
254
- if (n === 6)
255
- return call("freeflow_unit", {
256
- operation: "inspect",
257
- view: "detail",
258
- ref: receipt(m, "freeflow_return").handoff,
259
- });
260
- if (n === 7) {
261
- assert.equal(receipt(m, "freeflow_unit").code, "invalid_work_ref");
262
- return call("freeflow_unit", {
263
- operation: "inspect",
264
- view: "detail",
265
- ref: receipt(m, "freeflow_return").assignmentRef,
266
- });
267
- }
268
- if (n === 8) {
269
- assert.equal(receipt(m, "freeflow_unit").report, "Done");
270
- return call("freeflow_unit", { operation: "close", outcome: "accepted", assessment: "Read work assessed." });
271
- }
272
- return [];
273
- });
274
- });
275
-
276
- test("native compaction restores report limitations and revision into the same assessment", async () => {
277
- let saved;
278
- await fixture(
279
- (n, body, m) => {
280
- if (n === 1)
281
- return call("freeflow_delegate", { operation: "assign", contract: "Return a report for assessment." });
282
- if (n === 2)
283
- return call("freeflow_return", {
284
- operation: "submit",
285
- report: "COMPACT_REPORT_61",
286
- outcome: "partial",
287
- limitations: ["COMPACT_LIMIT_62"],
288
- });
289
- if (n === 3) {
290
- saved = receipt(m, "freeflow_return");
291
- return [];
292
- }
293
- if (n === 4) return call("freeflow_unit", { operation: "assess" });
294
- if (n === 5) {
295
- const wire = JSON.stringify(body);
296
- for (const value of ["COMPACT_REPORT_61", "COMPACT_LIMIT_62", "partial", saved.assignmentRef, saved.reportRef])
297
- assert.ok(wire.includes(value), value);
298
- }
299
- return [];
300
- },
301
- true,
302
- async ({ session }) => {
303
- await session.compact();
304
- await session.prompt("Resume assessment of the existing saved report.");
305
- await session.waitForIdle();
306
- },
307
- );
308
- });
309
-
310
- test("native retry delivers the unchanged complete report without a follow-up inspect", async () => {
311
- let saved;
312
- await fixture((n, body, m) => {
313
- if (n === 1) return call("freeflow_delegate", { operation: "assign", contract: "Return available findings." });
314
- if (n === 2) return call("freeflow_project", { operation: "add", refs: ["ctx:unavailable"] });
315
- if (n === 3)
316
- return call("freeflow_return", {
317
- operation: "submit",
318
- report: "RETRY_REPORT_11",
319
- outcome: "partial",
320
- limitations: ["RETRY_LIMIT_12"],
321
- });
322
- if (n === 4) {
323
- saved = receipt(m, "freeflow_return");
324
- assert.equal(saved.ready, false);
325
- return call("freeflow_project", {
326
- operation: "remove",
327
- refs: ["ctx:unavailable"],
328
- reason: "Original capture unavailable; retain limitation.",
329
- });
330
- }
331
- if (n === 5) return call("freeflow_return", { operation: "retry" });
332
- if (n === 6) {
333
- const retried = receipt(m, "freeflow_return");
334
- for (const key of ["report", "reportRef", "reportRevision", "assignmentRef", "outcome", "limitations"])
335
- assert.deepEqual(retried[key], saved[key]);
336
- assert.equal(retried.reportUnchanged, true);
337
- for (const value of ["RETRY_REPORT_11", "RETRY_LIMIT_12", saved.reportRef])
338
- assert.ok(JSON.stringify(body).includes(value));
339
- return call("freeflow_unit", { operation: "close", outcome: "deferred", assessment: "Partial report assessed." });
340
- }
341
- return [];
342
- });
343
- });
344
-
345
- test("empty new assignment advertises earlier evidence and selected receipts retain its producer", async () => {
346
- let ref, oldAssignment;
347
- await fixture((n, body, m) => {
348
- if (n === 1 || n === 5)
349
- return call("freeflow_delegate", {
350
- operation: "assign",
351
- contract: n === 1 ? "Read an observation." : "Reuse the earlier observation.",
352
- });
353
- if (n === 2) return call("read", { path: "evidence.txt" });
354
- if (n === 3) {
355
- ref = `ctx:${m.getBranch().find((e) => e.message?.toolName === "read").id}`;
356
- return call("freeflow_return", { operation: "submit", report: "Observation captured.", outcome: "completed" });
357
- }
358
- if (n === 4)
359
- return call("freeflow_unit", { operation: "close", outcome: "accepted", assessment: "Supported result." });
360
- if (n === 6) return call("freeflow_project", { operation: "inspect" });
361
- if (n === 7) {
362
- const r = receipt(m, "freeflow_project");
363
- assert.equal(r.count, 0);
364
- assert.ok(r.otherAssignments > 0);
365
- assert.match(r.historyHint, /scope history/);
366
- return call("freeflow_project", { operation: "inspect", scope: "history" });
367
- }
368
- if (n === 8) {
369
- const candidate = receipt(m, "freeflow_project").candidates.find((c) => c.ref === ref);
370
- assert.equal(candidate.producer, "executor");
371
- assert.equal(candidate.eligible, true);
372
- oldAssignment = candidate.assignment;
373
- return call("freeflow_project", { operation: "add", refs: [ref] });
374
- }
375
- if (n === 9) {
376
- const selected = receipt(m, "freeflow_project").evidence.selected[0];
377
- assert.equal(selected.producer, "executor");
378
- assert.equal(selected.assignment, oldAssignment);
379
- return call("freeflow_return", {
380
- operation: "submit",
381
- report: "Earlier evidence selected.",
382
- outcome: "completed",
383
- });
384
- }
385
- if (n === 10) {
386
- const wire = JSON.stringify(body);
387
- assert.ok(wire.includes(`${ref} | producer: executor`));
388
- assert.ok(wire.includes("EXACT_EVIDENCE_BODY_81"));
389
- return call("freeflow_unit", { operation: "inspect", view: "detail", ref: "assignment:absent" });
390
- }
391
- if (n === 11) {
392
- assert.equal(receipt(m, "freeflow_unit").code, "work_unavailable");
393
- return call("freeflow_unit", { operation: "close", outcome: "accepted", assessment: "Supported result." });
394
- }
395
- return [];
396
- });
397
- });
398
-
399
- test("normal inspection hides delivery gaps while selected inspection preserves their diagnostics", async () => {
400
- let ref;
401
- const result = await fixture(
402
- (n, body, m) => {
403
- if (n === 1) return call("freeflow_delegate", { operation: "assign", contract: "Read and inspect evidence." });
404
- if (n === 2) return call("read", { path: "evidence.txt" });
405
- if (n === 3) {
406
- ref = `ctx:${m.getBranch().find((e) => e.message?.content?.some?.((b) => b.type === "toolCall" && b.name === "read")).id}`;
407
- return call("freeflow_project", { operation: "inspect", scope: "assignment" });
408
- }
409
- if ([4, 5, 6].includes(n)) {
410
- const r = receipt(m, "freeflow_project");
411
- assert.ok(r.candidates.every((c) => c.eligible && c.targetReady));
412
- assert.ok(r.candidates.every((c) => !Object.hasOwn(c, "preview")));
413
- assert.ok(!JSON.stringify(r).includes("EXACT_EVIDENCE_BODY_81"), "inspection does not repeat source text");
414
- assert.equal(
415
- r.candidates.find((c) => c.ref === ref),
416
- undefined,
417
- "incompatible whole message is not a candidate",
418
- );
419
- assert.ok(
420
- r.candidates.some((c) => c.ref === `${ref}#text`),
421
- "usable visible text remains offered",
422
- );
423
- assert.equal(r.scopeCounts.candidates, r.scopeCounts.targetReady);
424
- assert.equal(r.pageCounts.candidates, r.pageCounts.targetReady);
425
- if (n === 4) return call("freeflow_project", { operation: "inspect", scope: "active" });
426
- if (n === 5) return call("freeflow_project", { operation: "inspect", scope: "history" });
427
- return call("freeflow_project", { operation: "add", refs: [ref] });
428
- }
429
- if (n === 7) {
430
- const r = receipt(m, "freeflow_project");
431
- assert.equal(r.ready, false);
432
- assert.ok(r.problems.some((p) => p.code === "target_representation"));
433
- return call("freeflow_project", { operation: "inspect", scope: "selected" });
434
- }
435
- if (n === 8) {
436
- const r = receipt(m, "freeflow_project");
437
- assert.deepEqual(r.selected, [ref]);
438
- const selected = r.candidates.find((c) => c.ref === ref);
439
- assert.equal(Object.hasOwn(selected, "preview"), false);
440
- assert.equal(selected.targetReady, false);
441
- assert.ok(selected.limitations.some((p) => p.code === "target_representation"));
442
- assert.equal(r.scopeCounts.candidates, 1);
443
- assert.equal(r.scopeCounts.targetReady, 0);
444
- }
445
- return [];
446
- },
447
- true,
448
- undefined,
449
- true,
450
- {
451
- extensions: [
452
- (pi) =>
453
- pi.on("message_end", (e) => {
454
- if (
455
- e.message.role === "assistant" &&
456
- e.message.content.some((b) => b.type === "toolCall" && b.name === "read")
457
- )
458
- return {
459
- message: {
460
- ...e.message,
461
- content: [
462
- ...e.message.content,
463
- {
464
- type: "thinking",
465
- thinking: "Fixture signed reasoning",
466
- thinkingSignature: JSON.stringify({
467
- type: "reasoning",
468
- id: "rs_candidate",
469
- summary: [{ type: "summary_text", text: "Fixture signed reasoning" }],
470
- }),
471
- },
472
- ],
473
- },
474
- };
475
- }),
476
- ],
477
- },
478
- );
479
- assert.deepEqual(
480
- [...result.state.selections.values()].at(-1).selected,
481
- [ref],
482
- "inspection does not silently withdraw evidence",
483
- );
484
- });