ultimate-pi 0.18.0 → 0.19.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 (314) hide show
  1. package/.agents/skills/harness-debate-plan/SKILL.md +1 -1
  2. package/.agents/skills/harness-decisions/SKILL.md +2 -3
  3. package/.agents/skills/harness-governor/SKILL.md +6 -5
  4. package/.agents/skills/harness-orchestration/SKILL.md +4 -4
  5. package/.agents/skills/harness-review/SKILL.md +7 -7
  6. package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
  7. package/.agents/skills/harness-steer/SKILL.md +1 -1
  8. package/.agents/skills/sentrux/SKILL.md +9 -9
  9. package/.pi/PACKAGING.md +4 -4
  10. package/.pi/SYSTEM.md +54 -120
  11. package/.pi/agents/harness/incident-recorder.md +0 -1
  12. package/.pi/agents/harness/planning/decompose.md +1 -3
  13. package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
  14. package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
  15. package/.pi/agents/harness/planning/hypothesis.md +0 -2
  16. package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
  17. package/.pi/agents/harness/planning/plan-adversary.md +0 -2
  18. package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
  19. package/.pi/agents/harness/planning/planning-context.md +0 -2
  20. package/.pi/agents/harness/planning/review-integrator.md +0 -2
  21. package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
  22. package/.pi/agents/harness/planning/stack-researcher.md +0 -2
  23. package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +0 -2
  24. package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
  25. package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
  26. package/.pi/agents/harness/{executor.md → running/executor.md} +0 -2
  27. package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
  28. package/.pi/agents/harness/sentrux-steward.md +0 -2
  29. package/.pi/agents/harness/trace-librarian.md +0 -1
  30. package/.pi/extensions/00-harness-project-control.ts +133 -0
  31. package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
  32. package/.pi/extensions/agt-kill-switch.ts +57 -0
  33. package/.pi/extensions/agt-prompt-guard.ts +32 -0
  34. package/.pi/extensions/budget-guard.ts +2 -0
  35. package/.pi/extensions/custom-footer.ts +46 -145
  36. package/.pi/extensions/custom-header.ts +1 -1
  37. package/.pi/extensions/custom-system-prompt.ts +1 -1
  38. package/.pi/extensions/debate-orchestrator.ts +7 -5
  39. package/.pi/extensions/harness-ask-user.ts +8 -8
  40. package/.pi/extensions/harness-debate-tools.ts +27 -43
  41. package/.pi/extensions/harness-lens.ts +94 -0
  42. package/.pi/extensions/harness-live-widget.ts +33 -2
  43. package/.pi/extensions/harness-plan-approval.ts +12 -12
  44. package/.pi/extensions/harness-run-context.ts +1214 -852
  45. package/.pi/extensions/harness-subagent-governance.ts +8 -0
  46. package/.pi/extensions/harness-subagent-submit.ts +36 -164
  47. package/.pi/extensions/harness-subagents.ts +4 -4
  48. package/.pi/extensions/harness-telemetry.ts +3 -1
  49. package/.pi/extensions/harness-web-tools.ts +3 -3
  50. package/.pi/extensions/observation-bus.ts +2 -0
  51. package/.pi/extensions/policy-gate.ts +27 -5
  52. package/.pi/extensions/review-integrity.ts +91 -10
  53. package/.pi/extensions/sentrux-rules-sync.ts +3 -1
  54. package/.pi/extensions/subagent-governance.ts +92 -0
  55. package/.pi/extensions/test-diff-integrity.ts +1 -0
  56. package/.pi/extensions/trace-recorder.ts +3 -1
  57. package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
  58. package/.pi/harness/README.md +6 -2
  59. package/.pi/harness/agents.manifest.json +38 -49
  60. package/.pi/harness/agents.policy.yaml +275 -0
  61. package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
  62. package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
  63. package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
  64. package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
  65. package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +3 -2
  66. package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
  67. package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
  68. package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
  69. package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
  70. package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
  71. package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
  72. package/.pi/harness/docs/adrs/README.md +6 -0
  73. package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
  74. package/.pi/harness/docs/practice-map.md +2 -2
  75. package/.pi/harness/evolution/README.md +1 -2
  76. package/.pi/harness/examples/agents.policy.project.yaml +19 -0
  77. package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
  78. package/.pi/harness/policies/bash-denylists.yaml +5 -0
  79. package/.pi/harness/policies/defaults.yaml +51 -0
  80. package/.pi/harness/policies/orchestrator.yaml +18 -0
  81. package/.pi/harness/policies/phases.yaml +10 -0
  82. package/.pi/harness/policies/roles.yaml +5 -0
  83. package/.pi/harness/policies/web-guard.yaml +5 -0
  84. package/.pi/harness/policies/workflow-sequences.yaml +9 -0
  85. package/.pi/harness/sentrux/architecture.manifest.json +26 -4
  86. package/.pi/harness/specs/harness-spawn-context.schema.json +1 -1
  87. package/.pi/harness/specs/observation.schema.json +2 -1
  88. package/.pi/lib/agents-policy.d.mts +70 -0
  89. package/.pi/lib/agents-policy.mjs +325 -0
  90. package/.pi/lib/agents-policy.ts +19 -0
  91. package/.pi/lib/agt/audit-run-sink.ts +52 -0
  92. package/.pi/lib/agt/build-evaluation-context.ts +285 -0
  93. package/.pi/lib/agt/config.ts +28 -0
  94. package/.pi/lib/agt/delegation.ts +69 -0
  95. package/.pi/lib/agt/evaluate-policy.ts +56 -0
  96. package/.pi/lib/agt/identity-registry.ts +41 -0
  97. package/.pi/lib/agt/index.ts +55 -0
  98. package/.pi/lib/agt/kill-switch-state.ts +11 -0
  99. package/.pi/lib/agt/legacy-evaluate.ts +101 -0
  100. package/.pi/lib/agt/policy-engine.ts +154 -0
  101. package/.pi/lib/agt/rings.ts +21 -0
  102. package/.pi/lib/agt/sre-hooks.ts +45 -0
  103. package/.pi/lib/agt/trust-run-store.ts +26 -0
  104. package/.pi/lib/agt/workflow-history.ts +29 -0
  105. package/.pi/lib/agt-governance-active.ts +14 -0
  106. package/.pi/lib/agt-tool-guard.ts +78 -0
  107. package/.pi/lib/ask-user/dialog.ts +314 -0
  108. package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
  109. package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
  110. package/.pi/{extensions/lib → lib}/extension-load-guard.ts +21 -0
  111. package/.pi/lib/harness-agt-tool-guard.ts +5 -0
  112. package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
  113. package/.pi/lib/harness-debate-core-deps.ts +14 -0
  114. package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
  115. package/.pi/lib/harness-lens/.gitattributes +1 -0
  116. package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
  117. package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
  118. package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
  119. package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
  120. package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
  121. package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
  122. package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
  123. package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
  124. package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
  125. package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
  126. package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
  127. package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
  128. package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
  129. package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
  130. package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
  131. package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
  132. package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
  133. package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
  134. package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
  135. package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
  136. package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
  137. package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
  138. package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
  139. package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
  140. package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
  141. package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
  142. package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
  143. package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
  144. package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
  145. package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
  146. package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
  147. package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
  148. package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
  149. package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
  150. package/.pi/lib/harness-lens/clients/types.ts +59 -0
  151. package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
  152. package/.pi/lib/harness-lens/index.ts +532 -0
  153. package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
  154. package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
  155. package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
  156. package/.pi/lib/harness-project-config.ts +91 -0
  157. package/.pi/lib/harness-run-context-responses.ts +9 -0
  158. package/.pi/lib/harness-run-context.ts +1 -3
  159. package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
  160. package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
  161. package/.pi/lib/harness-subagent-auth.ts +51 -0
  162. package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
  163. package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
  164. package/.pi/lib/harness-subagent-submit-register.ts +163 -0
  165. package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -55
  166. package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
  167. package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
  168. package/.pi/lib/harness-ui-state.ts +27 -12
  169. package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
  170. package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
  171. package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
  172. package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
  173. package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
  174. package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
  175. package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
  176. package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +3 -52
  177. package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
  178. package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
  179. package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
  180. package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
  181. package/.pi/prompts/harness-auto.md +2 -2
  182. package/.pi/prompts/harness-plan.md +4 -6
  183. package/.pi/prompts/harness-review.md +9 -9
  184. package/.pi/prompts/harness-run.md +7 -7
  185. package/.pi/prompts/harness-setup.md +42 -68
  186. package/.pi/prompts/harness-steer.md +2 -2
  187. package/.pi/scripts/README.md +3 -5
  188. package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
  189. package/.pi/scripts/graphify-kb-updater.mjs +48 -8
  190. package/.pi/scripts/harness-agents-manifest.mjs +61 -4
  191. package/.pi/scripts/harness-agt-doctor.ts +36 -0
  192. package/.pi/scripts/harness-cli-verify.sh +9 -2
  193. package/.pi/scripts/harness-project-toggle.mjs +129 -0
  194. package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
  195. package/.pi/scripts/harness-verify.mjs +113 -39
  196. package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
  197. package/.pi/scripts/validate-plan-dag.mjs +65 -74
  198. package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
  199. package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
  200. package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
  201. package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
  202. package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
  203. package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
  204. package/.pi/skills/architecture/layered/SKILL.md +68 -0
  205. package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
  206. package/.pi/skills/architecture/microservices/SKILL.md +64 -0
  207. package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
  208. package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
  209. package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
  210. package/.pi/skills/architecture/service-based/SKILL.md +64 -0
  211. package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
  212. package/.pi/skills/architecture/space-based/SKILL.md +60 -0
  213. package/.pi/skills/ast-grep/SKILL.md +40 -321
  214. package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
  215. package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
  216. package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
  217. package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
  218. package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
  219. package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
  220. package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
  221. package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
  222. package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
  223. package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
  224. package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
  225. package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
  226. package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
  227. package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
  228. package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
  229. package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
  230. package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
  231. package/.pi/skills/lsp-navigation/SKILL.md +89 -0
  232. package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
  233. package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
  234. package/.pi/skills/quality/security-review/SKILL.md +34 -0
  235. package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
  236. package/.pi/skills/quality/testability-design/SKILL.md +33 -0
  237. package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
  238. package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
  239. package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
  240. package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
  241. package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
  242. package/.sentrux/rules.toml +20 -4
  243. package/AGENTS.md +5 -0
  244. package/CHANGELOG.md +26 -0
  245. package/README.md +85 -58
  246. package/THIRD_PARTY_NOTICES.md +12 -21
  247. package/package.json +15 -7
  248. package/vendor/pi-subagents/src/agents.ts +45 -1
  249. package/vendor/pi-subagents/src/subagents.ts +866 -811
  250. package/vendor/pi-vcc/src/core/brief.ts +68 -99
  251. package/vendor/pi-vcc/src/core/settings.ts +2 -2
  252. package/.agents/skills/caveman/SKILL.md +0 -67
  253. package/.pi/agents/harness/meta-optimizer.md +0 -36
  254. package/.pi/agents/harness/planning/scout-graphify.md +0 -39
  255. package/.pi/agents/harness/planning/scout-semantic.md +0 -41
  256. package/.pi/agents/harness/planning/scout-structure.md +0 -37
  257. package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
  258. package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
  259. package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
  260. package/.pi/extensions/pi-model-router-harness.ts +0 -42
  261. package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
  262. package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
  263. package/.pi/model-router.example.json +0 -36
  264. package/.pi/prompts/harness-critic.md +0 -10
  265. package/.pi/prompts/harness-eval.md +0 -10
  266. package/.pi/prompts/harness-router-tune.md +0 -52
  267. package/.pi/scripts/harness-generate-model-router.mjs +0 -327
  268. package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
  269. package/.pi/scripts/harness-sync-model-router.mjs +0 -97
  270. package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
  271. package/vendor/pi-model-router/.prettierignore +0 -4
  272. package/vendor/pi-model-router/.prettierrc +0 -5
  273. package/vendor/pi-model-router/AGENTS.md +0 -39
  274. package/vendor/pi-model-router/LICENSE +0 -21
  275. package/vendor/pi-model-router/README.md +0 -99
  276. package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
  277. package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
  278. package/vendor/pi-model-router/extensions/commands.ts +0 -720
  279. package/vendor/pi-model-router/extensions/config.ts +0 -348
  280. package/vendor/pi-model-router/extensions/constants.ts +0 -1
  281. package/vendor/pi-model-router/extensions/index.ts +0 -478
  282. package/vendor/pi-model-router/extensions/provider.ts +0 -580
  283. package/vendor/pi-model-router/extensions/routing.ts +0 -564
  284. package/vendor/pi-model-router/extensions/state.ts +0 -52
  285. package/vendor/pi-model-router/extensions/types.ts +0 -95
  286. package/vendor/pi-model-router/extensions/ui.ts +0 -144
  287. package/vendor/pi-model-router/model-router.example.json +0 -48
  288. package/vendor/pi-model-router/package.json +0 -48
  289. package/vendor/pi-model-router/tsconfig.json +0 -16
  290. /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
  291. /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
  292. /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
  293. /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
  294. /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
  295. /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
  296. /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
  297. /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
  298. /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
  299. /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
  300. /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
  301. /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
  302. /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
  303. /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
  304. /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
  305. /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
  306. /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
  307. /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
  308. /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
  309. /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
  310. /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
  311. /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
  312. /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
  313. /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
  314. /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @deprecated Use subagent-governance.ts — re-export for subprocess -e path stability.
