@piagent/platform 1.5.5 → 1.6.1

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 (323) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +25 -21
  3. package/README.vi.md +34 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +92 -0
  24. package/benchmarks/production-v1/suite.json +46 -3
  25. package/catalog/capabilities.json +18 -18
  26. package/docs/authority-manifest.md +1 -1
  27. package/docs/auto-delegation-policy.md +38 -73
  28. package/docs/command-reference-vietnamese.md +27 -35
  29. package/docs/context-engine.md +12 -4
  30. package/docs/context-window-policy.md +37 -1
  31. package/docs/distribution-standard.md +7 -7
  32. package/docs/en/architecture.md +27 -13
  33. package/docs/languages.json +4 -4
  34. package/docs/model-options.md +8 -5
  35. package/docs/operator-manual-vietnamese.md +27 -45
  36. package/docs/project-adapters.md +2 -2
  37. package/docs/publishing-for-teams.md +10 -0
  38. package/docs/quality-benchmark.md +427 -62
  39. package/docs/release-install-policy.md +10 -9
  40. package/docs/runtime-harness-standard.md +3 -3
  41. package/docs/runtime-policy-design.md +4 -4
  42. package/docs/runtime-quality-baseline.md +13 -6
  43. package/docs/security-threat-model.md +2 -2
  44. package/docs/subagent-orchestration-capabilities.md +19 -51
  45. package/docs/subagents-and-multiagent.md +21 -47
  46. package/docs/task-implementation-contract.md +3 -3
  47. package/docs/task-lifecycle-vietnamese.md +3 -3
  48. package/docs/team-onboarding.md +2 -2
  49. package/docs/usage-observability.md +7 -2
  50. package/docs/vi/architecture.md +27 -13
  51. package/docs-site/architecture.html +1 -1
  52. package/docs-site/benchmark.html +89 -95
  53. package/docs-site/commands.html +1 -1
  54. package/docs-site/content/benchmark.html +88 -94
  55. package/docs-site/content/en/benchmark.html +134 -90
  56. package/docs-site/content/en/index.html +19 -19
  57. package/docs-site/content/en/runtime.html +4 -4
  58. package/docs-site/content/en/whats-new.html +12 -1
  59. package/docs-site/content/en/workflows.html +2 -2
  60. package/docs-site/content/index.html +19 -19
  61. package/docs-site/content/runtime.html +4 -4
  62. package/docs-site/content/whats-new.html +12 -1
  63. package/docs-site/content/workflows.html +2 -2
  64. package/docs-site/context-engine.html +1 -1
  65. package/docs-site/en/architecture.html +1 -1
  66. package/docs-site/en/benchmark.html +135 -91
  67. package/docs-site/en/commands.html +1 -1
  68. package/docs-site/en/context-engine.html +1 -1
  69. package/docs-site/en/index.html +20 -20
  70. package/docs-site/en/mcp-approval.html +1 -1
  71. package/docs-site/en/mcp-auth.html +1 -1
  72. package/docs-site/en/mcp-servers.html +1 -1
  73. package/docs-site/en/mcp.html +1 -1
  74. package/docs-site/en/permissions.html +1 -1
  75. package/docs-site/en/profiles.html +1 -1
  76. package/docs-site/en/quickstart.html +1 -1
  77. package/docs-site/en/runtime.html +5 -5
  78. package/docs-site/en/scope.html +1 -1
  79. package/docs-site/en/security.html +1 -1
  80. package/docs-site/en/team.html +1 -1
  81. package/docs-site/en/whats-new.html +13 -2
  82. package/docs-site/en/workflows.html +3 -3
  83. package/docs-site/index.html +20 -20
  84. package/docs-site/mcp-approval.html +1 -1
  85. package/docs-site/mcp-auth.html +1 -1
  86. package/docs-site/mcp-servers.html +1 -1
  87. package/docs-site/mcp.html +1 -1
  88. package/docs-site/permissions.html +1 -1
  89. package/docs-site/profiles.html +1 -1
  90. package/docs-site/quickstart.html +1 -1
  91. package/docs-site/runtime.html +5 -5
  92. package/docs-site/scope.html +1 -1
  93. package/docs-site/security.html +1 -1
  94. package/docs-site/team.html +1 -1
  95. package/docs-site/whats-new.html +13 -2
  96. package/docs-site/workflows.html +3 -3
  97. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  98. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  99. package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
  100. package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
  101. package/evals/fixtures/helper-request.invalid.json +1 -1
  102. package/evals/fixtures/helper-request.valid.json +1 -1
  103. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  104. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  105. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
  106. package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
  107. package/evals/fs4-readiness-matrix.v1.json +2 -2
  108. package/evals/fs5-causal-arm.v1.json +3 -3
  109. package/evals/fs5-pilot-protocol.v1.json +3 -3
  110. package/evals/fs5-pilot-protocol.v2.json +8 -8
  111. package/evals/fs5-pilot-protocol.v3.json +8 -8
  112. package/evals/fs5-pilot-protocol.v4.json +7 -6
  113. package/evals/fs5-pilot-protocol.v5.json +9 -7
  114. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  115. package/evals/runtime-conformance-v1/README.md +43 -0
  116. package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
  117. package/evals/runtime-conformance-v1/lane.json +57 -0
  118. package/evals/runtime-conformance-v1/paid-canary.json +31 -0
  119. package/evals/runtime-conformance-v1/runner.mjs +456 -0
  120. package/package.json +3 -1
  121. package/packages/piagent-core/README.md +13 -4
  122. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  123. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  124. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  125. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  126. package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
  127. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  128. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  129. package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
  130. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  131. package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
  132. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  133. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  134. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  135. package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
  136. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  137. package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
  138. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  139. package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
  140. package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
  141. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  142. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  143. package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
  144. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
  145. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  146. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  147. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  148. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  149. package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
  150. package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
  151. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  152. package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
  153. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  154. package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
  155. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  156. package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
  157. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  158. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  159. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  160. package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
  161. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  162. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  163. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  164. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  165. package/packages/piagent-core/extensions/context-engine.js +149 -156
  166. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  167. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  168. package/packages/piagent-core/extensions/context-index-policy.js +4 -0
  169. package/packages/piagent-core/extensions/context-query-paths.js +57 -0
  170. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  171. package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
  172. package/packages/piagent-core/extensions/criterion-graph.js +82 -9
  173. package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
  174. package/packages/piagent-core/extensions/guard-types.ts +6 -0
  175. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  176. package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
  177. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  178. package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
  179. package/packages/piagent-core/extensions/task-state.js +109 -142
  180. package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
  181. package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
  182. package/packages/piagent-core/package.json +1 -1
  183. package/packages/piagent-core/policies/base-policy.json +44 -2
  184. package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
  185. package/packages/piagent-core/prompts/be-to-fe.md +2 -8
  186. package/packages/piagent-core/prompts/plan.md +1 -1
  187. package/packages/piagent-core/prompts/platform-improve.md +2 -6
  188. package/packages/piagent-core/prompts/review.md +2 -7
  189. package/packages/piagent-core/prompts/scout.md +1 -1
  190. package/packages/piagent-core/prompts/task.md +4 -3
  191. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  192. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  193. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  194. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  195. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  196. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  197. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  198. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  199. package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
  200. package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
  201. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  202. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
  203. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  204. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
  205. package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
  206. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  207. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  208. package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
  209. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
  210. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  211. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  212. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
  213. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
  214. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
  215. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  216. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  217. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  218. package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
  219. package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
  220. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
  221. package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
  222. package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
  223. package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
  224. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  225. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
  226. package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
  227. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
  228. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  229. package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
  230. package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
  231. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  232. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  233. package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
  234. package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
  235. package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
  236. package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
  237. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
  238. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  239. package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
  240. package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
  241. package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
  242. package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
  243. package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
  244. package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
  245. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
  246. package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
  247. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
  248. package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
  249. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  250. package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
  251. package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
  252. package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
  253. package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
  254. package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
  255. package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
  256. package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
  257. package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
  258. package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
  259. package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
  260. package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
  261. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  262. package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
  263. package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
  264. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  265. package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
  266. package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
  267. package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
  268. package/packages/piagent-core/security/sensitive-data.js +13 -218
  269. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  270. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  271. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  272. package/packages/piagent-core/security/sensitive-text.js +234 -0
  273. package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
  274. package/packages/piagent-core/subagents/piagent-planner.md +6 -4
  275. package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
  276. package/packages/piagent-core/subagents/piagent-scout.md +4 -2
  277. package/packages/piagent-core/subagents/piagent-worker.md +7 -5
  278. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
  279. package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
  280. package/packages/piagent-webui/dist/client/index.html +1 -1
  281. package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
  282. package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
  283. package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
  284. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  285. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  286. package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
  287. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  288. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  289. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  290. package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
  291. package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
  292. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  293. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
  294. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  295. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
  296. package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
  297. package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
  298. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  299. package/packages/piagent-webui/server/transcript-projection.ts +113 -9
  300. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  301. package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
  302. package/schemas/piagent-webui/catalog-v1.json +9 -0
  303. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  304. package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
  305. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  306. package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
  307. package/schemas/project-profile.schema.json +1 -1
  308. package/schemas/task-contract.schema.json +27 -1
  309. package/scripts/benchmark-runner-core.mjs +367 -363
  310. package/scripts/benchmark-runner-finalization.mjs +313 -0
  311. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  312. package/scripts/benchmark-runner-support.mjs +200 -0
  313. package/scripts/benchmark-session.mjs +95 -41
  314. package/scripts/configure-model-scope.sh +4 -3
  315. package/scripts/configure-subagents.sh +42 -34
  316. package/scripts/helper-evaluation.mjs +2 -2
  317. package/scripts/install-global.sh +2 -2
  318. package/scripts/pi-usage-history.mjs +37 -10
  319. package/scripts/setup.sh +2 -2
  320. package/scripts/team-doctor.sh +1 -1
  321. package/scripts/verify-local.sh +10 -1
  322. package/templates/global/settings.json +2 -1
  323. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -2,7 +2,6 @@ import path from "node:path";
