@piagent/platform 1.3.0 → 1.4.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 (304) hide show
  1. package/CHANGELOG.md +56 -3
  2. package/README.md +3 -3
  3. package/architecture/layers.json +96 -1
  4. package/docs/README.md +1 -0
  5. package/docs/architecture.md +1 -0
  6. package/docs/authority-manifest.md +1 -0
  7. package/docs/auto-delegation-policy.md +1 -0
  8. package/docs/capability-packs.md +10 -2
  9. package/docs/command-reference-vietnamese.md +5 -4
  10. package/docs/context-engine.md +1 -0
  11. package/docs/context-window-policy.md +1 -0
  12. package/docs/distribution-standard.md +8 -7
  13. package/docs/herdr-workflow.md +1 -0
  14. package/docs/mcp-and-tools.md +14 -9
  15. package/docs/memory-policy.md +1 -0
  16. package/docs/model-options.md +1 -0
  17. package/docs/oauth-providers.md +1 -0
  18. package/docs/operator-manual-vietnamese.md +15 -12
  19. package/docs/package-architecture-notes.md +1 -0
  20. package/docs/project-adapters.md +1 -0
  21. package/docs/project-onboarding.md +1 -0
  22. package/docs/publishing-for-teams.md +1 -0
  23. package/docs/quality-benchmark.md +1 -0
  24. package/docs/quickstart-vietnamese.md +3 -2
  25. package/docs/release-install-policy.md +11 -7
  26. package/docs/runtime-harness-standard.md +1 -0
  27. package/docs/runtime-policy-design.md +1 -0
  28. package/docs/runtime-quality-baseline.md +1 -0
  29. package/docs/security-redaction-benchmark.md +1 -0
  30. package/docs/security-threat-model.md +2 -1
  31. package/docs/subagent-orchestration-capabilities.md +1 -0
  32. package/docs/subagents-and-multiagent.md +1 -0
  33. package/docs/task-implementation-contract.md +1 -0
  34. package/docs/task-lifecycle-vietnamese.md +1 -0
  35. package/docs/team-onboarding.md +3 -2
  36. package/docs/third-party-neutrality.md +26 -0
  37. package/docs/usage-observability.md +1 -0
  38. package/docs/vercel-docs-site.md +1 -0
  39. package/docs/workflow-recipes.md +1 -0
  40. package/docs-site/architecture.html +1 -1
  41. package/docs-site/benchmark.html +1 -1
  42. package/docs-site/commands.html +1 -1
  43. package/docs-site/context-engine.html +1 -1
  44. package/docs-site/en/architecture.html +1 -1
  45. package/docs-site/en/benchmark.html +1 -1
  46. package/docs-site/en/commands.html +1 -1
  47. package/docs-site/en/context-engine.html +1 -1
  48. package/docs-site/en/index.html +1 -1
  49. package/docs-site/en/mcp-approval.html +1 -1
  50. package/docs-site/en/mcp-auth.html +1 -1
  51. package/docs-site/en/mcp-servers.html +1 -1
  52. package/docs-site/en/mcp.html +1 -1
  53. package/docs-site/en/permissions.html +1 -1
  54. package/docs-site/en/profiles.html +1 -1
  55. package/docs-site/en/quickstart.html +1 -1
  56. package/docs-site/en/runtime.html +1 -1
  57. package/docs-site/en/scope.html +1 -1
  58. package/docs-site/en/security.html +1 -1
  59. package/docs-site/en/team.html +1 -1
  60. package/docs-site/en/whats-new.html +1 -1
  61. package/docs-site/en/workflows.html +1 -1
  62. package/docs-site/index.html +1 -1
  63. package/docs-site/mcp-approval.html +1 -1
  64. package/docs-site/mcp-auth.html +1 -1
  65. package/docs-site/mcp-servers.html +1 -1
  66. package/docs-site/mcp.html +1 -1
  67. package/docs-site/permissions.html +1 -1
  68. package/docs-site/profiles.html +1 -1
  69. package/docs-site/quickstart.html +1 -1
  70. package/docs-site/runtime.html +1 -1
  71. package/docs-site/scope.html +1 -1
  72. package/docs-site/security.html +1 -1
  73. package/docs-site/team.html +1 -1
  74. package/docs-site/whats-new.html +1 -1
  75. package/docs-site/workflows.html +1 -1
  76. package/evals/fixtures/mutation-provenance-record.invalid.json +17 -0
  77. package/evals/fixtures/mutation-provenance-record.valid.json +27 -0
  78. package/evals/fixtures/piagent-webui/approval-v1.invalid.json +54 -0
  79. package/evals/fixtures/piagent-webui/approval-v1.valid.json +58 -0
  80. package/evals/fixtures/piagent-webui/attachment-v1.invalid.json +25 -0
  81. package/evals/fixtures/piagent-webui/attachment-v1.valid.json +25 -0
  82. package/evals/fixtures/piagent-webui/capabilities-v1.invalid.json +51 -0
  83. package/evals/fixtures/piagent-webui/capabilities-v1.valid.json +122 -0
  84. package/evals/fixtures/piagent-webui/commit-summary-v1.invalid.json +10 -0
  85. package/evals/fixtures/piagent-webui/commit-summary-v1.valid.json +25 -0
  86. package/evals/fixtures/piagent-webui/control-command-v1.invalid.json +37 -0
  87. package/evals/fixtures/piagent-webui/control-command-v1.valid.json +35 -0
  88. package/evals/fixtures/piagent-webui/diff-v1.invalid.json +13 -0
  89. package/evals/fixtures/piagent-webui/diff-v1.valid.json +141 -0
  90. package/evals/fixtures/piagent-webui/gateway-capabilities-v1.invalid.json +35 -0
  91. package/evals/fixtures/piagent-webui/gateway-capabilities-v1.valid.json +35 -0
  92. package/evals/fixtures/piagent-webui/gateway-protocol-v1.invalid.json +14 -0
  93. package/evals/fixtures/piagent-webui/gateway-protocol-v1.valid.json +14 -0
  94. package/evals/fixtures/piagent-webui/handoff-history-v1.invalid.json +16 -0
  95. package/evals/fixtures/piagent-webui/handoff-history-v1.valid.json +16 -0
  96. package/evals/fixtures/piagent-webui/model-catalog-v1.invalid.json +31 -0
  97. package/evals/fixtures/piagent-webui/model-catalog-v1.valid.json +43 -0
  98. package/evals/fixtures/piagent-webui/queue-v1.invalid.json +29 -0
  99. package/evals/fixtures/piagent-webui/queue-v1.valid.json +44 -0
  100. package/evals/fixtures/piagent-webui/recovery-history-v1.invalid.json +17 -0
  101. package/evals/fixtures/piagent-webui/recovery-history-v1.valid.json +20 -0
  102. package/evals/fixtures/piagent-webui/release-monitor-v1.invalid.json +12 -0
  103. package/evals/fixtures/piagent-webui/release-monitor-v1.valid.json +29 -0
  104. package/evals/fixtures/piagent-webui/review-state-v1.invalid.json +25 -0
  105. package/evals/fixtures/piagent-webui/review-state-v1.valid.json +38 -0
  106. package/evals/fixtures/piagent-webui/runtime-event-v2.invalid.json +49 -0
  107. package/evals/fixtures/piagent-webui/runtime-event-v2.valid.json +49 -0
  108. package/evals/fixtures/piagent-webui/session-catalog-v1.invalid.json +17 -0
  109. package/evals/fixtures/piagent-webui/session-catalog-v1.valid.json +58 -0
  110. package/evals/fixtures/piagent-webui/session-command-v1.invalid.json +19 -0
  111. package/evals/fixtures/piagent-webui/session-command-v1.valid.json +19 -0
  112. package/evals/fixtures/piagent-webui/snapshot-v1.invalid.json +5 -0
  113. package/evals/fixtures/piagent-webui/snapshot-v1.valid.json +191 -0
  114. package/evals/fixtures/piagent-webui/source-change-v1.invalid.json +57 -0
  115. package/evals/fixtures/piagent-webui/source-change-v1.valid.json +98 -0
  116. package/evals/fixtures/piagent-webui/source-mutation-v1.invalid.json +19 -0
  117. package/evals/fixtures/piagent-webui/source-mutation-v1.valid.json +38 -0
  118. package/evals/fixtures/piagent-webui/source-revert-v1.invalid.json +20 -0
  119. package/evals/fixtures/piagent-webui/source-revert-v1.valid.json +62 -0
  120. package/evals/fixtures/piagent-webui/subagent-tree-v1.invalid.json +26 -0
  121. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +39 -0
  122. package/evals/fixtures/piagent-webui/task-index-v1.invalid.json +15 -0
  123. package/evals/fixtures/piagent-webui/task-index-v1.valid.json +15 -0
  124. package/evals/fixtures/piagent-webui/task-timeline-v1.invalid.json +14 -0
  125. package/evals/fixtures/piagent-webui/task-timeline-v1.valid.json +16 -0
  126. package/evals/fixtures/piagent-webui/transcript-v1.invalid.json +35 -0
  127. package/evals/fixtures/piagent-webui/transcript-v1.valid.json +55 -0
  128. package/evals/fixtures/task-baseline-manifest.invalid.json +16 -0
  129. package/evals/fixtures/task-baseline-manifest.valid.json +41 -0
  130. package/evals/fixtures/verifier-file-snapshot.invalid.json +21 -0
  131. package/evals/fixtures/verifier-file-snapshot.valid.json +21 -0
  132. package/package.json +30 -3
  133. package/packages/piagent-core/benchmark/benchmark-candidate.d.ts +1 -0
  134. package/packages/piagent-core/benchmark/benchmark-forensics.d.ts +1 -0
  135. package/packages/piagent-core/benchmark/benchmark-ledger.d.ts +4 -0
  136. package/packages/piagent-core/benchmark/benchmark-record-validation.d.ts +1 -0
  137. package/packages/piagent-core/benchmark/benchmark-runtime.d.ts +1 -0
  138. package/packages/piagent-core/capabilities/capability-core.js +2 -2
  139. package/packages/piagent-core/capabilities/capability-verification-cache.js +280 -0
  140. package/packages/piagent-core/capabilities/runtime-integrity.js +153 -1
  141. package/packages/piagent-core/extensions/document-intake.ts +24 -10
  142. package/packages/piagent-core/extensions/piagent-guard.ts +90 -73
  143. package/packages/piagent-core/extensions/redaction-core.d.ts +2 -0
  144. package/packages/piagent-core/extensions/state-retention.js +77 -44
  145. package/packages/piagent-core/extensions/task-journal.js +30 -1
  146. package/packages/piagent-core/extensions/task-state.js +33 -36
  147. package/packages/piagent-core/mcp/mcp-server-catalog.js +5 -5
  148. package/packages/piagent-core/package.json +1 -1
  149. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +2 -2
  150. package/packages/piagent-core/runtime/hooks/tool-call-hook.ts +5 -2
  151. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +20 -19
  152. package/packages/piagent-core/runtime/hooks/tool-result-metadata.ts +13 -0
  153. package/packages/piagent-core/runtime/input/chat-images.ts +2 -2
  154. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +94 -0
  155. package/packages/piagent-core/runtime/inspection/approval-broker.ts +318 -0
  156. package/packages/piagent-core/runtime/inspection/commit-summary-projection.ts +59 -0
  157. package/packages/piagent-core/runtime/inspection/context-telemetry-inspection.ts +37 -0
  158. package/packages/piagent-core/runtime/inspection/criteria-links.ts +295 -0
  159. package/packages/piagent-core/runtime/inspection/diff-projection.ts +499 -0
  160. package/packages/piagent-core/runtime/inspection/git-filter-safety.ts +38 -0
  161. package/packages/piagent-core/runtime/inspection/git-numstat-adapter.ts +71 -0
  162. package/packages/piagent-core/runtime/inspection/git-status-adapter.ts +493 -0
  163. package/packages/piagent-core/runtime/inspection/git-tree-adapter.ts +108 -0
  164. package/packages/piagent-core/runtime/inspection/mcp-control.ts +10 -0
  165. package/packages/piagent-core/runtime/inspection/mutation-provenance-contract.ts +131 -0
  166. package/packages/piagent-core/runtime/inspection/mutation-provenance-recorder.ts +48 -0
  167. package/packages/piagent-core/runtime/inspection/mutation-provenance-store.ts +161 -0
  168. package/packages/piagent-core/runtime/inspection/project-runtime-inspection.ts +36 -0
  169. package/packages/piagent-core/runtime/inspection/review-state-contract.ts +108 -0
  170. package/packages/piagent-core/runtime/inspection/review-state-projection.ts +93 -0
  171. package/packages/piagent-core/runtime/inspection/review-state-store.ts +112 -0
  172. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +262 -0
  173. package/packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts +103 -0
  174. package/packages/piagent-core/runtime/inspection/source-change-projection.ts +471 -0
  175. package/packages/piagent-core/runtime/inspection/source-evidence-contract.ts +139 -0
  176. package/packages/piagent-core/runtime/inspection/source-evidence-store.ts +315 -0
  177. package/packages/piagent-core/runtime/inspection/source-handoff-store.ts +70 -0
  178. package/packages/piagent-core/runtime/inspection/source-inspection-plan.ts +34 -0
  179. package/packages/piagent-core/runtime/inspection/source-mutation-projection.ts +164 -0
  180. package/packages/piagent-core/runtime/inspection/source-mutation-store.ts +168 -0
  181. package/packages/piagent-core/runtime/inspection/source-open-target.ts +38 -0
  182. package/packages/piagent-core/runtime/inspection/source-revert-projection.ts +147 -0
  183. package/packages/piagent-core/runtime/inspection/task-baseline-start-capture.ts +22 -0
  184. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +160 -0
  185. package/packages/piagent-core/runtime/inspection/task-control-journal.ts +131 -0
  186. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +95 -0
  187. package/packages/piagent-core/runtime/inspection/task-provenance-projection.ts +60 -0
  188. package/packages/piagent-core/runtime/inspection/task-recovery-timeline.ts +88 -0
  189. package/packages/piagent-core/runtime/inspection/task-run-index.ts +87 -0
  190. package/packages/piagent-core/runtime/inspection/task-source-projection.ts +401 -0
  191. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +101 -0
  192. package/packages/piagent-core/runtime/inspection/text-diff.ts +60 -0
  193. package/packages/piagent-core/runtime/inspection/verifier-snapshot-contract.ts +114 -0
  194. package/packages/piagent-core/runtime/inspection/verifier-snapshot-store.ts +189 -0
  195. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +320 -0
  196. package/packages/piagent-core/runtime/inspection/workspace-file-reader.ts +132 -0
  197. package/packages/piagent-core/runtime/inspection/workspace-source-projection.ts +96 -0
  198. package/packages/piagent-core/runtime/inspection/zero-turn-conformance.ts +175 -0
  199. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +68 -4
  200. package/packages/piagent-core/runtime/policy/source-index-transaction.ts +173 -0
  201. package/packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts +42 -0
  202. package/packages/piagent-core/runtime/policy/source-mutation-guard.ts +184 -0
  203. package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +105 -0
  204. package/packages/piagent-core/runtime/product/activity-inspector-footer.ts +5 -3
  205. package/packages/piagent-core/runtime/product/activity-inspector.ts +85 -343
  206. package/packages/piagent-core/runtime/recovery/resume-state.ts +38 -1
  207. package/packages/piagent-core/runtime/registration/activity-inspector-command.ts +68 -14
  208. package/packages/piagent-core/runtime/registration/task-start-tool.ts +13 -14
  209. package/packages/piagent-core/runtime/session/model-authorship-state.ts +31 -3
  210. package/packages/piagent-core/runtime/session/runtime-state.ts +11 -1
  211. package/packages/piagent-webui/dist/client/assets/index-DjMntaK5.js +172 -0
  212. package/packages/piagent-webui/dist/client/assets/index-DwKCWbwd.css +1 -0
  213. package/packages/piagent-webui/dist/client/index.html +16 -0
  214. package/packages/piagent-webui/extension/attachment-store.ts +323 -0
  215. package/packages/piagent-webui/extension/held-message-queue.ts +311 -0
  216. package/packages/piagent-webui/extension/lifecycle-controller.ts +408 -0
  217. package/packages/piagent-webui/extension/lifecycle-event-adapter.ts +23 -0
  218. package/packages/piagent-webui/extension/piagent-webui.ts +470 -0
  219. package/packages/piagent-webui/extension/review-controller.ts +160 -0
  220. package/packages/piagent-webui/extension/same-session-bridge.ts +500 -0
  221. package/packages/piagent-webui/extension/session-options-controller.ts +281 -0
  222. package/packages/piagent-webui/extension/session-stream-adapter.ts +249 -0
  223. package/packages/piagent-webui/extension/source-mutation-controller.ts +215 -0
  224. package/packages/piagent-webui/extension/source-open-controller.ts +136 -0
  225. package/packages/piagent-webui/extension/source-revert-controller.ts +189 -0
  226. package/packages/piagent-webui/extension/terminal-session-adapter.ts +73 -0
  227. package/packages/piagent-webui/extension/vscode-handoff.ts +51 -0
  228. package/packages/piagent-webui/gateway/control-socket.ts +92 -0
  229. package/packages/piagent-webui/gateway/gateway-events.ts +60 -0
  230. package/packages/piagent-webui/gateway/gateway-protocol-service.ts +64 -0
  231. package/packages/piagent-webui/gateway/gateway-service.ts +215 -0
  232. package/packages/piagent-webui/gateway/gateway-session-stream.ts +110 -0
  233. package/packages/piagent-webui/gateway/mcp-auth-broker.ts +158 -0
  234. package/packages/piagent-webui/gateway/native-project-picker.ts +29 -0
  235. package/packages/piagent-webui/gateway/pi-host.ts +41 -0
  236. package/packages/piagent-webui/gateway/profile-state.ts +1 -0
  237. package/packages/piagent-webui/gateway/project-registry.ts +108 -0
  238. package/packages/piagent-webui/gateway/provider-auth-broker.ts +172 -0
  239. package/packages/piagent-webui/gateway/session-catalog.ts +128 -0
  240. package/packages/piagent-webui/gateway/session-command-controller.ts +237 -0
  241. package/packages/piagent-webui/gateway/session-command-store.ts +224 -0
  242. package/packages/piagent-webui/gateway/session-inspection-registry.ts +204 -0
  243. package/packages/piagent-webui/gateway/session-lease-store.ts +1 -0
  244. package/packages/piagent-webui/gateway/session-metadata-store.ts +152 -0
  245. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +485 -0
  246. package/packages/piagent-webui/ownership/gateway-runtime-context.ts +8 -0
  247. package/packages/piagent-webui/ownership/profile-state.ts +117 -0
  248. package/packages/piagent-webui/ownership/session-lease-store.ts +329 -0
  249. package/packages/piagent-webui/ownership/session-refs.ts +8 -0
  250. package/packages/piagent-webui/package.json +33 -0
  251. package/packages/piagent-webui/server/README.md +4 -0
  252. package/packages/piagent-webui/server/benchmark-release-monitor.ts +230 -0
  253. package/packages/piagent-webui/server/core-inspection-provider.ts +403 -0
  254. package/packages/piagent-webui/server/gateway-websocket.ts +143 -0
  255. package/packages/piagent-webui/server/http-security.ts +27 -0
  256. package/packages/piagent-webui/server/ipc-read-model-client.ts +89 -0
  257. package/packages/piagent-webui/server/loopback-server.ts +368 -0
  258. package/packages/piagent-webui/server/read-model-provider.ts +37 -0
  259. package/packages/piagent-webui/server/read-only-router.ts +105 -0
  260. package/packages/piagent-webui/server/session-auth.ts +88 -0
  261. package/packages/piagent-webui/server/sidecar-main.ts +86 -0
  262. package/packages/piagent-webui/server/sse-hub.ts +76 -0
  263. package/packages/piagent-webui/server/static-bundle.ts +40 -0
  264. package/packages/piagent-webui/server/transcript-projection.ts +110 -0
  265. package/schemas/mutation-provenance-record.schema.json +100 -0
  266. package/schemas/piagent-webui/approval-v1.schema.json +654 -0
  267. package/schemas/piagent-webui/attachment-v1.schema.json +267 -0
  268. package/schemas/piagent-webui/capabilities-v1.schema.json +729 -0
  269. package/schemas/piagent-webui/catalog-v1.json +240 -0
  270. package/schemas/piagent-webui/commit-summary-v1.schema.json +63 -0
  271. package/schemas/piagent-webui/common-v1.schema.json +479 -0
  272. package/schemas/piagent-webui/control-command-v1.schema.json +1780 -0
  273. package/schemas/piagent-webui/diff-v1.schema.json +826 -0
  274. package/schemas/piagent-webui/gateway-capabilities-v1.schema.json +136 -0
  275. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +456 -0
  276. package/schemas/piagent-webui/handoff-history-v1.schema.json +92 -0
  277. package/schemas/piagent-webui/model-catalog-v1.schema.json +95 -0
  278. package/schemas/piagent-webui/queue-v1.schema.json +99 -0
  279. package/schemas/piagent-webui/recovery-history-v1.schema.json +144 -0
  280. package/schemas/piagent-webui/release-monitor-v1.schema.json +173 -0
  281. package/schemas/piagent-webui/review-state-v1.schema.json +206 -0
  282. package/schemas/piagent-webui/runtime-event-v2.schema.json +5206 -0
  283. package/schemas/piagent-webui/session-catalog-v1.schema.json +222 -0
  284. package/schemas/piagent-webui/session-command-v1.schema.json +390 -0
  285. package/schemas/piagent-webui/snapshot-v1.schema.json +2622 -0
  286. package/schemas/piagent-webui/source-change-v1.schema.json +763 -0
  287. package/schemas/piagent-webui/source-mutation-v1.schema.json +96 -0
  288. package/schemas/piagent-webui/source-revert-v1.schema.json +130 -0
  289. package/schemas/piagent-webui/subagent-tree-v1.schema.json +88 -0
  290. package/schemas/piagent-webui/task-index-v1.schema.json +74 -0
  291. package/schemas/piagent-webui/task-timeline-v1.schema.json +49 -0
  292. package/schemas/piagent-webui/transcript-v1.schema.json +243 -0
  293. package/schemas/task-baseline-manifest.schema.json +122 -0
  294. package/schemas/verifier-file-snapshot.schema.json +81 -0
  295. package/scripts/check-architecture.mjs +1 -0
  296. package/scripts/check-doc-languages.mjs +19 -2
  297. package/scripts/check-third-party-neutrality.mjs +43 -0
  298. package/scripts/install-global.sh +1 -1
  299. package/scripts/piagent-cli.mjs +17 -3
  300. package/scripts/piagent-dashboard.mjs +208 -0
  301. package/scripts/piagent-webui-launcher.mjs +121 -0
  302. package/scripts/typescript-loader.mjs +26 -1
  303. package/scripts/verify-local.sh +253 -0
  304. package/scripts/verify-release-identity.mjs +4 -1
