@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
@@ -0,0 +1,241 @@
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import { relative, resolve as resolvePath } from "node:path";
3
+ import { ROUTING_RECOVERY_HINT } from "./session.js";
4
+ export { ROUTING_RECOVERY_HINT };
5
+ import { bodyHash } from "./sources.js";
6
+ import { initialState } from "./state.js";
7
+ import { RoutingError, isWorkerProfile, requireCondition as check, ROUTING_TOOLS } from "./types.js";
8
+ import { cachedBranch } from "../host/branch.js";
9
+ /**
10
+ * What routing allows while a profile runs, and what it tells the tool runtime: which ordinary tools a worker may
11
+ * still call, which recovery reads an assessment permits, handoff batching rules, and the operation, admission, and
12
+ * observation scopes other packages attach to their work.
13
+ */
14
+ export class ToolGate {
15
+ session;
16
+ packageRoots;
17
+ constructor(session, packageRoots) {
18
+ this.session = session;
19
+ this.packageRoots = packageRoots;
20
+ }
21
+ observationScope() {
22
+ let state;
23
+ try {
24
+ state = this.session.stateData();
25
+ } catch {
26
+ state = initialState();
27
+ }
28
+ const pair = this.session.observed();
29
+ const profile = state.profile ?? "solo";
30
+ const control =
31
+ state.control === "manual" || state.control === "automatic" || state.control === "inactive"
32
+ ? state.control
33
+ : "unknown";
34
+ return {
35
+ profile,
36
+ control,
37
+ ...(state.assignmentId ? { assignmentId: state.assignmentId } : {}),
38
+ ...(this.session.turn?.id ? { executionId: this.session.turn.id } : {}),
39
+ ...(pair ? { provider: pair.provider, modelId: pair.modelId, thinking: pair.thinking } : {}),
40
+ };
41
+ }
42
+ operationScope(ctx = this.session.ctx) {
43
+ let state;
44
+ try {
45
+ state = this.session.stateData();
46
+ } catch {
47
+ state = initialState();
48
+ }
49
+ const sessionId = ctx?.sessionManager?.getSessionId?.();
50
+ return {
51
+ token: this.session.token,
52
+ revision: this.session.revision,
53
+ ...(typeof sessionId === "string" ? { sessionId } : {}),
54
+ supported: this.session.supported(),
55
+ control: state.control,
56
+ ...(state.profile ? { profile: state.profile } : {}),
57
+ ...(state.assignmentId ? { assignmentId: state.assignmentId } : {}),
58
+ ...(this.session.turn?.id ? { turnId: this.session.turn.id } : {}),
59
+ ...(this.session.turn ? { basisUserEntryId: this.session.turn.basisUserEntryId } : {}),
60
+ };
61
+ }
62
+ admitOperation(scope, effect, _operation, ctx = this.session.ctx) {
63
+ try {
64
+ const state = this.session.stateData();
65
+ if (state.recoveryId && effect !== "captured-read")
66
+ return {
67
+ kind: "denied",
68
+ code: "recovery_live_operation",
69
+ message: "Live operations are unavailable during attached evidence recovery.",
70
+ };
71
+ const current = this.operationScope(ctx);
72
+ const same =
73
+ current.token === scope.token &&
74
+ current.revision === scope.revision &&
75
+ current.sessionId === scope.sessionId &&
76
+ current.supported === scope.supported &&
77
+ current.control === scope.control &&
78
+ current.profile === scope.profile &&
79
+ current.assignmentId === scope.assignmentId &&
80
+ current.turnId === scope.turnId &&
81
+ current.basisUserEntryId === scope.basisUserEntryId;
82
+ return same
83
+ ? { kind: "allowed" }
84
+ : {
85
+ kind: "denied",
86
+ code: "routing_scope_changed",
87
+ message: "Routing responsibility changed before operation execution.",
88
+ };
89
+ } catch {
90
+ return { kind: "denied", code: "routing_unavailable", message: "Routing admission is unavailable." };
91
+ }
92
+ }
93
+ admitProgram(scope, ctx = this.session.ctx) {
94
+ try {
95
+ if (this.session.stateData().recoveryId)
96
+ return {
97
+ kind: "denied",
98
+ code: "recovery_program_unavailable",
99
+ message: "Programs are unavailable during attached evidence recovery.",
100
+ };
101
+ return this.admitOperation(scope, "captured-read", { id: "freeflow_run", revision: "1" }, ctx);
102
+ } catch {
103
+ return { kind: "denied", code: "routing_unavailable", message: "Routing admission is unavailable." };
104
+ }
105
+ }
106
+ resultReadAccess(id) {
107
+ try {
108
+ const state = this.session.stateData();
109
+ const recovery = state.recoveryId ? state.recoveries.get(state.recoveryId) : undefined;
110
+ if (recovery?.state !== "reading") return { recovery: false };
111
+ const grant = (recovery.results ?? []).find((candidate) => candidate.id === id);
112
+ return { recovery: true, ...(grant ? { sha256: grant.sha256 } : {}) };
113
+ } catch {
114
+ return { recovery: false };
115
+ }
116
+ }
117
+ handoffOperation(name, input) {
118
+ if (name === "freeflow_delegate") return true;
119
+ if (name === "freeflow_return") return ["submit", "supplement", "retry"].includes(input?.operation);
120
+ return name === "freeflow_unit" && input?.operation === "recover";
121
+ }
122
+ stableRecoveryPath(path) {
123
+ return (
124
+ !path.startsWith("@") &&
125
+ path !== "~" &&
126
+ !path.startsWith("~/") &&
127
+ !path.startsWith("file://") &&
128
+ !/[\u00a0\u2000-\u200a\u202f\u205f\u3000]/u.test(path) &&
129
+ !(process.platform === "win32" && /^\/(?:mnt\/|cygdrive\/)?[a-z](?:\/|$)/i.test(path))
130
+ );
131
+ }
132
+ canonicalRecoveryPath(path) {
133
+ check(this.stableRecoveryPath(path), "recovery_path_unsupported", `Recovery path spelling is unsupported: ${path}`);
134
+ const requested = resolvePath(this.session.ctx.cwd, path);
135
+ check(existsSync(requested), "recovery_path_unavailable", `Recovery path is unavailable: ${path}`);
136
+ try {
137
+ return realpathSync(requested);
138
+ } catch {
139
+ throw new RoutingError("recovery_path_unavailable", `Recovery path is unavailable: ${path}`);
140
+ }
141
+ }
142
+ packagedInstruction(path) {
143
+ return this.packageRoots.some((root) => {
144
+ const inside = relative(root, path);
145
+ if (!inside || inside.startsWith("..") || resolvePath(root, inside) !== path) return false;
146
+ return (
147
+ /^(?:skills|capabilities)\/[^/]+\/SKILL\.md$/.test(inside) ||
148
+ /^(?:skills|capabilities)\/[^/]+\/references\/[^/]+\.md$/.test(inside)
149
+ );
150
+ });
151
+ }
152
+ recoveryReadAllowed(state, name, input) {
153
+ if (!state.recoveryId) return false;
154
+ const recovery = state.recoveries.get(state.recoveryId);
155
+ if (!recovery || recovery.state !== "reading") return false;
156
+ if (name === "freeflow_result" && typeof input?.id === "string") {
157
+ return (recovery.results ?? []).some((grant) => grant.id === input.id);
158
+ }
159
+ if (name !== "read" || typeof input?.path !== "string" || !this.stableRecoveryPath(input.path)) return false;
160
+ try {
161
+ const path = this.canonicalRecoveryPath(input.path);
162
+ return recovery.paths.includes(path) || this.packagedInstruction(path);
163
+ } catch {
164
+ return false;
165
+ }
166
+ }
167
+ // A call another tool makes (codemode, ctx.executeTool) has the id `<issuing call id>/<n>` and never appears in the
168
+ // assistant message; it belongs to the batch of the model-issued call it descends from.
169
+ batch(callId, name, nested = false) {
170
+ check(this.session.turn?.message, "batch_unavailable");
171
+ const issued = (b) => b.type === "toolCall" && (nested ? callId.startsWith(`${b.id}/`) : b.id === callId);
172
+ const matches = cachedBranch(this.session.ctx.sessionManager).filter(
173
+ (e) =>
174
+ !this.session.turn.before.has(e.id) &&
175
+ e.type === "message" &&
176
+ e.message?.role === "assistant" &&
177
+ e.message.content?.some(issued),
178
+ );
179
+ check(matches.length === 1, "batch_source_changed");
180
+ const fingerprint = bodyHash(matches[0].message);
181
+ check(
182
+ !this.session.turn.sourceFingerprint || this.session.turn.sourceFingerprint === fingerprint,
183
+ "batch_source_changed",
184
+ );
185
+ // Later message_end handlers may legitimately replace the message. The first
186
+ // preflight freezes the actually persisted batch before any of its tools act.
187
+ this.session.turn.sourceFingerprint = fingerprint;
188
+ this.session.turn.message = structuredClone(matches[0].message);
189
+ const calls = (this.session.turn.message.content ?? []).filter((b) => b.type === "toolCall");
190
+ const handoffs = calls.filter((b) => this.handoffOperation(b.name, b.arguments));
191
+ check(
192
+ !handoffs.length ||
193
+ (handoffs.length === 1 &&
194
+ this.handoffOperation(calls.at(-1)?.name, calls.at(-1)?.arguments) &&
195
+ calls.every((b) => this.handoffOperation(b.name, b.arguments) || b.name === "freeflow_project")),
196
+ "invalid_handoff_batch",
197
+ "A handoff must be last and cannot accompany ordinary task tools.",
198
+ );
199
+ check(
200
+ calls.some((b) => issued(b) && (nested || b.name === name)),
201
+ "call_not_in_batch",
202
+ );
203
+ }
204
+ preflight(event, ctx) {
205
+ this.session.ctx = ctx;
206
+ const name = event.toolName,
207
+ isRouting = ROUTING_TOOLS.includes(name),
208
+ nested = typeof event.parentToolCallId === "string";
209
+ if (isRouting && nested) return { block: true, reason: `Call ${name} directly, not from a script.` };
210
+ if (!this.session.supported()) return isRouting ? { block: true, reason: "Routing is unavailable." } : undefined;
211
+ try {
212
+ const state = this.session.stateData();
213
+ if (state.control === "manual")
214
+ return isRouting && name !== "freeflow_unit" ? { block: true, reason: "Manual control is active." } : undefined;
215
+ if (state.control !== "automatic")
216
+ return isRouting ? { block: true, reason: "Automatic routing is inactive." } : undefined;
217
+ this.batch(event.toolCallId, name, nested);
218
+ check(!this.session.error && !this.session.store?.blocked, "routing_blocked");
219
+ this.session.openTurn();
220
+ if (isWorkerProfile(this.session.turn?.profile) && !isRouting) {
221
+ const a = state.assignmentId ? state.assignments.get(state.assignmentId) : undefined;
222
+ const worker = a ? this.session.assignedWorker(state, a.id) : undefined;
223
+ const ordinary =
224
+ a?.state === "outstanding" &&
225
+ this.session.turn.basisUserEntryId === this.session.taskBasis(state, a.id) &&
226
+ state.profile === worker &&
227
+ this.session.turn.profile === worker;
228
+ const recovery =
229
+ a?.state === "returned" &&
230
+ this.session.turn.basisUserEntryId === this.session.workerBasis(state, a.id) &&
231
+ state.profile === worker &&
232
+ this.session.turn.profile === worker &&
233
+ this.recoveryReadAllowed(state, name, event.input);
234
+ check(ordinary || recovery, "worker_task_phase_ended");
235
+ }
236
+ return undefined;
237
+ } catch (error) {
238
+ return { block: true, reason: error instanceof Error ? error.message : String(error) };
239
+ }
240
+ }
241
+ }