3
+ */
4
+ export {
5
+ default,
6
+ harnessSubagentGovernanceExtensionPath,
7
+ subagentGovernanceExtensionPath,
8
+ } from "./subagent-governance.js";
@@ -1,84 +1,42 @@
1
1
  /**
2
2
  * Subprocess-only harness submit tools — validate + write artifacts under run_dir.
3
- * Loaded via `pi --no-extensions -e harness-subagent-submit.ts` for harness agents.
3
+ * Prefer harness-subagent-governance.ts bundle (AGT + submit) for harness spawns.
4
4
  */
5
5
 
6
6
  import { join } from "node:path";
7
7
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
- import { Type } from "@sinclair/typebox";
9
- import { resolveGuardedRunDir } from "../lib/harness-subagent-submit-path.js";
10
- import { claimExtensionLoad } from "./lib/extension-load-guard.js";
11
- import { getHarnessPackageRoot } from "./lib/harness-paths.js";
12
- import { evaluateHarnessSubagentToolCall } from "./lib/harness-subagent-policy.js";
8
+ import { claimHarnessGovernanceLoad } from "../lib/extension-load-guard.js";
9
+ import { evaluateAgtHarnessToolCall } from "../lib/harness-agt-tool-guard.js";
10
+ import { getHarnessPackageRoot } from "../lib/harness-paths.js";
13
11
  import {
14
- executeSubmitPipeline,
15
- loadSubmitDocument,
16
- } from "./lib/harness-subagent-submit-pipeline.js";
17
- import { SUBMIT_TOOL_SPECS } from "./lib/harness-subagent-submit-registry.js";
12
+ isSubprocessHarnessSubmit,
13
+ registerHarnessSubagentSubmitTools,
14
+ resolveHarnessSubmitRunContext,
15
+ } from "../lib/harness-subagent-submit-register.js";
18
16
 