@@ -1,3 +1,6 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
1
4
  export const CORE_RUNTIME_INTEGRITY_FILES = Object.freeze([
2
5
  "packages/piagent-core/runtime/context/adaptive-planner.ts",
3
6
  "packages/piagent-core/runtime/context/retrieval-route-policy.ts",
@@ -7,17 +10,23 @@ export const CORE_RUNTIME_INTEGRITY_FILES = Object.freeze([
7
10
  "packages/piagent-core/extensions/acceptance-receipt.js",
8
11
  "packages/piagent-core/extensions/criterion-graph.js",
9
12
  "packages/piagent-core/extensions/criterion-context-pack.js",
13
+ "packages/piagent-core/extensions/document-intake.ts",
14
+ "packages/piagent-core/extensions/execution-backend.js",
10
15
  "packages/piagent-core/extensions/failure-types.ts",
11
16
  "packages/piagent-core/extensions/repository-memory.js",
17
+ "packages/piagent-core/extensions/state-retention.js",
12
18
  "packages/piagent-core/extensions/task-contract-view.js",
13
19
  "packages/piagent-core/extensions/task-authority-contract.js",
14
20
  "packages/piagent-core/extensions/task-digest-migration.js",
15
21
  "packages/piagent-core/extensions/task-journal.js",
16
22
  "packages/piagent-core/extensions/task-runtime-audit.js",
23
+ "packages/piagent-core/extensions/task-state.js",
17
24
  "packages/piagent-core/extensions/tenant-contract.js",
25
+ "packages/piagent-core/extensions/verification-intelligence.js",
18
26
  "packages/piagent-core/extensions/working-tree-digest.js",
19
27
  "packages/piagent-core/policy/authority-manifest.v1.json",
20
28
  "packages/piagent-core/capabilities/authority-manifest.ts",
29
+ "packages/piagent-core/capabilities/capability-verification-cache.js",
21
30
  "packages/piagent-core/runtime/hooks/agent-start-hook.ts",
22
31
  "packages/piagent-core/runtime/hooks/completion-hook.ts",
23
32
  "packages/piagent-core/runtime/hooks/input-hook.ts",
@@ -25,7 +34,53 @@ export const CORE_RUNTIME_INTEGRITY_FILES = Object.freeze([
25
34
  "packages/piagent-core/runtime/hooks/session-start-hook.ts",
26
35
  "packages/piagent-core/runtime/hooks/tool-call-hook.ts",
27
36
  "packages/piagent-core/runtime/hooks/tool-result-hook.ts",
37
+ "packages/piagent-core/runtime/hooks/tool-result-metadata.ts",
28
38
  "packages/piagent-core/runtime/input/chat-images.ts",
39
+ "packages/piagent-core/runtime/inspection/activity-event-adapter.ts",
40
+ "packages/piagent-core/runtime/inspection/approval-broker.ts",
41
+ "packages/piagent-core/runtime/inspection/criteria-links.ts",
42
+ "packages/piagent-core/runtime/inspection/commit-summary-projection.ts",
43
+ "packages/piagent-core/runtime/inspection/diff-projection.ts",
44
+ "packages/piagent-core/runtime/inspection/git-numstat-adapter.ts",
45
+ "packages/piagent-core/runtime/inspection/git-filter-safety.ts",
46
+ "packages/piagent-core/runtime/inspection/git-status-adapter.ts",
47
+ "packages/piagent-core/runtime/inspection/git-tree-adapter.ts",
48
+ "packages/piagent-core/runtime/inspection/mcp-control.ts",
49
+ "packages/piagent-core/runtime/inspection/mutation-provenance-contract.ts",
50
+ "packages/piagent-core/runtime/inspection/mutation-provenance-recorder.ts",
51
+ "packages/piagent-core/runtime/inspection/mutation-provenance-store.ts",
52
+ "packages/piagent-core/runtime/inspection/project-runtime-inspection.ts",
53
+ "packages/piagent-core/runtime/inspection/review-state-contract.ts",
54
+ "packages/piagent-core/runtime/inspection/review-state-projection.ts",
55
+ "packages/piagent-core/runtime/inspection/review-state-store.ts",
56
+ "packages/piagent-core/runtime/inspection/runtime-event-store.ts",
57
+ "packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts",
58
+ "packages/piagent-core/runtime/inspection/zero-turn-conformance.ts",
59
+ "packages/piagent-core/runtime/inspection/source-evidence-contract.ts",
60
+ "packages/piagent-core/runtime/inspection/source-evidence-store.ts",
61
+ "packages/piagent-core/runtime/inspection/source-inspection-plan.ts",
62
+ "packages/piagent-core/runtime/inspection/source-mutation-projection.ts",
63
+ "packages/piagent-core/runtime/inspection/source-mutation-store.ts",
64
+ "packages/piagent-core/runtime/inspection/source-revert-projection.ts",
65
+ "packages/piagent-core/runtime/inspection/source-handoff-store.ts",
66
+ "packages/piagent-core/runtime/inspection/source-open-target.ts",
67
+ "packages/piagent-core/runtime/inspection/task-run-index.ts",
68
+ "packages/piagent-core/runtime/inspection/task-recovery-timeline.ts",
69
+ "packages/piagent-core/runtime/inspection/task-compaction-history.ts",
70
+ "packages/piagent-core/runtime/inspection/context-telemetry-inspection.ts",
71
+ "packages/piagent-core/runtime/inspection/task-handoff-history.ts",
72
+ "packages/piagent-core/runtime/inspection/task-subagent-tree.ts",
73
+ "packages/piagent-core/runtime/inspection/task-baseline-start-capture.ts",
74
+ "packages/piagent-core/runtime/inspection/task-control-journal.ts",
75
+ "packages/piagent-core/runtime/inspection/task-source-projection.ts",
76
+ "packages/piagent-core/runtime/inspection/task-provenance-projection.ts",
77
+ "packages/piagent-core/runtime/inspection/verifier-snapshot-contract.ts",
78
+ "packages/piagent-core/runtime/inspection/verifier-snapshot-store.ts",
79
+ "packages/piagent-core/runtime/inspection/webui-snapshot.ts",
80
+ "packages/piagent-core/runtime/inspection/text-diff.ts",
81
+ "packages/piagent-core/runtime/inspection/source-change-projection.ts",
82
+ "packages/piagent-core/runtime/inspection/workspace-file-reader.ts",
83
+ "packages/piagent-core/runtime/inspection/workspace-source-projection.ts",
29
84
  "packages/piagent-core/runtime/model/capabilities.ts",
30
85
  "packages/piagent-core/runtime/model/authenticated-catalog.ts",
31
86
  "packages/piagent-core/runtime/model/model-route-policy.ts",
@@ -36,6 +91,10 @@ export const CORE_RUNTIME_INTEGRITY_FILES = Object.freeze([
36
91
  "packages/piagent-core/runtime/model/snapshot-telemetry.ts",
37
92
  "packages/piagent-core/runtime/policy/authority-manifest.ts",
38
93
  "packages/piagent-core/runtime/policy/authority-resume-policy.ts",
94
+ "packages/piagent-core/runtime/policy/source-index-transaction.ts",
95
+ "packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts",
96
+ "packages/piagent-core/runtime/policy/source-mutation-guard.ts",
97
+ "packages/piagent-core/runtime/policy/source-worktree-transaction.ts",
39
98
  "packages/piagent-core/runtime/policy/task-authority-runtime.ts",
40
99
  "packages/piagent-core/runtime/orchestration/role-policy.ts",
41
100
  "packages/piagent-core/runtime/orchestration/role-binder.ts",
@@ -95,5 +154,98 @@ export const CORE_RUNTIME_INTEGRITY_FILES = Object.freeze([
95
154
  "packages/piagent-core/runtime/tools/phase-tools.ts",
96
155
  "packages/piagent-core/runtime/tools/phase-tool-runtime.ts",
97
156
  "packages/piagent-core/runtime/workflows/input-routing.ts",
98
- "packages/piagent-core/runtime/workflows/task-intake.ts"
157
+ "packages/piagent-core/runtime/workflows/task-intake.ts",
158
+ "scripts/piagent-cli.mjs",
159
+ "scripts/register-typescript-loader.mjs",
160
+ "scripts/typescript-loader.mjs"
99
161
  ]);
162
+
163
+ const EXECUTABLE_SOURCE = /\.(?:[cm]?js|ts|json|sh)$/;
164
+ const IMPORT_SPECIFIER = /(?:\bimport\s*(?:type\s+)?(?:[^"'`]*?\s+from\s*)?|\bexport\s+(?:type\s+)?[^"'`]*?\s+from\s*)["']([^"']+)["']/g;
165
+ const DYNAMIC_IMPORT = /\bimport\(\s*["']([^"']+)["']\s*\)/g;
166
+ const RUNTIME_LITERAL = /["']((?:packages|scripts)\/[A-Za-z0-9_./-]+\.(?:[cm]?js|ts|json|sh))["']/g;
167
+ const MAX_INTEGRITY_FILES = 5_000;
168
+
169
+ function normalizedRelative(root, absolute) {
170
+ const relative = path.relative(root, absolute);
171
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return undefined;
172
+ return relative.split(path.sep).join("/");
173
+ }
174
+
175
+ function executableFilesBelow(root, relativeDirectory) {
176
+ const directory = path.join(root, relativeDirectory);
177
+ if (!fs.existsSync(directory)) return [];
178
+ const files = [];
179
+ const visit = (absolute) => {
180
+ const entries = fs.readdirSync(absolute, { withFileTypes: true });
181
+ for (const entry of entries) {
182
+ const target = path.join(absolute, entry.name), stat = fs.lstatSync(target);
183
+ if (stat.isSymbolicLink()) throw new Error(`runtime-integrity-symlink:${normalizedRelative(root, target) ?? "outside-root"}`);
184
+ if (stat.isDirectory()) visit(target);
185
+ else if (stat.isFile() && EXECUTABLE_SOURCE.test(entry.name)) files.push(normalizedRelative(root, target));
186
+ }
187
+ };
188
+ visit(directory);
189
+ return files.filter(Boolean);
190
+ }
191
+
192
+ function localImportTarget(root, importer, specifier) {
193
+ if (!specifier.startsWith(".")) return undefined;
194
+ const base = path.resolve(path.dirname(path.join(root, importer)), specifier);
195
+ const candidates = [base, `${base}.ts`, `${base}.js`, `${base}.mjs`, path.join(base, "index.ts"), path.join(base, "index.js")];
196
+ for (const candidate of candidates) {
197
+ const relative = normalizedRelative(root, candidate);
198
+ if (!relative || !fs.existsSync(candidate)) continue;
199
+ const stat = fs.lstatSync(candidate);
200
+ if (stat.isSymbolicLink()) throw new Error(`runtime-integrity-symlink:${relative}`);
201
+ if (stat.isFile() && EXECUTABLE_SOURCE.test(candidate)) return relative;
202
+ }
203
+ return undefined;
204
+ }
205
+
206
+ function sourceReferences(root, relativeFile) {
207
+ if (!/\.(?:[cm]?js|ts)$/.test(relativeFile)) return [];
208
+ const source = fs.readFileSync(path.join(root, relativeFile), "utf8"), references = new Set();
209
+ for (const pattern of [IMPORT_SPECIFIER, DYNAMIC_IMPORT]) {
210
+ pattern.lastIndex = 0;
211
+ for (let match = pattern.exec(source); match; match = pattern.exec(source)) {
212
+ const target = localImportTarget(root, relativeFile, match[1]);
213
+ if (target) references.add(target);
214
+ }
215
+ }
216
+ RUNTIME_LITERAL.lastIndex = 0;
217
+ for (let match = RUNTIME_LITERAL.exec(source); match; match = RUNTIME_LITERAL.exec(source)) {
218
+ const target = path.join(root, match[1]), relative = normalizedRelative(root, target);
219
+ if (relative && fs.existsSync(target) && fs.lstatSync(target).isFile()) references.add(relative);
220
+ }
221
+ return [...references];
222
+ }
223
+
224
+ export function discoverRuntimeIntegrityFiles(root) {
225
+ const packageDocument = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
226
+ const declaredExtensions = Array.isArray(packageDocument.pi?.extensions) ? packageDocument.pi.extensions : [];
227
+ const declaredBins = packageDocument.bin && typeof packageDocument.bin === "object" ? Object.values(packageDocument.bin) : [];
228
+ const declaredEntrypoints = Array.isArray(packageDocument.piagent?.runtimeIntegrityEntrypoints)
229
+ ? packageDocument.piagent.runtimeIntegrityEntrypoints : [];
230
+ const seeds = new Set([
231
+ ...CORE_RUNTIME_INTEGRITY_FILES,
232
+ "packages/piagent-core/extensions/piagent-guard.ts",
233
+ ...executableFilesBelow(root, "packages/piagent-core/runtime"),
234
+ ...declaredExtensions,
235
+ ...declaredBins,
236
+ ...declaredEntrypoints
237
+ ].filter((entry) => typeof entry === "string" && !path.isAbsolute(entry)));
238
+ const files = new Set(), pending = [...seeds];
239
+ while (pending.length) {
240
+ if (files.size >= MAX_INTEGRITY_FILES) throw new Error("runtime-integrity-file-limit");
241
+ const relative = pending.pop();
242
+ if (files.has(relative)) continue;
243
+ const absolute = path.join(root, relative), contained = normalizedRelative(root, absolute);
244
+ if (!contained || !fs.existsSync(absolute)) continue;
245
+ const stat = fs.lstatSync(absolute);
246
+ if (stat.isSymbolicLink() || !stat.isFile()) throw new Error(`runtime-integrity-file-unsafe:${relative}`);
247
+ files.add(contained);
248
+ for (const reference of sourceReferences(root, contained)) if (!files.has(reference)) pending.push(reference);
249
+ }
250
+ return [...files].sort((left, right) => left.localeCompare(right, "en"));
251
+ }
@@ -51,6 +51,8 @@ export type DocumentExtraction =
51
51
  | { status: "ok"; text: string; truncated: boolean; kind: "text" | "docx" | "pdf" }
52
52
  | { status: "error"; reason: string };
53
53
 
54
+ type DocumentCommandResult = { status: number | null; stdout: string; stderr: string; error?: Error };
55
+
54
56
  export function expandHome(candidate: string, home: string | undefined): string {
55
57
  if (candidate === "~") return home ?? candidate;
56
58
  if (candidate.startsWith("~/")) {
@@ -435,7 +437,7 @@ export function extractTextDocument(buffer: Buffer): DocumentExtraction {
435
437
  // missing tool would be a lie in the direction that wastes the most time.
436
438
  export function extractPdfFromDescriptor(
437
439
  fd: number,
438
- run: (command: string, args: string[], input?: number) => { status: number | null; stdout: string; stderr: string; error?: Error } = defaultRun
440
+ run: (command: string, args: string[], input?: number) => DocumentCommandResult = defaultRun
439
441
  ): DocumentExtraction {
440
442
  const probe = run("command", ["-v", "pdftotext"]);
441
443
  if (probe.error || probe.status !== 0) {
@@ -471,19 +473,31 @@ export function extractPdfFromDescriptor(
471
473
  // returns would hang the session rather than fail the read.
472
474
  const PDF_TIMEOUT_MS = 20_000;
473
475
 
476
+ export function probeExecutableOnPath(executable: string): DocumentCommandResult {
477
+ const result = spawnSync("/bin/sh", ["-c", 'command -v "$1"', "piagent-command-probe", executable], {
478
+ encoding: "utf8",
479
+ timeout: PDF_TIMEOUT_MS
480
+ });
481
+ return {
482
+ status: result.status,
483
+ stdout: result.stdout ?? "",
484
+ stderr: result.stderr ?? "",
485
+ error: result.error
486
+ };
487
+ }
488
+
474
489
  function defaultRun(
475
490
  command: string,
476
491
  args: string[],
477
492
  input?: number
478
- ): { status: number | null; stdout: string; stderr: string; error?: Error } {
479
- const result = command === "command"
480
- ? spawnSync("/bin/sh", ["-c", `command -v ${args[1]}`], { encoding: "utf8", timeout: PDF_TIMEOUT_MS })
481
- : spawnSync(command, args, {
482
- encoding: "utf8",
483
- maxBuffer: MAX_DOCUMENT_BYTES,
484
- timeout: PDF_TIMEOUT_MS,
485
- stdio: [input ?? "ignore", "pipe", "pipe"]
486
- });
493
+ ): DocumentCommandResult {
494
+ if (command === "command") return probeExecutableOnPath(args[1] ?? "");
495
+ const result = spawnSync(command, args, {
496
+ encoding: "utf8",
497
+ maxBuffer: MAX_DOCUMENT_BYTES,
498
+ timeout: PDF_TIMEOUT_MS,
499
+ stdio: [input ?? "ignore", "pipe", "pipe"]
500
+ });
487
501
  return {
488
502
  status: result.status,
489
503
  stdout: result.stdout ?? "",
@@ -12,17 +12,8 @@ import {
12
12
  globMatchesPath, matchesProtectedPath, normalizePathCandidate, matchesAnyPath
13
13
  } from "./policy-core.js";
14
14
  import { extractShellWritePathCandidates, shellHasFileWriteRedirection } from "./shell-write-targets.js";
15
- import {
16
- commandMatchesVerifyPlan,
17
- createBashResultLedger,
18
- findMatchingObservedBashResult,
19
- readObservedBashResults
20
- } from "./runtime-evidence.js";
21
- import {
22
- findPackageRoot,
23
- findPlatformRoot,
24
- readJsonFile
25
- } from "./guard-io.js";
15
+ import { commandMatchesVerifyPlan, createBashResultLedger, findMatchingObservedBashResult, readObservedBashResults } from "./runtime-evidence.js";
16
+ import { findPackageRoot, findPlatformRoot, readJsonFile } from "./guard-io.js";
26
17
  import {
27
18
  DOCUMENT_EXTENSIONS,
28
19
  extractDocument,
@@ -49,10 +40,13 @@ import * as mcpActions from "../mcp/mcp-command-actions.js";
49
40
  import { buildExtendingProfile, resolveProjectProfileDocument } from "../capabilities/project-profile.js";
50
41
  import {
51
42
  resolveCapabilityProfileDocument,
52
- verifyCapabilityLock,
53
43
  writeJsonAtomic,
54
44
  writeProfileLockAtomic
55
45
  } from "../capabilities/capability-core.js";
46
+ import {
47
+ createCapabilityVerificationCache,
48
+ verifyProjectCapabilityStateCached
49
+ } from "../capabilities/capability-verification-cache.js";
56
50
  import { resolveCapabilitySourceRoots } from "../capabilities/capability-sources.js";
57
51
  import {
58
52
  actionTextMatchesAny, actionTokens, classifyActionTokenSequence, classifyExplicitActionValues, classifyToolNameAction,
@@ -91,6 +85,9 @@ import { completeTaskDigestRefresh } from "./task-digest-migration.js";
91
85
  import { WORKING_TREE_DIGEST_ALGORITHM, isCurrentWorkingTreeDigest, workingTreeObservation, workingTreeSnapshotUsesCurrentAlgorithm } from "./working-tree-digest.js";
92
86
  import { appendJsonlBounded } from "./state-retention.js";
93
87
  import { ensurePrivateStateDirectory, resolveLocalStatePath } from "./local-state-path.js";
88
+ import { piApprovalBroker, type ApprovalActionDraft } from "../runtime/inspection/approval-broker.ts";
89
+ import { inspectTaskControlState } from "../runtime/inspection/task-control-journal.ts";
90
+ import { createSourceMutationGuardBindings } from "../runtime/policy/source-mutation-guard-binding.ts";
94
91
  import {
95
92
  TOOL_RESULT_CAPTURE_MAX_CHARS,
96
93
  TOOL_RESULT_COMPACT_CHAR_THRESHOLD,
@@ -157,6 +154,7 @@ import { buildLiveTaskStatus, formatLiveTaskStatus } from "../runtime/product/op
157
154
  import { buildTaskEfficiencyMetrics } from "../runtime/product/efficiency-metrics.ts";
158
155
  import { performanceReviewToolDecision, performanceReviewToolKind } from "../runtime/quality/performance-assurance.ts";
159
156
  import { expectedModelMutationProof } from "../runtime/quality/model-mutation-proof.ts";
157
+ import { captureVerifierFileSnapshot } from "../runtime/inspection/verifier-snapshot-store.ts";
160
158
  import { prefixCompletions, registerPiagentTool, registerRuntimeCommand, registerRuntimeTool } from "../runtime/registration/extension-registration.ts";
161
159
  import { FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP, ONBOARDING_COMMAND_ACTIONS, WORKFLOW_COMMAND_EXCLUSIONS } from "../runtime/registration/operator-catalogs.ts";
162
160
  import { registerPiagentStatusCommand } from "../runtime/registration/runtime-model-status.ts";
@@ -715,7 +713,15 @@ type ProjectCapabilityState = {
715
713
  filesystemWrite?: string[];
716
714
  };
717
715
 
718
- function verifyProjectCapabilityState(extensionDir: string, cwd: string, projectTrusted: boolean): ProjectCapabilityState {
716
+ const capabilityVerificationCache = createCapabilityVerificationCache();
717
+ const sessionCapabilityDigests = new Map<string, string>();
718
+
719
+ function verifyProjectCapabilityState(
720
+ extensionDir: string,
721
+ cwd: string,
722
+ projectTrusted: boolean,
723
+ options: { allowRepin?: boolean; forceFull?: boolean; sessionId?: string } = {}
724
+ ): ProjectCapabilityState {
719
725
  if (process.env.PIAGENT_PROFILE?.trim()) return { ok: true };
720
726
  if (!projectTrusted) return { ok: true };
721
727
  const profilePath = projectProfilePath(cwd);
@@ -725,46 +731,24 @@ function verifyProjectCapabilityState(extensionDir: string, cwd: string, project
725
731
  // would skip the lock entirely for every project that references an adapter.
726
732
  const stored = readJsonFile<ProjectProfile>(profilePath);
727
733
  if (!stored) return { ok: true };
728
- let profile: ProjectProfile;
729
734
  try {
730
- profile = resolveProjectProfileDocument(PLATFORM_ROOT, stored).profile as ProjectProfile;
735
+ const profile = resolveProjectProfileDocument(findPlatformRoot(extensionDir), stored).profile;
736
+ if (!Array.isArray(profile.capabilityPacks)) return { ok: true };
731
737
  } catch (error) {
732
738
  return { ok: false, reason: error instanceof Error ? error.message : String(error) };
733
739
  }
734
- if (!Array.isArray(profile.capabilityPacks)) return { ok: true };
735
740
  const lockPath = path.join(cwd, ".pi", "piagent-profile.lock.json");
736
741
  if (!fs.existsSync(lockPath)) return { ok: false, reason: "Capability lock is missing. Reapply the project profile." };
737
742
  const lock = readJsonFile<Record<string, unknown>>(lockPath);
738
743
  if (!lock) return { ok: false, reason: "Capability lock is unreadable. Reapply the project profile." };
739
744
  try {
740
- const verification = verifyCapabilityLock(findPlatformRoot(extensionDir), profilePath, lock, {
741
- packageSource: projectPackageSource(cwd),
742
- extraRoots: capabilitySourceRoots(cwd, profile)
745
+ return verifyProjectCapabilityStateCached({
746
+ cache: capabilityVerificationCache, cwd, platformRoot: findPlatformRoot(extensionDir), profilePath, lockPath,
747
+ lockDocument: lock, storedProfile: stored, packageSource: projectPackageSource, extraRoots: capabilitySourceRoots,
748
+ writeLock: writeJsonAtomic, allowRepin: options.allowRepin, forceFull: options.forceFull,
749
+ sessionDigest: options.sessionId ? sessionCapabilityDigests.get(`${cwd}\0${options.sessionId}`) : undefined,
750
+ rememberSessionDigest: options.sessionId ? (digest: string) => sessionCapabilityDigests.set(`${cwd}\0${options.sessionId}`, digest) : undefined
743
751
  });
744
- if (verification.status === "blocked") {
745
- return {
746
- ok: false,
747
- reason: `Capability lock does not match what this project agreed to: ${verification.reasons.join("; ")}. Reapply the project profile.`
748
- };
749
- }
750
- const granted = {
751
- filesystemRead: verification.expected.permissions.filesystemRead,
752
- filesystemWrite: verification.expected.permissions.filesystemWrite
753
- };
754
- if (verification.status !== "repin") return { ok: true, ...granted };
755
-
756
- // The grant is unchanged and only the platform build moved. Record the new
757
- // build instead of stopping the session, which is what turned every release
758
- // into a chore in every project.
759
- try {
760
- writeJsonAtomic(lockPath, verification.expected);
761
- } catch (error) {
762
- return {
763
- ok: false,
764
- reason: `Capability lock is behind the installed platform and could not be rewritten: ${error instanceof Error ? error.message : String(error)}`
765
- };
766
- }
767
- return { ok: true, repinned: verification.reasons.join("; "), ...granted };
768
752
  } catch (error) {
769
753
  return { ok: false, reason: `Capability validation failed: ${error instanceof Error ? error.message : String(error)}` };
770
754
  }
@@ -2199,10 +2183,12 @@ function recordObservedTaskVerification(
2199
2183
  isError?: boolean;
2200
2184
  recordedAt?: string;
2201
2185
  outputText?: string;
2186
+ toolCallId?: string;
2202
2187
  },
2203
2188
  pendingContext: ObservedTaskContext[],
2204
2189
  maxManifestFiles: number, shellSnapshotBefore?: Record<string, string>,
2205
- eventTree?: ReturnType<typeof workingTreeObservation>
2190
+ eventTree?: ReturnType<typeof workingTreeObservation>,
2191
+ readProtectedPaths: string[] = []
2206
2192
  ): TaskContract | undefined {
2207
2193
  const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
2208
2194
  if (!task || task.trace.outcome !== "pending") return;
@@ -2231,19 +2217,31 @@ function recordObservedTaskVerification(
2231
2217
  ));
2232
2218
  const contextAdded = mergeObservedTaskContext(task, pendingContext, maxManifestFiles, redactText);
2233
2219
  if (!duplicate) {
2220
+ const evidenceRecordedAt = nowIso();
2221
+ const observedAt = observed.recordedAt ?? evidenceRecordedAt;
2234
2222
  task.verifyEvidence.push({
2235
2223
  command: redactText(command),
2236
2224
  exitCode,
2237
2225
  summary: `Runtime observed configured verifier exit ${exitCode} (${classification.category}${classification.retryable ? ", retryable" : ""}).`,
2238
- recordedAt: nowIso(),
2226
+ recordedAt: evidenceRecordedAt,
2239
2227
  observed: true,
2240
- observedAt: observed.recordedAt ?? nowIso(),
2228
+ observedAt,
2241
2229
  isError: observed.isError === true,
2242
2230
  matchedProfileCommand: true,
2243
2231
  preWorkingTreeDigest,
2244
2232
  workingTreeDigest: currentDigest
2245
2233
  });
2246
2234
  task.verifyEvidence = task.verifyEvidence.slice(-100);
2235
+ try {
2236
+ captureVerifierFileSnapshot({
2237
+ projectRoot: ctx.cwd, taskId: task.taskId, taskRunId: task.taskRunId, sessionId: task.sessionId,
2238
+ toolCallId: observed.toolCallId ?? "", commandHash: observed.commandHash, observedAt,
2239
+ capturedAt: evidenceRecordedAt, exitCode, treeDigest: currentDigest, snapshot: currentDigests,
2240
+ protectedPaths: readProtectedPaths
2241
+ });
2242
+ } catch (error) {
2243
+ ctx.ui.notify(`Piagent could not persist verifier file snapshot: ${error instanceof Error ? error.message : String(error)}`, "warn");
2244
+ }
2247
2245
  }
2248
2246
 
2249
2247
  const hasChanges = taskChangedFileEvidence(ctx.cwd, task, currentDigests).expected.length > 0;
@@ -4045,6 +4043,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4045
4043
  const runtimeState = new RuntimeSessionState({
4046
4044
  maxObservedContext: contextBudgetConfig(policy).maxManifestFiles
4047
4045
  });
4046
+ const sourceMutationGuardBindings = createSourceMutationGuardBindings(policy, loadProfileFromContext);
4048
4047
  const runtimeSnapshotCapture = new RuntimeSnapshotCapture(), runtimeVersions = readRuntimeVersionMetadata(PLATFORM_ROOT);
4049
4048
  const solverShadow = solverMode === "off" ? undefined : new SolverShadowRuntime(solverMode);
4050
4049
  const modelRouteRuntime = new ModelRouteRuntime(parentRoutingMode, routingObjective);
@@ -4253,6 +4252,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4253
4252
  const activityInspector = registerActivityInspector(pi, {
4254
4253
  activeTask: (ctx) => activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined,
4255
4254
  readEvents: (cwd) => readContextTelemetry(cwd, { limit: 5_000 }) as any[],
4255
+ protectedPaths: (ctx) => effectiveProtectedPaths(policy, loadProfile(ctx.cwd)).readProtectedPaths,
4256
4256
  selectAction: selectRuntimeAction,
4257
4257
  emit: emitRuntimeMessage
4258
4258
  });
@@ -4273,13 +4273,15 @@ export default function piagentGuard(pi: ExtensionAPI) {
4273
4273
  resolveTaskAny: (cwd, reference) => resolveTaskContract(cwd, reference, undefined) as TaskContract | undefined,
4274
4274
  bindTask: bindSessionTask,
4275
4275
  writeTask,
4276
- capabilityState: (ctx) => verifyProjectCapabilityState(extensionDir, ctx.cwd, ctx.isProjectTrusted()),
4276
+ capabilityState: (ctx) => verifyProjectCapabilityState(extensionDir, ctx.cwd, ctx.isProjectTrusted(), { allowRepin: true, forceFull: true, sessionId: ctx.sessionManager.getSessionId() }),
4277
4277
  permissionProfile: (ctx, profile) => resolvePermissionProfile(profile, policy, permissionOverrideFromContext(ctx)),
4278
4278
  legacyProjectWarning: legacyProjectStateWarning,
4279
4279
  mcpReadinessNotice,
4280
4280
  updateAvailabilityNotice,
4281
4281
  contextExcludePatterns: (profile) => contextIndexExcludePatterns(policy, profile),
4282
- inspectResume: inspectTaskResumeState,
4282
+ inspectResume: (cwd, task, sessionId) => inspectTaskResumeState(cwd, task, sessionId, undefined, {
4283
+ protectedPaths: effectiveProtectedPaths(policy, loadProfile(cwd)).readProtectedPaths
4284
+ }),
4283
4285
  syncTrajectory: (ctx, task) => {
4284
4286
  const snapshot = workingTreeSnapshot(ctx.cwd) as Record<string, string>;
4285
4287
  const resume = workingTreeSnapshotHasUnavailableEvidence(snapshot)
@@ -4297,7 +4299,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4297
4299
  })));
4298
4300
  },
4299
4301
  telemetry,
4300
- afterStart: activityInspector.refresh
4302
+ afterStart: async (ctx) => { sourceMutationGuardBindings.bind(ctx); await activityInspector.refresh(ctx); }
4301
4303
  });
4302
4304
 
4303
4305
  registerSessionHooks(pi, {
@@ -4311,7 +4313,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
4311
4313
  flushObservedTaskContext,
4312
4314
  onTurnEnd: activityInspector.refresh,
4313
4315
  onAgentSettled: activityInspector.refresh,
4314
- beforeShutdown: activityInspector.dispose
4316
+ beforeShutdown: (ctx) => {
4317
+ sourceMutationGuardBindings.unbind(ctx);
4318
+ activityInspector.dispose(ctx);
4319
+ sessionCapabilityDigests.delete(`${ctx.cwd}\0${ctx.sessionManager.getSessionId()}`);
4320
+ }
4315
4321
  });
4316
4322
 
4317
4323
  registerInputHook(pi, {
@@ -4322,7 +4328,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4322
4328
  const projectTrusted = ctx.isProjectTrusted();
4323
4329
  const profile = loadProfileFromContext(ctx);
4324
4330
  const permissionProfile = resolvePermissionProfile(profile, policy, permissionOverrideFromContext(ctx));
4325
- const capabilityState = verifyProjectCapabilityState(extensionDir, ctx.cwd, projectTrusted);
4331
+ const capabilityState = verifyProjectCapabilityState(extensionDir, ctx.cwd, projectTrusted, { sessionId: ctx.sessionManager.getSessionId() });
4326
4332
  return {
4327
4333
  roots: resolveDocumentRoots({
4328
4334
  cwd: ctx.cwd,
@@ -4505,6 +4511,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
4505
4511
  if (reviewDecision) runtimeState.denyPerformanceReviewTool(task.taskRunId);
4506
4512
  return reviewDecision;
4507
4513
  },
4514
+ beforeStart: (_event, ctx) => {
4515
+ const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
4516
+ const control = task?.trace.outcome === "pending" ? inspectTaskControlState(ctx.cwd, task) : null;
4517
+ if (control?.dispatchBlocked) return { block: true, reason: `Task lifecycle control blocks tool start while state is ${control.state}.` };
4518
+ },
4508
4519
  afterDecision: (_event, ctx, metadata) => {
4509
4520
  if (metadata.allowed) return;
4510
4521
  const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId()) as TaskContract | undefined;
@@ -4572,7 +4583,7 @@ export default function piagentGuard(pi: ExtensionAPI) {
4572
4583
  const eventInput = isPlainRecord(event.input) ? event.input : {};
4573
4584
  const backendDecision = executionBackendToolDecision(isTaskMutationTool(event.toolName, eventInput));
4574
4585
  if (!backendDecision.allowed) return { block: true, reason: backendDecision.reason };
4575
- const capabilityState = verifyProjectCapabilityState(extensionDir, ctx.cwd, projectTrusted);
4586
+ const capabilityState = verifyProjectCapabilityState(extensionDir, ctx.cwd, projectTrusted, { sessionId: ctx.sessionManager.getSessionId() });
4576
4587
  const recoveryTools = new Set(["piagent_profile_options", "piagent_profile_apply", "piagent_context", "read", "grep", "find", "ls"]);
4577
4588
  if (!capabilityState.ok && !recoveryTools.has(event.toolName)) {
4578
4589
  return { block: true, reason: capabilityState.reason ?? "Capability lock validation failed." };
@@ -4639,13 +4650,13 @@ export default function piagentGuard(pi: ExtensionAPI) {
4639
4650
  if (approvalDecision.block) {
4640
4651
  return { block: true, reason: approvalDecision.reason };
4641
4652
  }
4642
-
4643
4653
  let shellProjectMutation = false;
4644
4654
  let shellMutationTargets: string[] = [];
4645
4655
  let shellMutationTargetBounded = false;
4646
4656
  let configuredVerifierShell = false;
4647
4657
  let authorizedShellCommand = "";
4648
4658
  let authorizedShellSegments: Array<{ words: string[] }> = [];
4659
+ let pendingHumanApproval: { prompt: string; title: string; action: Omit<ApprovalActionDraft, "treePrecondition"> } | null = null;
4649
4660
  if (SHELL_TOOL_NAMES.has(event.toolName)) {
4650
4661
  const shellInput = extractShellCommandInput(toolInput);
4651
4662
  if (!shellInput.command) {
@@ -4703,17 +4714,14 @@ export default function piagentGuard(pi: ExtensionAPI) {
4703
4714
  return { block: true, reason: unresolvedExpansionReason("Command", unresolvedExpansions) };
4704
4715
  }
4705
4716
 
4706
- const confirmationReasons = execDecision.mode !== "off" && execDecision.decision === "prompt"
4707
- ? [...execDecision.reasons]
4708
- : [];
4717
+ const confirmationReasons = execDecision.mode !== "off" && execDecision.decision === "prompt" ? [...execDecision.reasons] : [];
4709
4718
  const externalReason = findShellExternalConfirmationReason(execDecision.segments, externalActionPolicyConfig(policy));
4710
4719
  if (externalReason) confirmationReasons.push(externalReason);
4711
4720
  if (confirmationReasons.length > 0) {
4712
- const ok = await ctx.ui.confirm(
4713
- `Command requires confirmation.\n\n${confirmationReasons.join("\n")}\n\nAllow?`,
4714
- "Piagent exec policy confirmation"
4715
- );
4716
- if (!ok) return { block: true, reason: `User denied command: ${confirmationReasons.join("; ")}` };
4721
+ pendingHumanApproval = { prompt: `Command requires confirmation.\n\n${confirmationReasons.join("\n")}\n\nAllow?`, title: "Piagent exec policy confirmation",
4722
+ action: { kind: externalReason ? "external-provider-action" : "workspace-patch", preconditionClass: externalReason ? "runtime-only" : "workspace-tree", toolName: event.toolName, rawAction: { toolName: event.toolName, command, input: preparedInput.input },
4723
+ commandPreview: command, parameterPreview: preparedInput.confirmationSummary ?? "Shell command", targetPaths: shellMutationTargets, targetSummaries: confirmationReasons, provider: externalReason ? "shell-external" : null, urlOrigin: null,
4724
+ requestedScope: "one-shell-command", reason: confirmationReasons.join("; "), riskClass: shellProjectMutation ? "high" : "medium", allowConsequence: "Run this exact command once after the guard rechecks the task and working tree.", denyConsequence: "Block this command and return the denial to the active Pi operation." } };
4717
4725
  }
4718
4726
  }
4719
4727
 
@@ -4725,7 +4733,6 @@ export default function piagentGuard(pi: ExtensionAPI) {
4725
4733
  if (proxyShellDecision.block) {
4726
4734
  return { block: true, reason: proxyShellDecision.reason };
4727
4735
  }
4728
-
4729
4736
  const policyToolIdentity = preparedInput.proxyToolName ?? event.toolName;
4730
4737
  const usesKnownExternalProvider = externalActionPolicyConfig(policy).providerKeywords
4731
4738
  .some((provider) => actionTextMatchesAny(policyToolIdentity, [provider]));
@@ -4762,16 +4769,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
4762
4769
  }
4763
4770
  : classifiedExternalAction;
4764
4771
  if (externalAction.decision === "confirm") {
4765
- const inputSummary = preparedInput.confirmationSummary
4766
- ? `\ninput: ${preparedInput.confirmationSummary}`
4767
- : "";
4768
- const ok = await ctx.ui.confirm(
4769
- `External provider action requires confirmation.\n\nprovider: ${externalAction.provider}\naction: ${externalAction.action}\ntool: ${event.toolName}${inputSummary}\n\nAllow?`,
4770
- "Piagent external action confirmation"
4771
- );
4772
- if (!ok) {
4773
- return { block: true, reason: `User denied external provider action: ${event.toolName}` };
4774
- }
4772
+ const inputSummary = preparedInput.confirmationSummary ? `\ninput: ${preparedInput.confirmationSummary}` : "";
4773
+ pendingHumanApproval = { prompt: `External provider action requires confirmation.\n\nprovider: ${externalAction.provider}\naction: ${externalAction.action}\ntool: ${event.toolName}${inputSummary}\n\nAllow?`, title: "Piagent external action confirmation",
4774
+ action: { kind: "external-provider-action", preconditionClass: "runtime-only", toolName: event.toolName, rawAction: { toolName: event.toolName, input: preparedInput.input, provider: externalAction.provider, action: externalAction.action },
4775
+ commandPreview: null, parameterPreview: preparedInput.confirmationSummary ?? String(externalAction.action), targetPaths: [], targetSummaries: [], provider: String(externalAction.provider),
4776
+ urlOrigin: null, requestedScope: "one-external-action", reason: `External provider action ${String(externalAction.action)} requires confirmation`, riskClass: "high", allowConsequence: "Release this exact provider action once after the guard rechecks runtime authority.", denyConsequence: "Block the provider action without changing external state." } };
4775
4777
  }
4776
4778
  }
4777
4779
 
@@ -4813,6 +4815,11 @@ export default function piagentGuard(pi: ExtensionAPI) {
4813
4815
  verificationCarrier: SHELL_TOOL_NAMES.has(event.toolName)
4814
4816
  });
4815
4817
  if (phaseMutationDecision) return phaseMutationDecision;
4818
+ if (pendingHumanApproval && !sessionTask) {
4819
+ const approval = await piApprovalBroker.request({ cwd: ctx.cwd, rawSessionId: ctx.sessionManager.getSessionId(), toolCallId: String(event.toolCallId), expectedTask: null, action: { ...pendingHumanApproval.action, preconditionClass: "runtime-only", treePrecondition: null }, terminalConfirm: () => ctx.ui.confirm(pendingHumanApproval!.prompt, pendingHumanApproval!.title) });
4820
+ if (!approval.allowed) return { block: true, reason: pendingHumanApproval.action.kind === "external-provider-action" && !SHELL_TOOL_NAMES.has(event.toolName) ? `User denied external provider action: ${event.toolName}` : `User denied command: ${pendingHumanApproval.action.reason}` };
4821
+ pendingHumanApproval = null;
4822
+ }
4816
4823
  if (sessionTask && sessionTask.trace.outcome !== "pending" && (shellProjectMutation || directProjectMutation)) {
4817
4824
  return {
4818
4825
  block: true,
@@ -4863,6 +4870,17 @@ export default function piagentGuard(pi: ExtensionAPI) {
4863
4870
  }
4864
4871
  }
4865
4872
 
4873
+ if (pendingHumanApproval) {
4874
+ const currentTreeDigest = semanticSnapshot && !workingTreeSnapshotHasUnavailableEvidence(semanticSnapshot) ? workingTreeEvidenceDigest(semanticSnapshot) : null;
4875
+ const treePrecondition = pendingHumanApproval.action.preconditionClass === "runtime-only" || !currentTreeDigest ? null
4876
+ : { workspaceRevision: `workspace-rev.${crypto.createHash("sha256").update(currentTreeDigest).digest("hex")}`, indexRevision: null, preimageDigest: currentTreeDigest };
4877
+ const approval = await piApprovalBroker.request({ cwd: ctx.cwd, rawSessionId: ctx.sessionManager.getSessionId(), toolCallId: String(event.toolCallId), expectedTask: sessionTask ? { taskId: sessionTask.taskId, taskRunId: sessionTask.taskRunId } : null,
4878
+ action: { ...pendingHumanApproval.action, treePrecondition }, terminalConfirm: () => ctx.ui.confirm(pendingHumanApproval!.prompt, pendingHumanApproval!.title),
4879
+ recheck: () => !treePrecondition || (() => { const current = workingTreeSnapshot(ctx.cwd);
4880
+ return !workingTreeSnapshotHasUnavailableEvidence(current) && workingTreeEvidenceDigest(current) === treePrecondition.preimageDigest; })() });
4881
+ if (!approval.allowed) return { block: true, reason: pendingHumanApproval.action.kind === "external-provider-action" && !SHELL_TOOL_NAMES.has(event.toolName) ? `User denied external provider action: ${event.toolName}` : `User denied command: ${pendingHumanApproval.action.reason}` };
4882
+ if (!approval.consume()) return { block: true, reason: "Approval became stale before tool start; the action was blocked." };
4883
+ }
4866
4884
  if (
4867
4885
  sessionTask?.trace.outcome === "pending"
4868
4886
  && sessionTask.changeMode === "source-change"
@@ -4872,7 +4890,6 @@ export default function piagentGuard(pi: ExtensionAPI) {
4872
4890
  }
4873
4891
  }
4874
4892
  });
4875
-
4876
4893
  const registrationDeps = {
4877
4894
  CONTEXT_INDEX_EDGE_KINDS, CONTEXT_INDEX_NODE_KINDS, DEFAULT_MAX_TASK_ATTEMPTS, FRESH_COMMAND_ACTIONS, FRESH_COMMAND_HELP,
4878
4895
  ONBOARDING_COMMAND_ACTIONS, ORCHESTRATION_ROLES, PIAGENT_TOOL_NAMES, READ_ONLY_TOOL_NAMES, REVIEW_LENSES,
@@ -0,0 +1,2 @@
1
+ export function redactSensitiveText(input: unknown): { text: string; redacted: boolean };
2
+ export function redactForStorage(value: unknown): unknown;