2
2
 
3
3
  import type { TaskContract } from "../../extensions/guard-types.ts";
4
4
  import { allVerifyCommandsPassCurrentTree } from "../../extensions/task-contract-view.js";
5
- import { criterionGraphGuidance } from "../../extensions/criterion-graph.js";
6
5
  import { replayTaskCheckpoints, taskRecoveryDecision } from "../../extensions/task-journal.js";
7
6
  import { taskDigestMigrationArchiveStatus, workingTreeSnapshot, workingTreeSnapshotHasUnavailableEvidence } from "../../extensions/task-state.js";
8
7
  import { workingTreeEvidenceDigest } from "../../extensions/task-lifecycle.js";
@@ -10,12 +9,21 @@ import { hashEvidenceCommand } from "../../extensions/runtime-evidence.js";
10
9
  import { WORKING_TREE_DIGEST_ALGORITHM, isCurrentWorkingTreeDigest, workingTreeSnapshotUsesCurrentAlgorithm } from "../../extensions/working-tree-digest.js";
11
10
  import { handoffProjectionPath, readHandoffProjection } from "./handoff-projection.ts";
12
11
  import { inspectTaskAuthorityResumePolicy, type AuthorityResumeDecision } from "../policy/authority-resume-policy.ts";
12
+ import { presentedAcceptanceCriteria, presentedCriterionGraphGuidance } from "../session/task-contract-presentation.ts";
13
+ import { operatorRequestCarryLines } from "../session/operator-request-carry.ts";
13
14
  import { readTrajectoryStore } from "../trajectory/trajectory-store.ts";
