@hassangameryt/freeflow 0.7.3 → 0.9.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 (426) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +3 -3
  5. package/.cursor-plugin/plugin.json +1 -1
  6. package/NOTICE +14 -0
  7. package/README.md +101 -159
  8. package/capabilities/cognitive-routing/SKILL.md +47 -210
  9. package/capabilities/cognitive-routing/references/coordinator.md +163 -0
  10. package/capabilities/cognitive-routing/references/worker.md +38 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +250 -0
  13. package/capabilities/messaging/SKILL.md +49 -0
  14. package/capabilities/task-memory/SKILL.md +87 -0
  15. package/command-surface.json +8 -52
  16. package/gemini-extension.json +1 -1
  17. package/hooks/claude/hooks.json +13 -3
  18. package/hooks/codex/hooks.json +2 -1
  19. package/hooks/shared/runtime-context.mjs +73 -83
  20. package/licenses/Apache-2.0.txt +55 -0
  21. package/package.json +42 -12
  22. package/pi-extension/README.md +41 -0
  23. package/pi-extension/dist/ask-user/ask.js +108 -0
  24. package/pi-extension/dist/ask-user/form.js +250 -0
  25. package/pi-extension/dist/ask-user/questions.js +66 -0
  26. package/pi-extension/dist/ask-user/tool.js +124 -0
  27. package/pi-extension/dist/cognitive-routing/assembler.js +417 -0
  28. package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
  29. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +8 -6
  30. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  31. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  32. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +107 -41
  33. package/pi-extension/dist/cognitive-routing/gate.js +256 -0
  34. package/pi-extension/dist/cognitive-routing/handlers.js +901 -0
  35. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  36. package/pi-extension/dist/cognitive-routing/model-control.js +476 -0
  37. package/pi-extension/dist/cognitive-routing/projection.js +520 -0
  38. package/pi-extension/dist/cognitive-routing/provenance.js +83 -0
  39. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +15 -15
  40. package/pi-extension/dist/cognitive-routing/runtime.js +398 -0
  41. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +47 -11
  42. package/pi-extension/dist/cognitive-routing/session.js +223 -0
  43. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +126 -87
  44. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +55 -386
  45. package/pi-extension/dist/cognitive-routing/status.js +81 -0
  46. package/pi-extension/dist/cognitive-routing/tools.js +74 -0
  47. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +14 -11
  48. package/pi-extension/dist/compaction/carry.js +205 -0
  49. package/pi-extension/dist/compaction/config.js +24 -0
  50. package/pi-extension/dist/compaction/controller.js +494 -0
  51. package/pi-extension/dist/compaction/harness.js +93 -0
  52. package/pi-extension/dist/compaction/results.js +159 -0
  53. package/pi-extension/dist/compaction/thresholds.js +35 -0
  54. package/pi-extension/dist/compaction/tool.js +57 -0
  55. package/pi-extension/dist/host/branch.js +13 -0
  56. package/pi-extension/dist/host/canonical.js +12 -0
  57. package/pi-extension/dist/host/catalog.js +62 -0
  58. package/pi-extension/dist/host/config-validation.js +233 -0
  59. package/pi-extension/dist/host/config.js +361 -0
  60. package/pi-extension/dist/host/inbound.js +142 -0
  61. package/pi-extension/dist/host/projection-tags.js +60 -0
  62. package/pi-extension/dist/host/prompts.js +93 -0
  63. package/pi-extension/dist/{session-sources → host}/read-only-session.js +117 -1
  64. package/pi-extension/dist/{runtime → host}/request-history.js +125 -39
  65. package/pi-extension/dist/host/rows.js +25 -0
  66. package/pi-extension/dist/host/runtime-state.js +204 -0
  67. package/pi-extension/dist/host/section-keeper.js +85 -0
  68. package/pi-extension/dist/host/settings/freeflow-command.js +2323 -0
  69. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +205 -73
  70. package/pi-extension/dist/host/staging.js +28 -0
  71. package/pi-extension/dist/host/status.js +115 -0
  72. package/pi-extension/dist/index.js +1004 -188
  73. package/pi-extension/dist/messaging/config.js +27 -0
  74. package/pi-extension/dist/messaging/messaging.js +398 -0
  75. package/pi-extension/dist/messaging/registry.js +57 -0
  76. package/pi-extension/dist/messaging/render.js +90 -0
  77. package/pi-extension/dist/messaging/tools.js +109 -0
  78. package/pi-extension/dist/messaging/transport.js +101 -0
  79. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  80. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  81. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  82. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  83. package/pi-extension/dist/provider-support/cache/keepalive.js +222 -0
  84. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  85. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  86. package/pi-extension/dist/provider-support/effort.js +13 -0
  87. package/pi-extension/dist/provider-support/index.js +44 -3
  88. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  89. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  90. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  91. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  92. package/pi-extension/dist/provider-support/routes.js +17 -0
  93. package/pi-extension/dist/task-memory/config.js +49 -0
  94. package/pi-extension/dist/task-memory/registry.js +119 -0
  95. package/pi-extension/dist/task-memory/render.js +107 -0
  96. package/pi-extension/dist/task-memory/runtime.js +994 -0
  97. package/pi-extension/dist/task-memory/tool.js +233 -0
  98. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  99. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  100. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  101. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  102. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  103. package/pi-extension/dist/tool-execution/background.js +430 -0
  104. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  105. package/pi-extension/dist/tool-execution/config.js +33 -0
  106. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  107. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  108. package/pi-extension/dist/tool-execution/file-tracking.js +163 -0
  109. package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
  110. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  111. package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
  112. package/pi-extension/dist/tool-execution/tools.js +16 -0
  113. package/pi-extension/freeflow/README.md +9 -0
  114. package/runtime/prompts/cognitive-routing.md +4 -4
  115. package/runtime/prompts/core.md +29 -72
  116. package/runtime/prompts/interaction-contract.md +18 -8
  117. package/runtime/prompts/tool-execution.md +25 -0
  118. package/runtime/prompts/working-method.md +25 -0
  119. package/skills/deliver-work/SKILL.md +76 -0
  120. package/skills/deliver-work/references/branch-closeout.md +134 -0
  121. package/skills/deliver-work/references/commit.md +83 -0
  122. package/skills/deliver-work/references/launch.md +94 -0
  123. package/skills/deliver-work/references/release.md +85 -0
  124. package/skills/deliver-work/scripts/commit-state.mjs +139 -0
  125. package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
  126. package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
  127. package/skills/deliver-work/scripts/push-route.mjs +122 -0
  128. package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
  129. package/skills/discuss-work/SKILL.md +148 -0
  130. package/skills/{discuss → discuss-work}/references/discussion-continuity.md +13 -50
  131. package/skills/discuss-work/references/learning-action.md +36 -0
  132. package/skills/discuss-work/scripts/choices.mjs +101 -0
  133. package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
  134. package/skills/evaluate-behavior/SKILL.md +107 -0
  135. package/skills/evaluate-behavior/references/authoring.md +381 -0
  136. package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
  137. package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
  138. package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
  139. package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
  140. package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
  141. package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
  142. package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
  143. package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
  144. package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
  145. package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
  146. package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
  147. package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
  148. package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
  149. package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
  150. package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
  151. package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
  152. package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
  153. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
  154. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
  155. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
  156. package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
  157. package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
  158. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
  159. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
  160. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
  161. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
  162. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
  163. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
  164. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
  165. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
  166. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
  167. package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
  168. package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
  169. package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
  170. package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
  171. package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
  172. package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
  173. package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
  174. package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
  175. package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
  176. package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
  177. package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
  178. package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
  179. package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
  180. package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
  181. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
  182. package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
  183. package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
  184. package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
  185. package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
  186. package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
  187. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +121 -14
  188. package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
  189. package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
  190. package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
  191. package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
  192. package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
  193. package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
  194. package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +239 -262
  195. package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
  196. package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
  197. package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
  198. package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
  199. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +218 -11
  200. package/skills/evaluate-behavior/scripts/pi-command-tool.mjs +159 -0
  201. package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
  202. package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
  203. package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
  204. package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
  205. package/skills/execute-work/SKILL.md +96 -72
  206. package/skills/execute-work/references/code-practices.md +27 -49
  207. package/skills/execute-work/references/design.md +271 -0
  208. package/skills/execute-work/references/diagnose.md +117 -0
  209. package/skills/execute-work/references/migration.md +108 -0
  210. package/skills/execute-work/references/simplify.md +65 -0
  211. package/skills/execute-work/scripts/self-review.mjs +422 -0
  212. package/skills/execute-work/scripts/touch-map.mjs +290 -0
  213. package/skills/review-work/SKILL.md +94 -64
  214. package/skills/review-work/references/adjudication.md +94 -0
  215. package/skills/review-work/references/artifact.md +198 -0
  216. package/skills/review-work/references/evals-and-judges.md +66 -0
  217. package/skills/review-work/references/work.md +177 -0
  218. package/skills/review-work/scripts/check-brief.mjs +128 -0
  219. package/skills/review-work/scripts/review-packet.mjs +233 -0
  220. package/skills/setup-freeflow/SKILL.md +69 -16
  221. package/skills/track-work/SKILL.md +135 -151
  222. package/skills/track-work/references/handoff.md +190 -0
  223. package/skills/track-work/references/projects.md +51 -0
  224. package/skills/track-work/references/working-record-format.md +201 -344
  225. package/skills/track-work/scripts/lib/document.mjs +79 -19
  226. package/skills/track-work/scripts/lib/format.mjs +18 -1
  227. package/skills/track-work/scripts/lib/recover.mjs +254 -0
  228. package/skills/track-work/scripts/lib/registry.mjs +656 -0
  229. package/skills/track-work/scripts/lib/search.mjs +40 -0
  230. package/skills/track-work/scripts/lib/store.mjs +53 -21
  231. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  232. package/skills/track-work/scripts/lib/views.mjs +50 -1
  233. package/skills/track-work/scripts/working-record.mjs +263 -22
  234. package/skills/verify-work/SKILL.md +64 -47
  235. package/skills/verify-work/references/feature-map.md +120 -0
  236. package/skills/verify-work/references/runtime-evidence.md +203 -0
  237. package/skills/verify-work/references/test-design.md +27 -6
  238. package/skills/verify-work/scripts/test-changes.mjs +246 -0
  239. package/skills/write-docs/SKILL.md +171 -0
  240. package/skills/write-docs/references/documentation-shapes.md +184 -0
  241. package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
  242. package/skills/write-docs/scripts/artifact-check.mjs +148 -0
  243. package/skills/write-docs/scripts/docs-check.mjs +229 -0
  244. package/skills/write-docs/scripts/plan-lint.mjs +123 -0
  245. package/skills/write-skill/SKILL.md +165 -165
  246. package/skills/write-skill/references/activation-and-wording.md +143 -0
  247. package/skills/write-skill/references/placement-and-dependencies.md +154 -0
  248. package/skills/write-skill/scripts/lib/compare.mjs +701 -0
  249. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  250. package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
  251. package/skills/write-skill/scripts/skill-author.mjs +67 -8
  252. package/capabilities/context-virtualization/SKILL.md +0 -103
  253. package/capabilities/conversation-history/SKILL.md +0 -53
  254. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  255. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  256. package/pi-extension/dist/cognitive-routing-v2/projection.js +0 -330
  257. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  258. package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -60
  259. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  260. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  261. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  262. package/pi-extension/dist/context-virtualization/state.js +0 -57
  263. package/pi-extension/dist/context-virtualization/types.js +0 -10
  264. package/pi-extension/dist/conversation-history/passages.js +0 -66
  265. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  266. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  267. package/pi-extension/dist/conversation-history/search.js +0 -56
  268. package/pi-extension/dist/conversation-history/types.js +0 -1
  269. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  270. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  271. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  272. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  273. package/pi-extension/dist/freeflow-context/types.js +0 -12
  274. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  275. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  276. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  277. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  278. package/pi-extension/dist/session-sources/provenance.js +0 -32
  279. package/pi-extension/dist/settings/settings-ui.js +0 -1360
  280. package/pi-extension/dist/ui/text-component.js +0 -57
  281. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  282. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  283. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  284. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  285. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  286. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  287. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  288. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  289. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  290. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  291. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  292. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  293. package/pi-extension/src/context-virtualization/state.ts +0 -79
  294. package/pi-extension/src/context-virtualization/types.ts +0 -92
  295. package/pi-extension/src/conversation-history/passages.ts +0 -81
  296. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  297. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  298. package/pi-extension/src/conversation-history/search.ts +0 -79
  299. package/pi-extension/src/conversation-history/types.ts +0 -31
  300. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  301. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  302. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  303. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  304. package/pi-extension/src/freeflow-context/types.ts +0 -70
  305. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  306. package/pi-extension/src/index.ts +0 -390
  307. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  308. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  309. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  310. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  311. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  312. package/pi-extension/src/provider-support/index.ts +0 -5
  313. package/pi-extension/src/runtime/request-history.ts +0 -243
  314. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  315. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  316. package/pi-extension/src/session-sources/events.ts +0 -230
  317. package/pi-extension/src/session-sources/provenance.ts +0 -39
  318. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  319. package/pi-extension/src/session-sources/sources.ts +0 -334
  320. package/pi-extension/src/settings/settings-tui.ts +0 -501
  321. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  322. package/pi-extension/src/ui/text-component.ts +0 -66
  323. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  324. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  325. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  326. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  327. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  328. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  329. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  330. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  331. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  332. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  333. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  334. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  335. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  336. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  337. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  338. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  339. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  340. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  341. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  342. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  343. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  344. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  345. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  346. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  347. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  348. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  349. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  350. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  351. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  352. package/pi-extension/tests/provider-support/native.test.js +0 -183
  353. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  354. package/pi-extension/tsconfig.json +0 -16
  355. package/plugin.json +0 -33
  356. package/runtime/prompts/context-virtualization.md +0 -3
  357. package/runtime/prompts/conversation-history.md +0 -3
  358. package/skills/action-selection/SKILL.md +0 -100
  359. package/skills/action-selection/references/trajectory-stalls.md +0 -71
  360. package/skills/bypass/SKILL.md +0 -153
  361. package/skills/commit-work/SKILL.md +0 -145
  362. package/skills/commit-work/references/git-state-edges.md +0 -94
  363. package/skills/commit-work/references/simple-push.md +0 -92
  364. package/skills/commit-work/references/staging-decisions.md +0 -106
  365. package/skills/decision-gate/SKILL.md +0 -106
  366. package/skills/design-for-depth/SKILL.md +0 -117
  367. package/skills/design-for-depth/references/design-pressure-signals.md +0 -182
  368. package/skills/design-for-depth/references/implementation-decisions.md +0 -76
  369. package/skills/design-for-depth/references/interface-design-loop.md +0 -136
  370. package/skills/design-for-depth/references/module-and-dependency-design.md +0 -147
  371. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  372. package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -136
  373. package/skills/diagnose-failure/SKILL.md +0 -125
  374. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -48
  375. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
  376. package/skills/discuss/SKILL.md +0 -116
  377. package/skills/evaluate-skill/SKILL.md +0 -219
  378. package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -385
  379. package/skills/evaluate-skill/references/evaluation-design.md +0 -112
  380. package/skills/evaluate-skill/references/execution-and-evidence.md +0 -90
  381. package/skills/evaluate-skill/references/review-and-revision.md +0 -92
  382. package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -407
  383. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
  384. package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
  385. package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -368
  386. package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -164
  387. package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -102
  388. package/skills/execute-work/references/execute-work-edges.md +0 -68
  389. package/skills/finish-branch/SKILL.md +0 -117
  390. package/skills/finish-branch/references/integration-options.md +0 -80
  391. package/skills/handoff/SKILL.md +0 -82
  392. package/skills/handoff/references/handoff-templates.md +0 -150
  393. package/skills/launch-work/SKILL.md +0 -116
  394. package/skills/launch-work/references/launch-readiness.md +0 -85
  395. package/skills/migration-work/SKILL.md +0 -115
  396. package/skills/migration-work/references/migration-lifecycle.md +0 -82
  397. package/skills/release-work/SKILL.md +0 -118
  398. package/skills/release-work/references/release-evidence.md +0 -78
  399. package/skills/review-artifact/SKILL.md +0 -142
  400. package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
  401. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
  402. package/skills/review-work/references/adjudicate-work-review.md +0 -97
  403. package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
  404. package/skills/review-work/references/security-risk-lens.md +0 -69
  405. package/skills/setup-freeflow/references/activation-contract.md +0 -109
  406. package/skills/setup-freeflow/references/host-setup.md +0 -67
  407. package/skills/simplify-code/SKILL.md +0 -85
  408. package/skills/simplify-code/references/simplification-patterns.md +0 -70
  409. package/skills/verify-work/references/browser-runtime-evidence.md +0 -55
  410. package/skills/verify-work/references/integration-evidence.md +0 -47
  411. package/skills/verify-work/references/performance-evidence.md +0 -57
  412. package/skills/workflow/SKILL.md +0 -127
  413. package/skills/workflow/references/domain-skill-composition.md +0 -43
  414. package/skills/write-plan/SKILL.md +0 -92
  415. package/skills/write-plan/references/plan-shapes.md +0 -156
  416. package/skills/write-skill/references/activation-boundaries.md +0 -104
  417. package/skills/write-skill/references/agent-first-instructions.md +0 -121
  418. package/skills/write-skill/references/progressive-disclosure.md +0 -127
  419. package/skills/write-skill/references/skill-dependency-graphs.md +0 -190
  420. package/skills/write-spec/SKILL.md +0 -92
  421. package/skills/write-spec/references/artifact-standards.md +0 -86
  422. package/skills/write-spec/references/decision-records.md +0 -78
  423. package/skills/write-spec/references/spec-shapes.md +0 -143
  424. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  425. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
  426. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