19
17
  // @ts-expect-error pi extensions run as ESM
20
18
  const MODULE_URL = import.meta.url;
21
19
 
22
- const DocumentSchema = Type.Object(
23
- {
24
- document: Type.Optional(
25
- Type.Record(Type.String(), Type.Unknown(), {
26
- description:
27
- "Artifact fields (deprecated when source_path is set; ADR 0043)",
28
- }),
29
- ),
30
- source_path: Type.Optional(
31
- Type.String({
32
- description:
33
- "Relative path under run dir, e.g. artifacts/.draft/decomposition.yaml",
34
- }),
35
- ),
36
- },
37
- { additionalProperties: false },
38
- );
39
-
40
- function resolveRunContext(): {
41
- projectRoot: string;
42
- specsDir: string;
43
- runId: string;
44
- runDirEnv?: string;
45
- agentId: string;
46
- } {
47
- const projectRoot = process.env.HARNESS_PKG_ROOT ?? process.cwd();
48
- const specsDir = join(projectRoot, ".pi", "harness", "specs");
49
- const runId = process.env.HARNESS_RUN_ID?.trim() ?? "";
50
- const runDirEnv = process.env.HARNESS_RUN_DIR?.trim();
51
- const agentId = process.env.HARNESS_AGENT_ID?.trim() ?? "";
52
- return { projectRoot, specsDir, runId, runDirEnv, agentId };
53
- }
54
-
55
- function isSubprocessHarness(): boolean {
56
- return (
57
- process.env.PI_HARNESS_SUBPROCESS === "1" &&
58
- Boolean(process.env.HARNESS_RUN_ID?.trim())
59
- );
60
- }
61
-
62
20
  export default function harnessSubagentSubmit(pi: ExtensionAPI) {
63
- if (!claimExtensionLoad("harness-subagent-submit", MODULE_URL)) return;
64
- // Option A: only load submit tools in subprocess (`-e` bundle), not parent discovery.
21
+ if (!claimHarnessGovernanceLoad("harness-subagent-submit", MODULE_URL))
22
+ return;
65
23
  if (process.env.PI_HARNESS_SUBPROCESS !== "1") {
66
24
  return;
67
25
  }
68
26
 
69
- const _packageRoot = getHarnessPackageRoot(MODULE_URL);
27
+ const packageRoot = getHarnessPackageRoot(MODULE_URL);
28
+ registerHarnessSubagentSubmitTools(pi, packageRoot);
70
29
 
71
- pi.on("tool_call", async (event) => {
30
+ pi.on("tool_call", async (event, ctx) => {
72
31
  if (!event.toolName.startsWith("submit_")) return undefined;
73
- const subprocessOk = isSubprocessHarness();
74
- if (!subprocessOk) {
32
+ if (!isSubprocessHarnessSubmit()) {
75
33
  return {
76
34
  block: true,
77
35
  reason:
78
36
  "harness-subagent-submit: submit_* tools are only available in harness subagent subprocesses.",
79
37
  };
80
38
  }
81
- const { agentId } = resolveRunContext();
39
+ const { agentId } = resolveHarnessSubmitRunContext(packageRoot);
82
40
  if (!agentId) {
83
41
  return {
84
42
  block: true,
@@ -86,118 +44,32 @@ export default function harnessSubagentSubmit(pi: ExtensionAPI) {
86
44
  "harness-subagent-submit: HARNESS_AGENT_ID is required for submit tools.",
87
45
  };
88
46
  }
89
- const decision = evaluateHarnessSubagentToolCall(
90
- event.toolName,
91
- event.input as Record<string, unknown>,
92
- agentId,
93
- );
94
- if (decision.action === "block") {
95
- return { block: true, reason: decision.reason };
96
- }
97
- return undefined;
98
- });
99
47
 
100
- for (const spec of SUBMIT_TOOL_SPECS) {
101
- pi.registerTool({
102
- name: spec.toolName,
103
- label: spec.toolName.replace(/^submit_/, "Submit "),
104
- description: `Terminal harness artifact submit for ${spec.agents.join(", ")}. Call once with the full schema document before ending the turn.`,
105
- parameters: DocumentSchema,
106
- async execute(_id, params, _signal, _onUpdate, _ctx) {
107
- if (!isSubprocessHarness()) {
108
- return {
109
- content: [
110
- {
111
- type: "text",
112
- text: "submit tools require PI_HARNESS_SUBPROCESS and HARNESS_RUN_ID",
113
- },
114
- ],
115
- details: {},
116
- isError: true,
117
- };
118
- }
119
- const { projectRoot, specsDir, runId, runDirEnv, agentId } =
120
- resolveRunContext();
121
- if (!spec.agents.includes(agentId)) {
122
- return {
123
- content: [
124
- {
125
- type: "text",
126
- text: `${spec.toolName} is not allowed for agent ${agentId}`,
127
- },
128
- ],
129
- details: { agentId, tool: spec.toolName },
130
- isError: true,
131
- };
132
- }
133
- const runResolved = await resolveGuardedRunDir({
134
- projectRoot,
135
- runId,
136
- runDirEnv,
137
- });
138
- if (!runResolved.ok) {
139
- return {
140
- content: [{ type: "text", text: runResolved.error }],
141
- details: {},
142
- isError: true,
143
- };
144
- }
145
- const loaded = await loadSubmitDocument({
146
- projectRoot,
147
- runDir: runResolved.runDir,
148
- document: (params as { document?: Record<string, unknown> }).document,
149
- source_path: (params as { source_path?: string }).source_path,
150
- });
151
- if (!loaded.ok) {
152
- return {
153
- content: [
154
- {
155
- type: "text",
156
- text: `Validation failed:\n${loaded.validation_errors.join("\n")}`,
157
- },
158
- ],
159
- isError: true,
160
- details: loaded,
161
- };
162
- }
163
- const result = await executeSubmitPipeline({
164
- projectRoot,
165
- specsDir,
166
- spec,
167
- agentId,
168
- document: loaded.document,
169
- runId,
170
- runDirEnv,
171
- });
172
- if (!result.ok) {
173
- return {
174
- content: [
175
- {
176
- type: "text",
177
- text: `Validation failed:\n${(result.validation_errors ?? []).join("\n")}`,
178
- },
179
- ],
180
- isError: true,
181
- details: result,
182
- };
183
- }
184
- const lines = [`ok: wrote ${result.artifact_path}`];
185
- if (result.lane_result?.messenger_posted) {
186
- lines.push("messenger updated");
187
- }
188
- if (result.human_required) {
189
- lines.push("human_required: parent must call ask_user");
190
- }
191
- return {
192
- content: [{ type: "text", text: lines.join("\n") }],
193
- details: result as unknown,
194
- };
48
+ return evaluateAgtHarnessToolCall({
49
+ moduleUrl: MODULE_URL,
50
+ toolName: event.toolName,
51
+ toolInput: event.input as Record<string, unknown>,
52
+ policyState: {
53
+ phase:
54
+ (process.env.HARNESS_SUBAGENT_PHASE_HINT as
55
+ | "plan"
56
+ | "execute"
57
+ | "evaluate"
58
+ | "adversary"
59
+ | "merge") ?? "plan",
60
+ approvedPlan: true,
61
+ planId: null,
62
+ aborted: false,
63
+ budgetBypass: false,
195
64
  },
65
+ entries: ctx.sessionManager.getEntries(),
66
+ sessionId: ctx.sessionManager.getSessionId(),
67
+ projectRoot: ctx.cwd,
196
68
  });
197
- }
69
+ });
198
70
  }
199
71
 
200
- /** Absolute path to the subprocess submit extension (Option A). */
72
+ /** Absolute path to the subprocess submit extension (legacy standalone). */
201
73
  export function harnessSubagentSubmitExtensionPath(
202
74
  packageRoot: string,
203
75
  ): string {
@@ -6,18 +6,18 @@
6
6
  */
7
7
 
8
8
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
- import { claimExtensionLoad } from "./lib/extension-load-guard.js";
9
+ import { claimHarnessGovernanceLoad } from "../lib/extension-load-guard.js";
10
10
 
11
11
  // @ts-expect-error pi extensions run as ESM
12
12
  const MODULE_URL = import.meta.url;
13
13
 
14
14
  async function loadHarnessSubagents(): Promise<(pi: ExtensionAPI) => void> {
15
- if (!claimExtensionLoad("harness-subagents", MODULE_URL)) {
15
+ if (!claimHarnessGovernanceLoad("harness-subagents", MODULE_URL)) {
16
16
  return () => {};
17
17
  }
18
- const { getHarnessPackageRoot } = await import("./lib/harness-paths.js");
18
+ const { getHarnessPackageRoot } = await import("../lib/harness-paths.js");
19
19
  const { createHarnessSubagentsExtension } = await import(
20
- "./lib/harness-subagents-bridge.js"
20
+ "../lib/harness-subagents-bridge.js"
21
21
  );
22
22
  return createHarnessSubagentsExtension(getHarnessPackageRoot(MODULE_URL));
23
23
  }
@@ -13,7 +13,8 @@ import {
13
13
  captureHarnessEvent,
14
14
  type HarnessPostHogEventName,
15
15
  shutdownHarnessPostHog,
16
- } from "./lib/harness-posthog.js";
16
+ } from "../lib/harness-posthog.js";
17
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
17
18
 
18
19
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
19
20
 
@@ -338,6 +339,7 @@ function mapCustomEntry(
338
339
  }
339
340
 
340
341
  export default function harnessTelemetry(pi: ExtensionAPI) {
342
+ if (!isHarnessProjectEnabled()) return;
341
343
  const flushedHashes = new Set<string>();
342
344
  let lastPolicyPhase: HarnessPhase | null = null;
343
345
 
@@ -4,13 +4,13 @@
4
4
 
5
5
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
6
  import { Type } from "@sinclair/typebox";
7
- import { claimExtensionLoad } from "./lib/extension-load-guard.js";
7
+ import { claimHarnessGovernanceLoad } from "../lib/extension-load-guard.js";
8
8
  import {
9
9
  harnessWebContextLine,
10
10
  readTextExcerpt,
11
11
  runHarnessWeb,
12
12
  summarizeSearchJson,
13
- } from "./lib/harness-web/run-cli.js";
13
+ } from "../lib/harness-web/run-cli.js";
14
14
 
15
15
  // @ts-expect-error pi extensions run as ESM
16
16
  const MODULE_URL = import.meta.url;
@@ -98,7 +98,7 @@ function sessionCwd(ctx: { cwd?: string }): string {
98
98
  }
99
99
 
100
100
  export default function harnessWebTools(pi: ExtensionAPI) {
101
- if (!claimExtensionLoad("harness-web-tools", MODULE_URL)) return;
101
+ if (!claimHarnessGovernanceLoad("harness-web-tools", MODULE_URL)) return;
102
102
  pi.on("before_agent_start", async (event) => {
103
103
  return {
104
104
  systemPrompt: `${event.systemPrompt}\n\n${harnessWebContextLine()}`,
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { randomUUID } from "node:crypto";
9
9
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
10
11
  import { getRunIdFromSession } from "../lib/harness-run-context.js";
11
12
 
12
13
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
@@ -87,6 +88,7 @@ function getRunId(ctx: {
87
88
  }
88
89
 
89
90
  export default function observationBus(pi: ExtensionAPI) {
91
+ if (!isHarnessProjectEnabled()) return;
90
92
  const seen = new Set<string>();
91
93
 
92
94
  pi.on("agent_end", async (_event, ctx) => {
@@ -9,10 +9,9 @@
9
9
  */
10
10
 
11
11
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
- import {
13
- evaluateContextModeMutation,
14
- isMutatingBash,
15
- } from "../lib/harness-context-mode-policy.js";
12
+ import { isHarnessAgtPolicyEnabled } from "../lib/agt/config.js";
13
+ import { evaluateAgtHarnessToolCall } from "../lib/harness-agt-tool-guard.js";
14
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
16
15
  import {
17
16
  extractWritePathFromToolInput,
18
17
  getLatestRunContext,
@@ -31,7 +30,7 @@ import {
31
30
  userVisiblePromptSlice,
32
31
  validatePlanPacket,
33
32
  } from "../lib/harness-run-context.js";
34
- import { bootstrapHarnessSubprocessFromEnv } from "./lib/harness-subprocess-bootstrap.js";
33
+ import { bootstrapHarnessSubprocessFromEnv } from "../lib/harness-subprocess-bootstrap.js";
35
34
 
36
35
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
37
36
 
@@ -60,6 +59,9 @@ const PHASE_ORDER: HarnessPhase[] = [
60
59
  "merge",
61
60
  ];
62
61
 
62
+ // @ts-expect-error pi extensions run as ESM
63
+ const MODULE_URL = import.meta.url;
64
+
63
65
  const MUTATING_TOOLS = new Set(["write", "edit"]);
64
66
 
65
67
  function nowIso(): string {
@@ -126,6 +128,7 @@ function getLatestPolicyStateFull(ctx: {
126
128
  }
127
129
 
128
130
  export default function policyGate(pi: ExtensionAPI) {
131
+ if (!isHarnessProjectEnabled()) return;
129
132
  let state = defaultState();
130
133
 
131
134
  const appendPolicyState = (next: PolicyState): void => {
@@ -248,6 +251,19 @@ export default function policyGate(pi: ExtensionAPI) {
248
251
  const entries = ctx.sessionManager.getEntries();
249
252
  const projectRoot = process.cwd();
250
253
  const sessionId = ctx.sessionManager.getSessionId();
254
+
255
+ if (isHarnessAgtPolicyEnabled()) {
256
+ return evaluateAgtHarnessToolCall({
257
+ moduleUrl: MODULE_URL,
258
+ toolName: event.toolName,
259
+ toolInput: event.input as Record<string, unknown>,
260
+ policyState: state,
261
+ entries,
262
+ sessionId,
263
+ projectRoot,
264
+ });
265
+ }
266
+
251
267
  const runCtx = getLatestRunContext(entries);
252
268
 
253
269
  if (MUTATING_TOOLS.has(event.toolName)) {
@@ -272,6 +288,9 @@ export default function policyGate(pi: ExtensionAPI) {
272
288
 
273
289
  if (event.toolName === "bash") {
274
290
  const command = String(event.input.command ?? "");
291
+ const { isMutatingBash } = await import(
292
+ "../lib/harness-context-mode-policy.js"
293
+ );
275
294
  if (!isMutatingBash(command)) return undefined;
276
295
  if (state.aborted) {
277
296
  return {
@@ -288,6 +307,9 @@ export default function policyGate(pi: ExtensionAPI) {
288
307
  }
289
308
  }
290
309
 
310
+ const { evaluateContextModeMutation } = await import(
311
+ "../lib/harness-context-mode-policy.js"
312
+ );
291
313
  const ctxDecision = evaluateContextModeMutation(
292
314
  event.toolName,
293
315
  event.input as Record<string, unknown>,
@@ -8,6 +8,7 @@
8
8
  import { appendFile, mkdir } from "node:fs/promises";
9
9
  import { join } from "node:path";
10
10
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
11
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
11
12
 
12
13
  type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
13
14
 
@@ -15,12 +16,13 @@ const INCIDENTS_DIR = join(process.cwd(), ".pi", "harness", "incidents");
15
16
  const INCIDENT_FILE = join(INCIDENTS_DIR, "review-integrity.jsonl");
16
17
 
17
18
  const REVIEW_SUBAGENT_TYPES = new Set([
18
- "harness/evaluator",
19
- "harness/adversary",
20
- "harness/tie-breaker",
19
+ "harness/reviewing/evaluator",
20
+ "harness/reviewing/adversary",
21
+ "harness/reviewing/tie-breaker",
21
22
  ]);
22
23
 
23
- const EXECUTOR_SUBAGENT_TYPE = "harness/executor";
24
+ const EXECUTOR_SUBAGENT_TYPE = "harness/running/executor";
25
+ const PLANNING_SUBAGENT_PREFIX = "harness/planning/";
24
26
 
25
27
  interface IsolationState {
26
28
  executorSessionId: string | null;
@@ -138,6 +140,70 @@ function agentsFromSubagentInput(
138
140
  return names;
139
141
  }
140
142
 
143
+ function latestCustomData(
144
+ entries: SessionEntryLike[],
145
+ customType: string,
146
+ ): Record<string, unknown> | null {
147
+ for (let i = entries.length - 1; i >= 0; i--) {
148
+ const entry = entries[i];
149
+ if (entry.type !== "custom" || entry.customType !== customType) continue;
150
+ return entry.data && typeof entry.data === "object" ? entry.data : null;
151
+ }
152
+ return null;
153
+ }
154
+
155
+ function collectStrings(value: unknown, depth = 0): string[] {
156
+ if (depth > 5 || value == null) return [];
157
+ if (typeof value === "string") return [value];
158
+ if (Array.isArray(value)) {
159
+ return value.flatMap((item) => collectStrings(item, depth + 1));
160
+ }
161
+ if (typeof value === "object") {
162
+ return Object.values(value).flatMap((item) =>
163
+ collectStrings(item, depth + 1),
164
+ );
165
+ }
166
+ return [];
167
+ }
168
+
169
+ export function hasPlanReviseRecommendation(entries: unknown[]): boolean {
170
+ const typedEntries = entries as SessionEntryLike[];
171
+ const runContext = latestCustomData(typedEntries, "harness-run-context");
172
+ const text = collectStrings({
173
+ next_recommended_command: runContext?.next_recommended_command,
174
+ last_completed_step: runContext?.last_completed_step,
175
+ last_outcome: runContext?.last_outcome,
176
+ phase: runContext?.phase,
177
+ })
178
+ .join("\n")
179
+ .toLowerCase();
180
+
181
+ return text.includes("/harness-plan") && text.includes("revise");
182
+ }
183
+
184
+ export function isPlanRevisePlanningSubagent(input: {
185
+ agents: string[];
186
+ entries: unknown[];
187
+ toolInput?: Record<string, unknown>;
188
+ }): boolean {
189
+ if (input.agents.length === 0) return false;
190
+ if (
191
+ !input.agents.every((agent) => agent.startsWith(PLANNING_SUBAGENT_PREFIX))
192
+ ) {
193
+ return false;
194
+ }
195
+ if (hasPlanReviseRecommendation(input.entries)) return true;
196
+
197
+ const toolText = collectStrings(input.toolInput).join("\n").toLowerCase();
198
+ return (
199
+ toolText.includes("harness-plan") &&
200
+ (toolText.includes("mode: revise") ||
201
+ toolText.includes("mode=revise") ||
202
+ toolText.includes("--mode revise") ||
203
+ toolText.includes("--mode=revise"))
204
+ );
205
+ }
206
+
141
207
  async function appendIncident(payload: Record<string, unknown>): Promise<void> {
142
208
  await mkdir(INCIDENTS_DIR, { recursive: true });
143
209
  await appendFile(
@@ -148,6 +214,7 @@ async function appendIncident(payload: Record<string, unknown>): Promise<void> {
148
214
  }
149
215
 
150
216
  export default function reviewIntegrity(pi: ExtensionAPI) {
217
+ if (!isHarnessProjectEnabled()) return;
151
218
  let state: IsolationState = {
152
219
  executorSessionId: null,
153
220
  violationActive: false,
@@ -175,7 +242,10 @@ export default function reviewIntegrity(pi: ExtensionAPI) {
175
242
  const phase = getPhase(ctx);
176
243
  const currentSessionId = ctx.sessionManager.getSessionId();
177
244
  const inReview = phase === "evaluate" || phase === "adversary";
178
- if (!inReview) {
245
+ if (
246
+ !inReview ||
247
+ hasPlanReviseRecommendation(ctx.sessionManager.getEntries())
248
+ ) {
179
249
  state.violationActive = false;
180
250
  state.updatedAt = nowIso();
181
251
  persist();
@@ -201,7 +271,7 @@ export default function reviewIntegrity(pi: ExtensionAPI) {
201
271
  customType: "harness-review-integrity-hint",
202
272
  display: true,
203
273
  content: [
204
- "Review phase in executor session: spawn harness/evaluator or harness/adversary via subagent (isolated subprocess).",
274
+ "Review phase in executor session: spawn harness/reviewing/evaluator or harness/reviewing/adversary via subagent (isolated subprocess).",
205
275
  "Do not run review checks directly in this session.",
206
276
  ].join("\n"),
207
277
  },
@@ -210,9 +280,8 @@ export default function reviewIntegrity(pi: ExtensionAPI) {
210
280
 
211
281
  pi.on("tool_call", async (event, ctx) => {
212
282
  if (event.toolName === "subagent") {
213
- const agents = agentsFromSubagentInput(
214
- event.input as Record<string, unknown> | undefined,
215
- );
283
+ const toolInput = event.input as Record<string, unknown> | undefined;
284
+ const agents = agentsFromSubagentInput(toolInput);
216
285
  if (agents.includes(EXECUTOR_SUBAGENT_TYPE)) {
217
286
  state.executorSessionId = ctx.sessionManager.getSessionId();
218
287
  state.violationActive = false;
@@ -226,6 +295,18 @@ export default function reviewIntegrity(pi: ExtensionAPI) {
226
295
  persist();
227
296
  return undefined;
228
297
  }
298
+ if (
299
+ isPlanRevisePlanningSubagent({
300
+ agents,
301
+ entries: ctx.sessionManager.getEntries(),
302
+ toolInput,
303
+ })
304
+ ) {
305
+ state.violationActive = false;
306
+ state.updatedAt = nowIso();
307
+ persist();
308
+ return undefined;
309
+ }
229
310
  }
230
311
 
231
312
  if (!state.violationActive) return undefined;
@@ -237,7 +318,7 @@ export default function reviewIntegrity(pi: ExtensionAPI) {
237
318
  reason:
238
319
  "direct tool use in review phase while sharing executor session context",
239
320
  mitigation:
240
- "spawn harness/evaluator or harness/adversary via subagent instead",
321
+ "spawn harness/reviewing/evaluator or harness/reviewing/adversary via subagent instead",
241
322
  });
242
323
 
243
324
  return {
@@ -4,7 +4,8 @@
4
4
 
5
5
  import { spawn } from "node:child_process";
6
6
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
- import { resolveHarnessScript } from "./lib/harness-paths.js";
7
+ import { resolveHarnessScript } from "../lib/harness-paths.js";
8
+ import { isHarnessProjectEnabled } from "../lib/harness-project-config.js";
8
9
 
9
10
  function resolveSyncScript(): string {
10
11
  return resolveHarnessScript(
@@ -36,6 +37,7 @@ function runSync(args: string[]): Promise<{ code: number; output: string }> {
36
37
  }
37
38
 
38
39
  export default function sentruxRulesSync(pi: ExtensionAPI) {
40
+ if (!isHarnessProjectEnabled()) return;
39
41
  pi.on("session_start", async () => {
40
42
  const { code, output } = await runSync(["--check"]);
41
43
  if (code !== 0) {
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Subprocess governance bundle: AGT policy on all tool_call + harness submit_* tools.
3
+ * Loaded via `pi --no-extensions -e subagent-governance.ts` for every subagent spawn.
4
+ */
5
+
6
+ import { join } from "node:path";
7
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
+ import {
9
+ getAgentKind,
10
+ harnessSubagentPhaseHint,
11
+ } from "../lib/agents-policy.mjs";
12
+ import { evaluateAgtToolCall } from "../lib/agt-tool-guard.js";
13
+ import { claimSubagentGovernanceLoad } from "../lib/extension-load-guard.js";
14
+ import { getHarnessPackageRoot } from "../lib/harness-paths.js";
15
+ import { registerHarnessSubagentSubmitTools } from "../lib/harness-subagent-submit-register.js";
16
+
17
+ // @ts-expect-error pi extensions run as ESM
18
+ const MODULE_URL = import.meta.url;
19
+
20
+ function policyStateFromEnv(packageRoot: string, projectRoot: string) {
21
+ const agentId = process.env.HARNESS_AGENT_ID?.trim() ?? "unknown";
22
+ const phase =
23
+ (process.env.HARNESS_SUBAGENT_PHASE_HINT?.trim() as
24
+ | "plan"
25
+ | "execute"
26
+ | "evaluate"
27
+ | "adversary"
28
+ | "merge") ??
29
+ (harnessSubagentPhaseHint(packageRoot, projectRoot, agentId) as
30
+ | "plan"
31
+ | "execute"
32
+ | "evaluate"
33
+ | "adversary"
34
+ | "merge"
35
+ | null) ??
36
+ "plan";
37
+ return {
38
+ phase,
39
+ approvedPlan: phase === "execute" || phase === "merge",
40
+ planId: process.env.HARNESS_PLAN_ID?.trim() || null,
41
+ aborted: false,
42
+ budgetBypass: false,
43
+ };
44
+ }
45
+
46
+ export function subagentGovernanceExtensionPath(packageRoot: string): string {
47
+ return join(packageRoot, ".pi", "extensions", "subagent-governance.ts");
48
+ }
49
+
50
+ /** @deprecated Use subagentGovernanceExtensionPath */
51
+ export function harnessSubagentGovernanceExtensionPath(
52
+ packageRoot: string,
53
+ ): string {
54
+ return subagentGovernanceExtensionPath(packageRoot);
55
+ }
56
+
57
+ export default function subagentGovernance(pi: ExtensionAPI) {
58
+ if (!claimSubagentGovernanceLoad("subagent-governance", MODULE_URL)) return;
59
+ if (process.env.PI_HARNESS_SUBPROCESS !== "1") {
60
+ return;
61
+ }
62
+
63
+ const packageRoot = getHarnessPackageRoot(MODULE_URL);
64
+ const projectRoot = process.env.HARNESS_PROJECT_ROOT?.trim() || process.cwd();
65
+ const agentId = process.env.HARNESS_AGENT_ID?.trim() ?? "unknown";
66
+
67
+ if (agentId.startsWith("harness/")) {
68
+ registerHarnessSubagentSubmitTools(pi, packageRoot);
69
+ const kind = getAgentKind(packageRoot, projectRoot, agentId);
70
+ process.env.HARNESS_SUBAGENT_PHASE_HINT =
71
+ kind === "executor"
72
+ ? "execute"
73
+ : kind === "evaluator"
74
+ ? "evaluate"
75
+ : kind === "adversary"
76
+ ? "adversary"
77
+ : "plan";
78
+ }
79
+
80
+ pi.on("tool_call", async (event, ctx) => {
81
+ const state = policyStateFromEnv(packageRoot, projectRoot);
82
+ return evaluateAgtToolCall({
83
+ moduleUrl: MODULE_URL,
84
+ toolName: event.toolName,
85
+ toolInput: (event.input ?? {}) as Record<string, unknown>,
86
+ policyState: state,
87
+ entries: ctx.sessionManager.getEntries(),
88
+ sessionId: ctx.sessionManager.getSessionId(),
89
+ projectRoot: ctx.cwd,
90
+ });
91
+ });
92
+ }