14
15
  import { findVerifierFileSnapshot, inspectVerifierStaleness, readVerifierFileSnapshots } from "../inspection/verifier-snapshot-store.ts";
15
16
 
16
17
  export const RESUME_STATE_VERSION = "resume-v1" as const;
17
18
  export const RESUME_CONTEXT_VERSION = "resume-context-v1" as const;
18
- export const RESUME_CONTEXT_MAX_CHARS = 6_000;
19
+ export const RESUME_CONTEXT_MAX_CHARS = 12_000;
20
+ // Validated task text is bounded in Unicode code points by JSON Schema. Astral
21
+ // characters occupy two UTF-16 code units, so the exceptional resume ceiling
22
+ // must cover the worst valid lossless task even though the normal target stays
23
+ // at 12k. This overflow is explicit and is never used for routine compaction.
24
+ export const RESUME_CONTEXT_LOSSLESS_MAX_CHARS = 40_000;
25
+ export const RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER = `[Piagent lossless resume overflow: the ${RESUME_CONTEXT_MAX_CHARS}-character target was exceeded; complete authoritative task truth is retained.]`;
26
+ export const LEGACY_TASK_TRUTH_OVERFLOW_MARKER = `[Piagent legacy-task-truth-overflow: automatic continuation is paused because lossless task truth exceeds the ${RESUME_CONTEXT_LOSSLESS_MAX_CHARS}-character exceptional ceiling.]`;
19
27
  type ResumePlanStep = {
20
28
  id: string;
21
29
  title: string;
@@ -153,44 +161,129 @@ function limitedLines(values: unknown[], maximumItems: number, maximumChars: num
153
161
  return selected;
154
162
  }
155
163
 
164
+ function losslessManualResumeContent(task: TaskContract): string {
165
+ return [
166
+ "[Piagent durable task resume]",
167
+ RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
168
+ `Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
169
+ `Goal: ${task.summary}`,
170
+ `Expected output: ${task.expectedOutput}`,
171
+ "Acceptance focus:", ...presentedAcceptanceCriteria(task).map((value, index) => `- C${index + 1}: ${value}`),
172
+ "Exact verifier commands:", ...task.verifyCommands.map((value, index) => `${index + 1}. ${value}`),
173
+ "Continue only under the current runtime-enforced task state and every durable task clause above; do not infer progress from transcript memory."
174
+ ].join("\n");
175
+ }
176
+
177
+ /** Exact UTF-16 traffic size for the smallest lossless automatic legacy resume. */
178
+ export function legacyTaskResumeTruthChars(task: TaskContract): number {
179
+ return operatorRequestCarryLines(task).length > 0 ? 0 : losslessManualResumeContent(task).length;
180
+ }
181
+
182
+ function legacyTaskTruthOverflowsAutomaticResume(task: TaskContract): boolean {
183
+ const chars = legacyTaskResumeTruthChars(task);
184
+ return chars > RESUME_CONTEXT_LOSSLESS_MAX_CHARS;
185
+ }
186
+
187
+ function legacyOverflowResumeContent(task: TaskContract): string {
188
+ return [
189
+ "[Piagent durable task resume]",
190
+ LEGACY_TASK_TRUTH_OVERFLOW_MARKER,
191
+ `Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
192
+ "The durable task remains byte-for-byte intact, but its complete acceptance truth cannot be injected safely in one automatic resume.",
193
+ "Do not mutate or auto-continue this task. Ask the operator to re-intake the original request as a new bounded task or provide an explicit compact handoff."
194
+ ].join("\n");
195
+ }
196
+
156
197
  export function buildTaskResumeContext(task: TaskContract, resume: ResumeState): {
157
198
  customType: "piagent-runtime-task-resume";
158
199
  content: string;
159
200
  details: Record<string, unknown>;
160
201
  } {
161
- const criteria = limitedLines(task.acceptanceCriteria, 8, 180, (value, index) => `- C${index + 1}: ${value}`);
202
+ if (legacyTaskTruthOverflowsAutomaticResume(task)) {
203
+ return {
204
+ customType: "piagent-runtime-task-resume",
205
+ content: legacyOverflowResumeContent(task),
206
+ details: {
207
+ resumeContextVersion: RESUME_CONTEXT_VERSION, taskId: task.taskId, taskRunId: task.taskRunId,
208
+ phase: resume.phase, checkpointId: resume.latestCheckpoint?.checkpointId ?? null,
209
+ decision: "blocked", enforcementSafe: false, verifierEvidenceCurrent: resume.verifierEvidenceCurrent,
210
+ staleVerifierEvidence: resume.staleVerifierEvidence, losslessOverflow: true,
211
+ legacyTaskTruthOverflow: true, nextAction: resume.reconstruction.nextAction
212
+ }
213
+ };
214
+ }
215
+ const operatorTruth = operatorRequestCarryLines(task);
216
+ const presentedCriteria = presentedAcceptanceCriteria(task);
217
+ const criteria = operatorTruth.length > 0
218
+ ? limitedLines(presentedCriteria, 8, 180, (value, index) => `- C${index + 1}: ${value}`)
219
+ : presentedCriteria.map((value, index) => `- C${index + 1}: ${value}`);
162
220
  const scope = limitedLines(task.scope, 8, 100, (value) => `- ${value}`);
163
221
  const plan = resume.reconstruction.plan.slice(0, 12).map((step) => (
164
222
  `- ${step.id}: ${step.status}; ${compact(step.title, 140)}${step.dependsOn.length > 0 ? `; after=${step.dependsOn.join(",")}` : ""}`
165
223
  ));
166
224
  if (resume.reconstruction.plan.length > 12) plan.push(`- [${resume.reconstruction.plan.length - 12} more steps retained in the Task Contract]`);
167
- const verifiers = limitedLines(task.verifyCommands, 8, 180, (value, index) => `${index + 1}. ${value}`);
168
- const executionMap = criterionGraphGuidance(task.criterionGraph, 8).map((line: string) => `- ${line}`);
225
+ const verifiers = task.verifyCommands.map((value, index) => `${index + 1}. ${value}`);
226
+ const executionMap = presentedCriterionGraphGuidance(task, 8).map((line: string) => `- ${line}`);
169
227
  const next = resume.reconstruction.nextAction;
170
228
  const lines = [
171
229
  "[Piagent durable task resume]",
172
230
  "This brief is reconstructed from the current Task Contract, journal, trajectory, handoff and working tree. Durable files remain authoritative.",
173
231
  `Task: ${compact(task.taskId, 160)} (${compact(task.taskRunId, 160)})`,
174
- `Goal: ${compact(task.summary, 500)}`,
175
- `Expected output: ${compact(task.expectedOutput, 400)}`,
232
+ `Goal: ${operatorTruth.length > 0 ? compact(task.summary, 500) : task.summary}`,
233
+ ...operatorTruth,
234
+ `Expected output: ${operatorTruth.length > 0 ? compact(task.expectedOutput, 400) : task.expectedOutput}`,
176
235
  "Acceptance focus:", ...criteria,
177
- "Scope:", ...scope,
236
+ "Initial focus (advisory):", ...scope,
237
+ "Initial focus guides retrieval/review and neither authorizes nor forbids mutation.",
178
238
  "Work plan/progress:", ...plan,
179
239
  ...(executionMap.length > 0 ? ["Execution map (planning only):", ...executionMap] : []),
180
240
  `Current phase/checkpoint: ${resume.phase ?? "unknown"} / ${resume.latestCheckpoint?.checkpointId ?? "none"}`,
181
241
  `Verifier state: current=${resume.verifierEvidenceCurrent}; stale=${resume.staleVerifierEvidence}; tree=${resume.currentTreeDigest}`,
182
242
  "Exact verifier commands:", ...verifiers,
183
243
  `Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 500)}`,
184
- ...(next.exactCommands.length > 0 ? ["Required commands for that action:", ...limitedLines(next.exactCommands, 8, 180, (value, index) => `${index + 1}. ${value}`)] : []),
244
+ ...(next.exactCommands.length > 0 ? ["Required commands for that action:", ...next.exactCommands.map((value, index) => `${index + 1}. ${value}`)] : []),
185
245
  `Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}; handoff=${resume.handoff.exists ? resume.handoff.path : "none"}.`,
186
246
  "Do not infer progress from transcript memory, reopen completed steps, or mutate when the next action is inspect-handoff, wait-paused, or terminal."
187
247
  ];
188
248
  let content = lines.join("\n");
189
249
  if (content.length > RESUME_CONTEXT_MAX_CHARS) {
190
- const marker = "\n[Middle details shortened; complete task truth remains in the Task Contract.]\n";
191
- const available = RESUME_CONTEXT_MAX_CHARS - marker.length;
192
- const head = Math.floor(available * 0.7);
193
- content = `${content.slice(0, head).trimEnd()}${marker}${content.slice(-(available - head)).trimStart()}`;
250
+ const boundedOperatorTruth = operatorRequestCarryLines(task);
251
+ content = boundedOperatorTruth.length > 0 ? [
252
+ "[Piagent durable task resume]",
253
+ `Task: ${compact(task.taskId, 160)} (${compact(task.taskRunId, 160)})`,
254
+ ...boundedOperatorTruth,
255
+ "Acceptance focus:", ...limitedLines(presentedAcceptanceCriteria(task), 8, 160, (value, index) => `- C${index + 1}: ${value}`),
256
+ `Current phase/checkpoint: ${resume.phase ?? "unknown"} / ${resume.latestCheckpoint?.checkpointId ?? "none"}`,
257
+ "Exact verifier commands:", ...verifiers,
258
+ `Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 400)}`,
259
+ `Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
260
+ "Continue from current source. Preserve every operator clause exactly; do not infer progress from transcript memory."
261
+ ].join("\n") : losslessManualResumeContent(task);
262
+ if (content.length > RESUME_CONTEXT_MAX_CHARS && boundedOperatorTruth.length > 0) {
263
+ content = [
264
+ "[Piagent durable task resume]",
265
+ RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
266
+ `Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
267
+ ...boundedOperatorTruth,
268
+ "Acceptance focus:", ...limitedLines(presentedAcceptanceCriteria(task), 12, 220, (value, index) => `- C${index + 1}: ${value}`),
269
+ "Exact verifier commands:", ...verifiers,
270
+ `Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 300)}`,
271
+ `Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
272
+ "Continue only from current source, the complete operator request, exact verifiers, and durable atomic acceptance facts."
273
+ ].join("\n");
274
+ if (content.length > RESUME_CONTEXT_LOSSLESS_MAX_CHARS) {
275
+ content = [
276
+ "[Piagent durable task resume]",
277
+ RESUME_CONTEXT_LOSSLESS_OVERFLOW_MARKER,
278
+ `Task: ${compact(task.taskId, 120)} (${compact(task.taskRunId, 120)})`,
279
+ ...boundedOperatorTruth,
280
+ "Exact verifier commands:", ...verifiers,
281
+ `Next safe action: ${next.action}${next.stepId ? ` (${next.stepId})` : ""}. ${compact(next.reason, 240)}`,
282
+ `Resume safety: decision=${resume.decision}; enforcementSafe=${resume.enforcementSafe}.`,
283
+ "The complete operator request above is the acceptance source of truth. Continue only from current source and these exact verifiers."
284
+ ].join("\n");
285
+ }
286
+ }
194
287
  }
195
288
  return {
196
289
  customType: "piagent-runtime-task-resume",
@@ -205,6 +298,7 @@ export function buildTaskResumeContext(task: TaskContract, resume: ResumeState):
205
298
  enforcementSafe: resume.enforcementSafe,
206
299
  verifierEvidenceCurrent: resume.verifierEvidenceCurrent,
207
300
  staleVerifierEvidence: resume.staleVerifierEvidence,
301
+ losslessOverflow: content.length > RESUME_CONTEXT_MAX_CHARS,
208
302
  nextAction: resume.reconstruction.nextAction
209
303
  }
210
304
  };
@@ -220,6 +314,7 @@ export function inspectTaskResumeState(
220
314
  const currentTreeDigest = workingTreeEvidenceDigest(currentDigests);
221
315
  const authorityPolicy = inspectTaskAuthorityResumePolicy(cwd, task);
222
316
  const archive = taskDigestMigrationArchiveStatus(cwd, task);
317
+ const legacyTruthOverflow = task.trace.outcome === "pending" && legacyTaskTruthOverflowsAutomaticResume(task);
223
318
  const algorithmReady = task.workingTreeDigestAlgorithm === WORKING_TREE_DIGEST_ALGORITHM
224
319
  && isCurrentWorkingTreeDigest(currentTreeDigest)
225
320
  && workingTreeSnapshotUsesCurrentAlgorithm(currentDigests)
@@ -265,8 +360,9 @@ export function inspectTaskResumeState(
265
360
  if (!trajectory.enforcementSafe) warnings.push(`trajectory state is unsafe: ${trajectory.warnings[0] ?? "unknown error"}`);
266
361
  if (!algorithmReady) warnings.push("working-tree digest algorithm/evidence is not current and proof-capable");
267
362
  if (!archive.valid) warnings.push(archive.reason);
363
+ if (legacyTruthOverflow) warnings.push("legacy-task-truth-overflow requires explicit operator re-intake or compact handoff");
268
364
  if (refreshRequired) warnings.push("legacy evidence was archived; run each exact configured verifier once against the current wt-content-v2 tree");
269
- const enforcementSafe = authorityReady && !identityConflict && algorithmReady && archive.valid && journal.corruptions.length === 0 && trajectory.enforcementSafe && handoffValid;
365
+ const enforcementSafe = authorityReady && !identityConflict && !legacyTruthOverflow && algorithmReady && archive.valid && journal.corruptions.length === 0 && trajectory.enforcementSafe && handoffValid;
270
366
  const recovery = refreshRequired ? { decision: "resume", reason: "Digest migration requires current exact verifier evidence." } : taskRecoveryDecision(task, journal);
271
367
  const decision: ResumeState["decision"] = terminal
272
368
  ? "terminal"
@@ -281,6 +377,8 @@ export function inspectTaskResumeState(
281
377
  : "resume";
282
378
  const reason = terminal
283
379
  ? `Task contract is immutable after ${task.trace.outcome}.`
380
+ : legacyTruthOverflow
381
+ ? "legacy-task-truth-overflow: automatic continuation is paused; ask the operator to re-intake the original request or provide a compact handoff."
284
382
  : journal.corruptions.length > 0
285
383
  ? `task journal is corrupt: ${journal.corruptions[0]}`
286
384
  : !authorityReady
@@ -11,12 +11,12 @@ import { safeTaskId } from "../../extensions/task-state.js";
11
11
  import { isCurrentWorkingTreeDigest } from "../../extensions/working-tree-digest.js";
12
12
 
13
13
  const HASH = /^[a-f0-9]{64}$/;
14
- const GLOB = /[?*[\]{}]/;
15
14
  const MAX_FILE_BYTES = 256 * 1024;
16
15
  export const MAX_SEMANTIC_REPAIR_PATHS = 12;
17
16
  export const MAX_SEMANTIC_REPAIR_REVISIONS = 2;
18
17
  export const MAX_SEMANTIC_REPAIR_MUTATIONS = 8;
19
18
  export const MAX_SEMANTIC_REPAIR_UNSUCCESSFUL_CALLS = 3;
19
+ export const BOUNDED_RECOVERY_CONFLICT_CODE = "post-verifier-model-omission";
20
20
 
21
21
  type RepairStatus = "reserved" | "authorized" | "active" | "retry-ready" | "verifier-pending" | "passed" | "cancelled" | "locked";
22
22
  type RepairPendingCall = {
@@ -105,13 +105,6 @@ function tokenHash(token: string): string {
105
105
  return crypto.createHash("sha256").update(token).digest("hex");
106
106
  }
107
107
 
108
- function exactDeclaredPath(task: TaskContract, target: string): boolean {
109
- return (task.scope ?? []).some((candidate) => {
110
- const normalized = normalizePathCandidate(candidate);
111
- return Boolean(normalized && !GLOB.test(normalized) && normalized === target);
112
- });
113
- }
114
-
115
108
  function isTestPath(file: string): boolean {
116
109
  return /(^|\/)(?:test|tests|spec|__tests__)(\/|$)|[._-](?:test|spec)\.[cm]?[jt]sx?$/i.test(file);
117
110
  }
@@ -145,12 +138,13 @@ export function decideSemanticRepairHandshake(input: {
145
138
  verifierCurrent: boolean;
146
139
  }): SemanticRepairHandshakeDecision {
147
140
  const targets = uniquePaths(input.mutationTargets), delta = new Set(uniquePaths(input.currentDeltaPaths));
141
+ const targetSet = new Set(targets);
148
142
  const empty = { authorized: false, conflictCodes: [], eligibleTargets: [], eligiblePaths: [], pathConflictCodes: {} };
149
143
  if (!input.verifierCurrent || targets.length === 0 || delta.size === 0) return empty;
150
- const candidates = uniquePaths([...delta, ...(input.task.scope ?? []).filter((item) => !GLOB.test(item))]);
144
+ const candidates = uniquePaths([...delta, ...targets]);
151
145
  const pathConflictCodes: Record<string, string[]> = {}, sourceTexts = new Map<string, string>();
152
146
  for (const file of candidates) {
153
- if (isTestPath(file) || (!delta.has(file) && !exactDeclaredPath(input.task, file))) continue;
147
+ if (isTestPath(file) || (!delta.has(file) && !targetSet.has(file))) continue;
154
148
  const source = readSmallFile(input.cwd, file), conflicts = contentLinkedConflictCodes(input.task, source);
155
149
  if (conflicts.length === 0) continue;
156
150
  pathConflictCodes[file] = conflicts;
@@ -160,7 +154,7 @@ export function decideSemanticRepairHandshake(input: {
160
154
  if (sourcePaths.length === 0) return empty;
161
155
  const relatedTests = candidates.filter((file) => (
162
156
  isTestPath(file)
163
- && (delta.has(file) || exactDeclaredPath(input.task, file))
157
+ && (delta.has(file) || targetSet.has(file))
164
158
  && sourcePaths.some((sourcePath) => acceptanceExecutableTestBinding({
165
159
  sourceEntry: { path: sourcePath, text: sourceTexts.get(sourcePath) ?? "" },
166
160
  testEntry: { path: file, text: readSmallFile(input.cwd, file) }
@@ -173,6 +167,47 @@ export function decideSemanticRepairHandshake(input: {
173
167
  return { authorized: includesConflictingSource && targets.length === eligibleTargets.length, conflictCodes, eligibleTargets, eligiblePaths, pathConflictCodes };
174
168
  }
175
169
 
170
+ /**
171
+ * Grant a single fail-closed recovery entry when the model notices an omission
172
+ * after a current exact verifier has already moved the task into verify.
173
+ *
174
+ * This is intentionally narrower than semantic repair: every target must be
175
+ * an actual current task delta, must have been observed in this task, and must
176
+ * still carry successful structured-mutation authorship for the exact task,
177
+ * run, and session. The caller separately enforces CAP-09/12/13 and every
178
+ * normal protected-path, permission, approval, and exact-target guard before
179
+ * persisting the reservation; task scope is advisory.
180
+ */
181
+ export function decideBoundedRecoveryHandshake(input: {
182
+ task: TaskContract;
183
+ mutationTargets: string[];
184
+ currentDeltaPaths: string[];
185
+ observedChangedPaths: string[];
186
+ authoredFileDigests: Record<string, string>;
187
+ verifierCurrent: boolean;
188
+ }): SemanticRepairHandshakeDecision {
189
+ const targets = uniquePaths(input.mutationTargets);
190
+ const empty = { authorized: false, conflictCodes: [], eligibleTargets: [], eligiblePaths: [], pathConflictCodes: {} };
191
+ if (!input.verifierCurrent || targets.length === 0 || targets.length > MAX_SEMANTIC_REPAIR_PATHS) return empty;
192
+
193
+ const delta = new Set(uniquePaths(input.currentDeltaPaths));
194
+ const observed = new Set(uniquePaths(input.observedChangedPaths));
195
+ const authored = new Set(uniquePaths(Object.entries(input.authoredFileDigests)
196
+ .filter(([, digest]) => typeof digest === "string" && digest.length > 0)
197
+ .map(([file]) => file)));
198
+ const eligibleTargets = targets.filter((file) => delta.has(file) && observed.has(file) && authored.has(file));
199
+ if (eligibleTargets.length !== targets.length) return empty;
200
+
201
+ const pathConflictCodes = Object.fromEntries(targets.map((file) => [file, [BOUNDED_RECOVERY_CONFLICT_CODE]]));
202
+ return {
203
+ authorized: true,
204
+ conflictCodes: [BOUNDED_RECOVERY_CONFLICT_CODE],
205
+ eligibleTargets: targets,
206
+ eligiblePaths: targets,
207
+ pathConflictCodes
208
+ };
209
+ }
210
+
176
211
  export function semanticRepairStatePath(cwd: string, taskRunId: string): string {
177
212
  return path.join(cwd, ".pi", "piagent-state", "semantic-repair", `${safeTaskId(taskRunId)}.json`);
178
213
  }
@@ -364,6 +399,7 @@ export function authorizeSemanticRepairCall(input: {
364
399
  return { handled: true, allowed: true, bypassPhase: false, state: writeState(input.cwd, state) };
365
400
  }
366
401
  if (state.status !== "active") return { handled: true, allowed: false, bypassPhase: false, reason: `semantic repair is ${state.status}` };
402
+ const boundedRecovery = state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE);
367
403
  if (input.exactVerifier) {
368
404
  if (state.successfulMutationsInRevision < 1) return denyActiveCall(input.cwd, state, "semantic repair verifier requires a successful mutation in this revision", input.recordedAt);
369
405
  state.status = "verifier-pending";
@@ -372,6 +408,7 @@ export function authorizeSemanticRepairCall(input: {
372
408
  return { handled: true, allowed: true, bypassPhase: false, state: writeState(input.cwd, state) };
373
409
  }
374
410
  if (input.projectMutation) {
411
+ if (boundedRecovery) return denyActiveCall(input.cwd, state, "bounded recovery permits exactly one successful mutation before the exact verifier", input.recordedAt);
375
412
  const targets = uniquePaths(input.targetPaths);
376
413
  if (input.targetExtractionComplete === false) return denyActiveCall(input.cwd, state, "semantic repair mutation targets are not statically complete", input.recordedAt);
377
414
  if (targets.length === 0 || targets.some((file) => !state.eligiblePaths.includes(file))) return denyActiveCall(input.cwd, state, "semantic repair mutation is outside the exact persisted grant", input.recordedAt);
@@ -402,6 +439,7 @@ export function pendingSemanticRepairCall(cwd: string, taskRunId: string, toolCa
402
439
  export function completeSemanticRepairCall(input: {
403
440
  cwd: string; taskRunId: string; toolCallId: string; success: boolean; exitCode?: number;
404
441
  currentDigest: string; changedPaths: string[]; retryableFailure?: boolean; correctiveFailure?: boolean; recordedAt?: string;
442
+ authoredChangedPaths?: string[];
405
443
  }): { result: "unmatched" | "opened" | "recorded" | "retry" | "correction" | "passed" | "cancelled" | "locked"; state?: SemanticRepairState } {
406
444
  const view = readSemanticRepairState(input.cwd, input.taskRunId), state = view.state;
407
445
  if (!view.enforcementSafe || !state?.pending || state.pending.toolCallId !== input.toolCallId) return { result: "unmatched", state };
@@ -410,6 +448,7 @@ export function completeSemanticRepairCall(input: {
410
448
  return { result: "locked", state: writeState(input.cwd, state) };
411
449
  }
412
450
  const pending = state.pending, changed = uniquePaths(input.changedPaths), treeChanged = input.currentDigest !== pending.preDigest;
451
+ const boundedRecovery = state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE);
413
452
  const now = input.recordedAt ?? new Date().toISOString();
414
453
  if (!pending.authorized) { state.status = "locked"; state.pending = null; state.updatedAt = now; return { result: "locked", state: writeState(input.cwd, state) }; }
415
454
  if (pending.kind === "verifier") {
@@ -418,12 +457,20 @@ export function completeSemanticRepairCall(input: {
418
457
  state.failedCalls += 1;
419
458
  if (treeChanged || unsuccessful(state) >= MAX_SEMANTIC_REPAIR_UNSUCCESSFUL_CALLS) state.status = "locked";
420
459
  else if (input.retryableFailure && !state.transientRetryUsed) { state.transientRetryUsed = true; state.status = "retry-ready"; }
421
- else if (input.correctiveFailure && state.revision < MAX_SEMANTIC_REPAIR_REVISIONS) { state.revision += 1; state.successfulMutationsInRevision = 0; state.status = "active"; }
460
+ else if (!boundedRecovery && input.correctiveFailure && state.revision < MAX_SEMANTIC_REPAIR_REVISIONS) { state.revision += 1; state.successfulMutationsInRevision = 0; state.status = "active"; }
422
461
  else state.status = "locked";
423
462
  const result = state.status === "retry-ready" ? "retry" : state.status === "active" ? "correction" : "locked";
424
463
  return { result, state: writeState(input.cwd, state) };
425
464
  }
426
- const unexpected = changed.length === 0 || changed.some((file) => !pending.targetPaths.includes(file) || !state.eligiblePaths.includes(file));
465
+ const authoredChanged = uniquePaths(input.authoredChangedPaths ?? []);
466
+ const boundedTargetMismatch = boundedRecovery
467
+ && (changed.length !== pending.targetPaths.length || changed.some((file, index) => pending.targetPaths[index] !== file));
468
+ const boundedAuthorshipMissing = boundedRecovery
469
+ && (authoredChanged.length !== changed.length || changed.some((file, index) => authoredChanged[index] !== file));
470
+ const unexpected = changed.length === 0
471
+ || changed.some((file) => !pending.targetPaths.includes(file) || !state.eligiblePaths.includes(file))
472
+ || boundedTargetMismatch
473
+ || boundedAuthorshipMissing;
427
474
  if (treeChanged && (!input.success || unexpected)) { state.status = "locked"; state.pending = null; state.updatedAt = now; return { result: "locked", state: writeState(input.cwd, state) }; }
428
475
  if (input.success && treeChanged) {
429
476
  if (pending.opensRepair) writeSemanticRepairOrigin(input.cwd, state, input.currentDigest, now);
@@ -3,8 +3,10 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
3
  import type { TaskContract } from "../../extensions/guard-types.ts";
4
4
  import type { TrajectorySyncResult } from "../trajectory/trajectory-runtime.ts";
5
5
  import {
6
+ BOUNDED_RECOVERY_CONFLICT_CODE,
6
7
  authorizeSemanticRepairCall,
7
8
  completeSemanticRepairCall,
9
+ decideBoundedRecoveryHandshake,
8
10
  decideSemanticRepairHandshake,
9
11
  pendingSemanticRepairCall,
10
12
  readSemanticRepairState,
@@ -31,6 +33,7 @@ export type SemanticRepairCompletionMetadata = {
31
33
  exitCode: number;
32
34
  currentWorkingTreeDigest: string;
33
35
  changedPaths: string[];
36
+ authoredChangedPaths?: string[];
34
37
  retryableFailure: boolean; correctiveFailure: boolean;
35
38
  };
36
39
 
@@ -100,6 +103,52 @@ export class SemanticRepairRuntime {
100
103
  return true;
101
104
  }
102
105
 
106
+ prepareBoundedRecovery(input: {
107
+ ctx: ExtensionContext; task: TaskContract; event: ToolEvent; currentDigest: string;
108
+ currentDeltaPaths: string[]; observedChangedPaths: string[];
109
+ authoredFileDigests: Record<string, string>; targetPaths: string[]; verifierCurrent: boolean;
110
+ }): boolean {
111
+ // This path is a one-shot CAP-12 bridge, not CAP-13's multi-revision
112
+ // semantic-repair protocol. Any durable state, including a cancelled or
113
+ // interrupted reservation, consumes the task-run grant and fails closed.
114
+ if (readSemanticRepairState(input.ctx.cwd, input.task.taskRunId).status !== "missing"
115
+ || semanticRepairStateRequired(input.ctx.cwd, input.task.taskRunId)) return false;
116
+ const decision = decideBoundedRecoveryHandshake({
117
+ task: input.task,
118
+ mutationTargets: input.targetPaths,
119
+ currentDeltaPaths: input.currentDeltaPaths,
120
+ observedChangedPaths: input.observedChangedPaths,
121
+ authoredFileDigests: input.authoredFileDigests,
122
+ verifierCurrent: input.verifierCurrent
123
+ });
124
+ if (!decision.authorized) return false;
125
+ const toolCallId = String(input.event.toolCallId);
126
+ const reserved = reserveSemanticRepairCall({
127
+ cwd: input.ctx.cwd,
128
+ task: input.task,
129
+ sessionId: input.ctx.sessionManager.getSessionId(),
130
+ toolCallId,
131
+ toolName: input.event.toolName,
132
+ currentDigest: input.currentDigest,
133
+ decision,
134
+ targetPaths: input.targetPaths,
135
+ recordedAt: this.dependencies.now()
136
+ });
137
+ if (!reserved.reserved) return false;
138
+ this.reservationTokens.set(
139
+ this.reservationKey(input.ctx.cwd, input.task.taskRunId, toolCallId),
140
+ reserved.reservationToken!
141
+ );
142
+ this.dependencies.trace(input.ctx, input.task, {
143
+ event: "bounded_recovery_repair_reserved",
144
+ workingTreeDigest: input.currentDigest,
145
+ reasonCode: BOUNDED_RECOVERY_CONFLICT_CODE,
146
+ eligiblePaths: decision.eligiblePaths,
147
+ targetCount: input.targetPaths.length
148
+ });
149
+ return true;
150
+ }
151
+
103
152
  reservedCallMatches(input: {
104
153
  cwd: string; taskRunId: string; sessionId: string; toolCallId: string;
105
154
  toolName: string; currentDigest: string; targetPaths: string[];
@@ -131,7 +180,8 @@ export class SemanticRepairRuntime {
131
180
  if (view.state.successfulMutations > 0 && !semanticRepairOriginMatches(cwd, view.state)) return "semantic repair durable origin is missing or invalid";
132
181
  if (view.state.status === "passed") return;
133
182
  if (view.state.successfulMutations === 0 && view.state.pending === null && !semanticRepairStateRequired(cwd, taskRunId)) return;
134
- return `semantic repair exact final verifier is not satisfied (${view.state.status})`;
183
+ const label = view.state.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE) ? "bounded recovery" : "semantic repair";
184
+ return `${label} exact final verifier is not satisfied (${view.state.status})`;
135
185
  }
136
186
 
137
187
  complete(
@@ -148,13 +198,16 @@ export class SemanticRepairRuntime {
148
198
  exitCode: metadata.exitCode,
149
199
  currentDigest: metadata.currentWorkingTreeDigest,
150
200
  changedPaths: metadata.changedPaths,
201
+ authoredChangedPaths: metadata.authoredChangedPaths,
151
202
  retryableFailure: metadata.retryableFailure,
152
203
  correctiveFailure: metadata.correctiveFailure,
153
204
  recordedAt: this.dependencies.now()
154
205
  });
155
206
  if (completion.result === "unmatched") return;
156
207
  this.dependencies.trace(ctx, task, {
157
- event: completion.result === "opened" ? "semantic_contradiction_repair_opened" : `semantic_repair_${completion.result}`,
208
+ event: completion.state?.conflictCodes.includes(BOUNDED_RECOVERY_CONFLICT_CODE)
209
+ ? `bounded_recovery_repair_${completion.result}`
210
+ : completion.result === "opened" ? "semantic_contradiction_repair_opened" : `semantic_repair_${completion.result}`,
158
211
  toolName: event.toolName,
159
212
  revision: completion.state?.revision,
160
213
  successfulMutations: completion.state?.successfulMutations,