@@ -0,0 +1,309 @@
1
+ // The confinement rules every host's guard enforces: which tools a subject may call, where reads and writes may
2
+ // go, and how bash reaches its container. The evaluator passes them to the subject's host process in SKILL_EVAL_*
3
+ // variables; Pi's guard extension (pi-guard.mjs) and Claude Code's PreToolUse hook (claude-guard.mjs) both decide
4
+ // with `decideToolCall`, so a rule holds the same way on every host. Tool names here are the evaluator's own
5
+ // (read, write, edit, ls, find, grep, bash) plus Freeflow's Tool Execution tools on Pi (bash_background, monitor,
6
+ // stop_background, apply_patch); each guard maps its host's names onto them.
7
+
8
+ import { createHash } from "node:crypto";
9
+ import { lstatSync, readFileSync, realpathSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import path from "node:path";
12
+
13
+ const PATH_TOOLS = new Set(["read", "write", "edit"]);
14
+ // Tools whose `command` runs in a shell: Pi's bash, and Freeflow's background commands and monitors, which run their
15
+ // command with Pi's shell on the host unless the guard routes it into the subject's container.
16
+ export const SHELL_TOOLS = new Set(["bash", "bash_background", "monitor"]);
17
+ // Background commands outlive the call that starts them, so only a container, which the evaluator removes when the
18
+ // subject ends, can hold them; a shell isolation extension confines only the bash tool it wraps.
19
+ const CONTAINER_ONLY_TOOLS = new Set(["bash_background", "monitor"]);
20
+ // Freeflow's patch tool writes, moves and deletes every file its patch names (and reads the files it updates).
21
+ const PATCH_TOOL = "apply_patch";
22
+ const PATCH_HEADERS = ["*** Add File: ", "*** Delete File: ", "*** Update File: ", "*** Move to: "];
23
+ // Set by pi-container-route.mjs when it loads; Pi's guard refuses a container shell call without it.
24
+ export const CONTAINER_ROUTE = Symbol.for("skill-eval.container-route");
25
+ // Listing and search tools take an optional path that defaults to the working directory; an explicit path is
26
+ // confined to the readable roots like a read.
27
+ const LISTING_TOOLS = new Set(["ls", "find", "grep"]);
28
+
29
+ export function parseStringArray(value) {
30
+ try {
31
+ const parsed = JSON.parse(value ?? "[]");
32
+ return Array.isArray(parsed) && parsed.every((entry) => typeof entry === "string") ? parsed : [];
33
+ } catch {
34
+ return [];
35
+ }
36
+ }
37
+
38
+ // A declared shell (SKILL_EVAL_SHELL, set by the evaluator only for arms whose shell isolation it verified)
39
+ // is the only way bash is allowed. Its optional `workspace` is the path where the isolation shows the workspace.
40
+ export function parseShell(value) {
41
+ if (!value) return null;
42
+ try {
43
+ const parsed = JSON.parse(value);
44
+ return parsed !== null && typeof parsed === "object" && typeof parsed.isolation === "string" ? parsed : null;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ // A path under the isolation's workspace mount (such as /workspace/src/a.js) names the same file in the host
51
+ // workspace, so it is checked there; every other path is checked as a host path.
52
+ function mapGuestPath(requested, shell, writableRoot) {
53
+ const mount = shell?.workspace;
54
+ if (typeof mount !== "string" || !requested.startsWith("/")) return requested;
55
+ const normalized = path.posix.normalize(requested);
56
+ if (normalized === mount) return writableRoot;
57
+ if (!normalized.startsWith(`${mount}/`)) return requested;
58
+ return path.join(writableRoot, ...normalized.slice(mount.length + 1).split("/"));
59
+ }
60
+
61
+ // The running container a container environment's bash is routed into: { name, host, workdir }.
62
+ function parseContainer(value) {
63
+ if (!value) return null;
64
+ try {
65
+ const parsed = JSON.parse(value);
66
+ return typeof parsed?.name === "string" && typeof parsed.workdir === "string" ? parsed : null;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ function shellQuote(value) {
73
+ return `'${String(value).replaceAll("'", `'\\''`)}'`;
74
+ }
75
+
76
+ // Runs in the container around a shell tool's command, so the command's whole process group ends when the docker
77
+ // client that started it does. The client's stdin (see containerCommand) stays open while it lives and never sends
78
+ // anything; killing the client closes it, and the watcher reading it then kills the command's group. Without this, a
79
+ // command Pi stops (a timeout, an abort, stop_background, the session ending) would keep running in the container:
80
+ // docker exec does not pass signals on. The command itself gets no stdin, as on the host, runs in its own session when
81
+ // the image has setsid, and its exit status is the wrapper's.
82
+ const CONTAINER_LIFETIME = [
83
+ "exec 3<&0",
84
+ 'if command -v setsid >/dev/null 2>&1; then setsid sh -c "$1" </dev/null & else sh -c "$1" </dev/null & fi',
85
+ "p=$!",
86
+ '{ while read -r _; do :; done; kill -s KILL -- "-$p" 2>/dev/null || kill -s KILL "$p" 2>/dev/null; } <&3 >/dev/null 2>&1 &',
87
+ "w=$!",
88
+ "exec 3<&-",
89
+ 'wait "$p"',
90
+ "s=$?",
91
+ 'kill "$w" 2>/dev/null',
92
+ 'exit "$s"',
93
+ ].join("\n");
94
+
95
+ /**
96
+ * The host command that runs `command` in the subject's container: the docker client, given a stdin that stays open
97
+ * for as long as it runs (a FIFO opened read-write, which never ends and is removed at once), runs the lifetime
98
+ * wrapper, which runs the command at the container's working directory.
99
+ */
100
+ export function containerCommand(target, command) {
101
+ const host = typeof target.host === "string" && target.host !== "" ? `--host ${shellQuote(target.host)} ` : "";
102
+ const exec = `docker ${host}exec -i --workdir ${shellQuote(target.workdir)} ${shellQuote(target.name)} sh -c ${shellQuote(CONTAINER_LIFETIME)} sh ${shellQuote(command)}`;
103
+ return `skill_eval_in=$(mktemp -d) && mkfifo "$skill_eval_in/in" && { rm -rf "$skill_eval_in"; ${exec}; } <>"$skill_eval_in/in"`;
104
+ }
105
+
106
+ /**
107
+ * A path argument as the host's tools take it: Pi and Freeflow drop a leading `@` and expand a leading `~` to the home
108
+ * directory, so the guard checks the file the tool will actually use.
109
+ */
110
+ function hostToolPath(requested) {
111
+ const declared = requested.startsWith("@") ? requested.slice(1) : requested;
112
+ if (declared === "~") return homedir();
113
+ if (declared.startsWith("~/")) return `${homedir()}${declared.slice(1)}`;
114
+ return declared;
115
+ }
116
+
117
+ /**
118
+ * Every path a patch for Freeflow's apply_patch names, over-approximated: each line that is, once trimmed, or once
119
+ * trailing space is removed, a file header or a move target, split on the same line breaks as Freeflow's parser.
120
+ * Freeflow reads a header only from such a line, so no path it uses is missed; a context line that only looks like a
121
+ * header adds a path to check and can only refuse more.
122
+ */
123
+ export function patchPaths(patch) {
124
+ const paths = [];
125
+ for (const line of patch.split(/\r?\n/)) {
126
+ for (const candidate of new Set([line.trim(), line.trimEnd()])) {
127
+ const header = PATCH_HEADERS.find((marker) => candidate.startsWith(marker));
128
+ if (header !== undefined) paths.push(candidate.slice(header.length));
129
+ }
130
+ }
131
+ return [...new Set(paths)];
132
+ }
133
+
134
+ function isContained(root, target) {
135
+ const relative = path.relative(root, target);
136
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
137
+ }
138
+
139
+ function resolveWritablePath(cwd, requested) {
140
+ const absolute = path.resolve(cwd, requested);
141
+ /** @type {string[]} */
142
+ const missingSegments = [];
143
+ let candidate = absolute;
144
+ while (true) {
145
+ try {
146
+ return path.join(realpathSync(candidate), ...missingSegments);
147
+ } catch {
148
+ try {
149
+ lstatSync(candidate);
150
+ throw new Error(`Existing writable path cannot be resolved: ${requested}`);
151
+ } catch (error) {
152
+ if (error?.code !== "ENOENT" && error?.code !== "ENOTDIR") throw error;
153
+ }
154
+ const parent = path.dirname(candidate);
155
+ if (parent === candidate) throw new Error(`Cannot resolve writable path: ${requested}`);
156
+ missingSegments.unshift(path.basename(candidate));
157
+ candidate = parent;
158
+ }
159
+ }
160
+ }
161
+
162
+ export function escapeXml(value) {
163
+ const entities = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&apos;" };
164
+ return value.replace(/[&<>"']/g, (character) => entities[character]);
165
+ }
166
+
167
+ export function loadDeclaredContext(manifestPath) {
168
+ if (!manifestPath) return [];
169
+ let manifest;
170
+ try {
171
+ manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
172
+ } catch (error) {
173
+ throw new Error(
174
+ `Declared context manifest cannot be read: ${error instanceof Error ? error.message : String(error)}`,
175
+ );
176
+ }
177
+ if (manifest?.schema_version !== 1 || !Array.isArray(manifest.entries)) {
178
+ throw new Error("Declared context manifest must use schema_version 1 and contain entries");
179
+ }
180
+ for (const entry of manifest.entries) {
181
+ if (typeof entry?.declaredPath !== "string" || !Array.isArray(entry.files)) {
182
+ throw new Error("Declared context manifest entry is malformed");
183
+ }
184
+ for (const file of entry.files) {
185
+ if (
186
+ typeof file?.path !== "string" ||
187
+ typeof file.sha256 !== "string" ||
188
+ typeof file.content !== "string" ||
189
+ createHash("sha256").update(file.content).digest("hex") !== file.sha256
190
+ ) {
191
+ throw new Error(`Declared context file is malformed or changed: ${file?.path ?? "unknown"}`);
192
+ }
193
+ }
194
+ }
195
+ return manifest.entries;
196
+ }
197
+
198
+ export function renderDeclaredContext(declaredContext) {
199
+ const lines = ["<declared_context>"];
200
+ for (const entry of declaredContext) {
201
+ lines.push(` <context declared-path="${escapeXml(entry.declaredPath)}">`);
202
+ for (const file of entry.files) {
203
+ lines.push(` <file path="${escapeXml(file.path)}">\n${file.content}</file>`);
204
+ }
205
+ lines.push(" </context>");
206
+ }
207
+ lines.push("</declared_context>");
208
+ return lines.join("\n");
209
+ }
210
+
211
+ /**
212
+ * The guard's rules from the evaluator's variables: readable roots, the writable workspace, the declared tools, and
213
+ * the verified shell, if any.
214
+ *
215
+ * @param {Record<string, string | undefined>} environment
216
+ */
217
+ export function guardState(environment) {
218
+ const allowedRoots = parseStringArray(environment.SKILL_EVAL_ALLOWED_ROOTS).map((root) => realpathSync(root));
219
+ const writableRootValue = environment.SKILL_EVAL_WRITABLE_ROOT;
220
+ if (!writableRootValue) throw new Error("SKILL_EVAL_WRITABLE_ROOT is required");
221
+ return {
222
+ allowedRoots,
223
+ writableRoot: realpathSync(writableRootValue),
224
+ allowedTools: new Set(parseStringArray(environment.SKILL_EVAL_ALLOWED_TOOLS)),
225
+ shell: parseShell(environment.SKILL_EVAL_SHELL),
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Decides one tool call. `input.path` is the path argument of a path or listing tool, `input.command` bash's command.
231
+ * Returns `{ block: true, reason }` to refuse the call, `{ command }` when bash must run that command instead (its
232
+ * container), or undefined to let the call run as it is.
233
+ *
234
+ * @param {ReturnType<typeof guardState>} state
235
+ * @param {string} toolName the evaluator's tool name
236
+ * @param {any} input
237
+ * @param {string} cwd the subject's working directory
238
+ * @param {Record<string, string | undefined>} environment
239
+ */
240
+ export function decideToolCall(state, toolName, input, cwd, environment) {
241
+ const { allowedRoots, writableRoot, allowedTools, shell } = state;
242
+ if (!allowedTools.has(toolName)) {
243
+ return { block: true, reason: `Tool is not declared for this evaluation: ${toolName}` };
244
+ }
245
+ if (SHELL_TOOLS.has(toolName)) {
246
+ if (shell === null) {
247
+ return { block: true, reason: `${toolName} runs only in a declared shell isolation for this evaluation` };
248
+ }
249
+ if (CONTAINER_ONLY_TOOLS.has(toolName) && !shell.container) {
250
+ return { block: true, reason: `${toolName} runs only in a container environment for this evaluation` };
251
+ }
252
+ }
253
+ if (SHELL_TOOLS.has(toolName) && shell.container) {
254
+ const target = parseContainer(environment.SKILL_EVAL_CONTAINER);
255
+ if (target === null) return { block: true, reason: "the container for this evaluation is not running" };
256
+ if (typeof input?.command !== "string") return { block: true, reason: `${toolName} needs a command` };
257
+ // The host's shell then runs only the docker client; the command itself runs in the subject's container.
258
+ return { command: containerCommand(target, input.command) };
259
+ }
260
+ if (toolName === PATCH_TOOL) return decidePatch(writableRoot, input, cwd);
261
+ const listing = LISTING_TOOLS.has(toolName);
262
+ if (!PATH_TOOLS.has(toolName) && !listing) return undefined;
263
+ if (listing && input?.path === undefined) {
264
+ const canonicalCwd = realpathSync(cwd);
265
+ return allowedRoots.some((root) => isContained(root, canonicalCwd))
266
+ ? undefined
267
+ : { block: true, reason: `${toolName} needs a path inside the evaluation environment` };
268
+ }
269
+ if (typeof input?.path !== "string") {
270
+ return { block: true, reason: `${toolName} requires a path inside the evaluation environment` };
271
+ }
272
+
273
+ const declared = input.path.startsWith("@") ? input.path.slice(1) : input.path;
274
+ const requested = mapGuestPath(hostToolPath(input.path), shell, writableRoot);
275
+ let canonical;
276
+ try {
277
+ canonical = toolName === "write" ? resolveWritablePath(cwd, requested) : realpathSync(path.resolve(cwd, requested));
278
+ } catch {
279
+ return {
280
+ block: true,
281
+ reason: `${toolName} path cannot be resolved inside the evaluation environment: ${declared}`,
282
+ };
283
+ }
284
+ const roots = toolName === "read" || listing ? allowedRoots : [writableRoot];
285
+ if (!roots.some((root) => isContained(root, canonical))) {
286
+ return { block: true, reason: `${toolName} path escapes the evaluation environment: ${declared}` };
287
+ }
288
+ return undefined;
289
+ }
290
+
291
+ /** apply_patch writes only inside the workspace: every path its patch names must resolve there, as a write's would. */
292
+ function decidePatch(writableRoot, input, cwd) {
293
+ if (typeof input?.input !== "string") return { block: true, reason: "apply_patch needs a patch" };
294
+ for (const declared of patchPaths(input.input)) {
295
+ let canonical;
296
+ try {
297
+ canonical = resolveWritablePath(cwd, hostToolPath(declared));
298
+ } catch {
299
+ return {
300
+ block: true,
301
+ reason: `apply_patch path cannot be resolved inside the evaluation environment: ${declared}`,
302
+ };
303
+ }
304
+ if (!isContained(writableRoot, canonical)) {
305
+ return { block: true, reason: `apply_patch path escapes the evaluation environment: ${declared}` };
306
+ }
307
+ }
308
+ return undefined;
309
+ }
@@ -0,0 +1,90 @@
1
+ // Agreement between raters on nominal answers: Cohen's kappa for two raters, Krippendorff's alpha for more.
2
+ // Answers are compared as categories ("yes", "no", "unknown", 0, 1, 2); `null` or `undefined` is a missing answer.
3
+
4
+ const missing = (value) => value === null || value === undefined;
5
+ const key = (value) => JSON.stringify(value);
6
+
7
+ /**
8
+ * Cohen's kappa for two raters over the items both answered.
9
+ * `kappa` is null when chance agreement is complete (every answer in one category), where kappa is undefined.
10
+ *
11
+ * @param {Array<[unknown, unknown]>} pairs
12
+ * @returns {{method: "cohen-kappa", items: number, observed: number | null, expected: number | null, kappa: number | null}}
13
+ */
14
+ export function cohenKappa(pairs) {
15
+ const rated = pairs.filter(([first, second]) => !missing(first) && !missing(second));
16
+ const items = rated.length;
17
+ if (items === 0) return { method: "cohen-kappa", items, observed: null, expected: null, kappa: null };
18
+ const first = new Map();
19
+ const second = new Map();
20
+ let agreed = 0;
21
+ for (const [left, right] of rated) {
22
+ if (key(left) === key(right)) agreed += 1;
23
+ first.set(key(left), (first.get(key(left)) ?? 0) + 1);
24
+ second.set(key(right), (second.get(key(right)) ?? 0) + 1);
25
+ }
26
+ const observed = agreed / items;
27
+ let expected = 0;
28
+ for (const [category, count] of first) expected += (count / items) * ((second.get(category) ?? 0) / items);
29
+ const kappa = expected === 1 ? null : (observed - expected) / (1 - expected);
30
+ return { method: "cohen-kappa", items, observed, expected, kappa };
31
+ }
32
+
33
+ /**
34
+ * Krippendorff's alpha for nominal data. Each unit is one item's answers, one entry per rater, with missing
35
+ * answers allowed; a unit with fewer than two answers is not pairable and is left out.
36
+ * `alpha` is null when there is no expected disagreement (every pairable answer in one category).
37
+ *
38
+ * @param {Array<Array<unknown>>} units
39
+ * @returns {{method: "krippendorff-alpha", units: number, pairable: number, alpha: number | null}}
40
+ */
41
+ export function krippendorffAlpha(units) {
42
+ // Coincidence counts o[c][k], built from every ordered pair of answers within a unit, weighted 1/(m_u - 1).
43
+ const coincidences = new Map();
44
+ let pairableUnits = 0;
45
+ for (const unit of units) {
46
+ const values = unit.filter((value) => !missing(value)).map(key);
47
+ if (values.length < 2) continue;
48
+ pairableUnits += 1;
49
+ const weight = 1 / (values.length - 1);
50
+ for (let left = 0; left < values.length; left += 1) {
51
+ for (let right = 0; right < values.length; right += 1) {
52
+ if (left === right) continue;
53
+ const row = coincidences.get(values[left]) ?? new Map();
54
+ row.set(values[right], (row.get(values[right]) ?? 0) + weight);
55
+ coincidences.set(values[left], row);
56
+ }
57
+ }
58
+ }
59
+ const totals = new Map();
60
+ let total = 0;
61
+ let disagreement = 0;
62
+ for (const [category, row] of coincidences) {
63
+ for (const [other, count] of row) {
64
+ totals.set(category, (totals.get(category) ?? 0) + count);
65
+ total += count;
66
+ if (category !== other) disagreement += count;
67
+ }
68
+ }
69
+ if (total <= 1) return { method: "krippendorff-alpha", units: units.length, pairable: pairableUnits, alpha: null };
70
+ let expected = 0;
71
+ for (const [category, count] of totals) {
72
+ for (const [other, otherCount] of totals) if (category !== other) expected += count * otherCount;
73
+ }
74
+ // alpha = 1 - D_o / D_e, with D_o = disagreement / n and D_e = expected / (n (n - 1)).
75
+ const alpha = expected === 0 ? null : 1 - ((total - 1) * disagreement) / expected;
76
+ return { method: "krippendorff-alpha", units: units.length, pairable: pairableUnits, alpha };
77
+ }
78
+
79
+ /**
80
+ * Agreement between raters, by the method their number calls for: Cohen's kappa for two, Krippendorff's alpha
81
+ * for three or more. Each unit holds one answer per rater, in a fixed rater order.
82
+ *
83
+ * @param {Array<Array<unknown>>} units
84
+ */
85
+ export function raterAgreement(units) {
86
+ const raters = Math.max(0, ...units.map((unit) => unit.length));
87
+ if (raters < 2) return null;
88
+ if (raters === 2) return cohenKappa(units.map((unit) => [unit[0], unit[1]]));
89
+ return krippendorffAlpha(units);
90
+ }
@@ -0,0 +1,55 @@
1
+ // Arm names and the reference arm, shared by the loader, runner, graders, aggregate, view and review.
2
+ // A group's arms are its loaded `variants`, reference first; evidence written before named arms, and test
3
+ // fixtures without them, are read as the two classic arms.
4
+
5
+ export const DEFAULT_ARMS = Object.freeze(["baseline", "candidate"]);
6
+ export const DEFAULT_REFERENCE = "baseline";
7
+ export const ARM_NAME = /^[a-z][a-z0-9-]{0,31}$/;
8
+
9
+ function keysOf(value) {
10
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? Object.keys(value) : [];
11
+ }
12
+
13
+ /** The arms of a loaded group (or a stored definition), reference first. */
14
+ export function armNames(group) {
15
+ const names = keysOf(group?.variants);
16
+ return names.length > 0 ? names : [...DEFAULT_ARMS];
17
+ }
18
+
19
+ export function referenceArm(group) {
20
+ return typeof group?.reference === "string" ? group.reference : DEFAULT_REFERENCE;
21
+ }
22
+
23
+ /** The arms of a stored group: its summary entry's run states, then its definition, then the classic pair. */
24
+ export function storedArms(summaryGroup, definition) {
25
+ const fromSummary = keysOf(summaryGroup?.variants);
26
+ return fromSummary.length > 0 ? fromSummary : armNames(definition);
27
+ }
28
+
29
+ /**
30
+ * Exactly `baseline` and `candidate` with `baseline` as the reference: such a group keeps every output shape it
31
+ * had before named arms, so existing readers keep working. Any other group uses the arm-keyed shapes.
32
+ */
33
+ export function isClassicPair(arms, reference = DEFAULT_REFERENCE) {
34
+ return (
35
+ reference === DEFAULT_REFERENCE &&
36
+ arms.length === 2 &&
37
+ arms.includes(DEFAULT_ARMS[0]) &&
38
+ arms.includes(DEFAULT_ARMS[1])
39
+ );
40
+ }
41
+
42
+ /**
43
+ * One grade comparison in the arm-keyed form, whichever shape it was stored in: `{ reference, arms: { <arm>:
44
+ * { check, state } }, transitions: { <non-reference arm>: transition } }`.
45
+ */
46
+ export function comparisonArms(comparison) {
47
+ if (comparison?.arms !== undefined) {
48
+ return { reference: comparison.reference, arms: comparison.arms, transitions: comparison.transitions ?? {} };
49
+ }
50
+ return {
51
+ reference: DEFAULT_REFERENCE,
52
+ arms: { baseline: comparison?.baseline, candidate: comparison?.candidate },
53
+ transitions: { candidate: comparison?.transition },
54
+ };
55
+ }
@@ -0,0 +1,87 @@
1
+ // The cost ceiling (`--max-cost`), shared by every subject of a run or of all its trials. Reserve-then-charge:
2
+ // a subject reserves the mean cost of the subjects finished so far (zero before any finished) before it starts,
3
+ // and is charged its host-reported cost when it ends. A subject may not start once the spent cost plus what the
4
+ // running subjects reserved reaches the ceiling, so the overshoot is bounded by the running set. A subject that
5
+ // is running when the ceiling is reached completes and is charged.
6
+
7
+ /** @param {number | null | undefined} maxCost */
8
+ export function createBudget(maxCost) {
9
+ if (maxCost === null || maxCost === undefined) return null;
10
+ return { maxCost, spent: 0, exhausted: false, reserved: 0, finished: 0 };
11
+ }
12
+
13
+ /**
14
+ * Reserves an estimate for one subject, or returns null (and marks the budget exhausted) when it may not start.
15
+ *
16
+ * @returns {{amount: number} | null}
17
+ */
18
+ export function reserve(budget) {
19
+ if (budget === null || budget === undefined) return { amount: 0 };
20
+ budget.reserved ??= 0;
21
+ budget.finished ??= 0;
22
+ if (budget.exhausted || budget.spent + budget.reserved >= budget.maxCost) {
23
+ budget.exhausted = true;
24
+ return null;
25
+ }
26
+ const amount = budget.finished > 0 ? budget.spent / budget.finished : 0;
27
+ budget.reserved += amount;
28
+ return { amount };
29
+ }
30
+
31
+ /** Releases a subject's reservation and charges what it actually cost. */
32
+ export function charge(budget, reservation, cost) {
33
+ if (budget === null || budget === undefined) return;
34
+ budget.reserved = Math.max(0, (budget.reserved ?? 0) - (reservation?.amount ?? 0));
35
+ budget.spent += cost;
36
+ budget.finished = (budget.finished ?? 0) + 1;
37
+ if (budget.spent >= budget.maxCost) budget.exhausted = true;
38
+ }
39
+
40
+ /** The budget as evidence: the ceiling, what was spent, whether it ran out, and how far spending passed it. */
41
+ export function budgetEvidence(budget) {
42
+ const spent = Number(budget.spent.toFixed(10));
43
+ return {
44
+ maxCost: budget.maxCost,
45
+ spent,
46
+ exhausted: budget.exhausted,
47
+ overshoot: Number(Math.max(0, budget.spent - budget.maxCost).toFixed(10)),
48
+ };
49
+ }
50
+
51
+ /**
52
+ * The budget as a trial's evidence. The budget belongs to the whole run, so its total is named as the run's: what
53
+ * every trial had spent when this one ended. The trial's own spend is its summary's `cost`.
54
+ */
55
+ export function runBudgetEvidence(budget) {
56
+ const { spent, ...rest } = budgetEvidence(budget);
57
+ return { maxCost: rest.maxCost, runTotal: spent, exhausted: rest.exhausted, overshoot: rest.overshoot };
58
+ }
59
+
60
+ /**
61
+ * A run's host-reported cost, or null when it is not a measure: no cost at all, or a cost of 0 for a run that used
62
+ * tokens. Pi reports 0 for a model it has no price for, so real work would otherwise read as free; a run that used
63
+ * no tokens did cost nothing, and a subscription that reports a list price keeps it.
64
+ *
65
+ * @param {any} usage the run's usage, as hosts record it
66
+ * @returns {number | null}
67
+ */
68
+ export function measuredCost(usage) {
69
+ const total = usage?.cost?.total;
70
+ if (typeof total !== "number" || !Number.isFinite(total)) return null;
71
+ if (total === 0) {
72
+ const tokens = ["input", "output", "cacheRead", "cacheWrite"].reduce(
73
+ (sum, key) => sum + (Number.isFinite(usage?.[key]) ? usage[key] : 0),
74
+ 0,
75
+ );
76
+ if (tokens > 0) return null;
77
+ }
78
+ return total;
79
+ }
80
+
81
+ /**
82
+ * One invocation's own spend: the host-reported cost of the subjects it ran (reused runs cost nothing again), how
83
+ * many ran, and how many have no measured cost (`measuredCost`), so a missing figure is never read as free.
84
+ */
85
+ export function costEvidence(cost) {
86
+ return { spent: Number(cost.spent.toFixed(10)), subjects: cost.subjects, unmeasured: cost.unmeasured };
87
+ }