@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
@@ -0,0 +1,217 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import type { TaskContract } from "../../extensions/guard-types.ts";
5
+ import { readTaskJournal } from "../../extensions/task-journal.js";
6
+ import {
7
+ directChildGitEvidenceRoots,
8
+ normalizedGitPath
9
+ } from "../../extensions/workspace-evidence-roots.js";
10
+ import {
11
+ workingTreeSnapshot,
12
+ workingTreeSnapshotHasUnavailableEvidence
13
+ } from "../../extensions/task-state.js";
14
+ import { workingTreeEvidenceDigest } from "../../extensions/working-tree-digest.js";
15
+ import { readMutationProvenance } from "../inspection/mutation-provenance-store.ts";
16
+ import { readTaskBaselineManifest } from "../inspection/source-evidence-store.ts";
17
+
18
+ // Some filesystems expose timestamps at coarse granularity. Requiring a clear
19
+ // pre-task gap prevents a file created in the same timestamp bucket as task
20
+ // startup from being mistaken for legacy baseline state.
21
+ const LEGACY_EVIDENCE_TIMESTAMP_SAFETY_MS = 2_000;
22
+
23
+ export type TaskEvidenceCoverageRefresh = {
24
+ task: TaskContract;
25
+ refreshed: boolean;
26
+ reason: string;
27
+ addedPaths: string[];
28
+ retainedMutationPaths: string[];
29
+ ambiguousPaths: string[];
30
+ previousBaselineDigest: string;
31
+ nextBaselineDigest: string;
32
+ };
33
+
34
+ function strings(value: unknown): string[] {
35
+ return Array.isArray(value)
36
+ ? [...new Set(value.filter((item): item is string => typeof item === "string" && item.trim().length > 0)
37
+ .map((item) => normalizedGitPath(item).replace(/\/$/, "")))]
38
+ : [];
39
+ }
40
+
41
+ function decodedMutationPath(value: unknown): string | undefined {
42
+ if (typeof value !== "string" || value.length > 8192 || !/^[A-Za-z0-9_-]*$/.test(value)) return undefined;
43
+ try {
44
+ const decoded = Buffer.from(value, "base64url").toString("utf8");
45
+ const normalized = normalizedGitPath(decoded);
46
+ return normalized && normalized !== "." && normalized !== ".." && !normalized.startsWith("../")
47
+ ? normalized
48
+ : undefined;
49
+ } catch {
50
+ return undefined;
51
+ }
52
+ }
53
+
54
+ function journalMutationPaths(cwd: string, taskRunId: string): { paths: string[]; corruptions: string[] } {
55
+ const journal = readTaskJournal(cwd, { taskRunId });
56
+ const paths: string[] = [];
57
+ for (const event of journal.events) {
58
+ if (event.eventType !== "checkpoint" || event.taskRunId !== taskRunId) continue;
59
+ const evidence = event.data?.evidence;
60
+ if (!evidence || typeof evidence !== "object" || Array.isArray(evidence)) continue;
61
+ paths.push(...strings((evidence as Record<string, unknown>).files));
62
+ paths.push(...strings((evidence as Record<string, unknown>).targetPaths));
63
+ }
64
+ return { paths: [...new Set(paths)].sort(), corruptions: journal.corruptions };
65
+ }
66
+
67
+ function provenanceMutationPaths(cwd: string, taskRunId: string): { paths: string[]; corruptions: string[] } {
68
+ const provenance = readMutationProvenance(cwd, taskRunId);
69
+ const paths = provenance.records.flatMap((record) => record.changes
70
+ .map((change) => decodedMutationPath(change.repoPathBase64))
71
+ .filter((item): item is string => Boolean(item)));
72
+ return { paths: [...new Set(paths)].sort(), corruptions: provenance.corruptions };
73
+ }
74
+
75
+ function isLegacyLooseHiddenPath(file: string, childGitPrefixes: string[]): boolean {
76
+ const normalized = normalizedGitPath(file);
77
+ if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return false;
78
+ if (childGitPrefixes.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`))) return false;
79
+ return normalized.split("/").some((segment) => segment.startsWith(".") && segment.length > 1);
80
+ }
81
+
82
+ function fileStatePredatesTask(cwd: string, file: string, createdAt: string): boolean {
83
+ const taskStartedAt = Date.parse(createdAt);
84
+ if (!Number.isFinite(taskStartedAt)) return false;
85
+ const absolute = path.resolve(cwd, file);
86
+ const relative = path.relative(cwd, absolute);
87
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return false;
88
+
89
+ // The legacy inventory has no content digest for this path. Metadata that is
90
+ // older than the task is the only local positive evidence that the exact file
91
+ // state existed before task execution. A symlink, unreadable component, zero
92
+ // timestamp, or any task-time ctime/mtime remains an ordinary task delta.
93
+ let current = cwd;
94
+ try {
95
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
96
+ current = path.join(current, segment);
97
+ const component = fs.lstatSync(current);
98
+ if (component.isSymbolicLink()) return false;
99
+ }
100
+ const stat = fs.lstatSync(absolute);
101
+ if (!stat.isFile() || stat.isSymbolicLink()) return false;
102
+ const timestamps = [stat.birthtimeMs, stat.ctimeMs, stat.mtimeMs];
103
+ const latestSafeTimestamp = taskStartedAt - LEGACY_EVIDENCE_TIMESTAMP_SAFETY_MS;
104
+ return timestamps.every((value) => Number.isFinite(value) && value > 0 && value <= latestSafeTimestamp);
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ function manifestProvenBaselinePaths(
111
+ cwd: string,
112
+ task: TaskContract,
113
+ previousBaseline: Record<string, string>,
114
+ current: Record<string, string>,
115
+ candidates: string[]
116
+ ): string[] {
117
+ if (candidates.length === 0) return [];
118
+ try {
119
+ const manifest = readTaskBaselineManifest(cwd, task.taskRunId);
120
+ if (!manifest || manifest.taskId !== task.taskId || manifest.taskRunId !== task.taskRunId) return [];
121
+ const expanded = { ...previousBaseline };
122
+ for (const file of candidates) expanded[file] = current[file];
123
+ return workingTreeEvidenceDigest(expanded) === manifest.baselineTreeDigest ? candidates : [];
124
+ } catch {
125
+ return [];
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Reconcile the one coverage gap introduced when parent-workspace evidence
131
+ * began including hidden project source. Only paths the legacy collector could
132
+ * not see are adopted, and any path with durable mutation evidence remains a
133
+ * real task delta. Existing baseline entries are never rewritten.
134
+ */
135
+ export function refreshLegacyHiddenWorkspaceEvidenceCoverage(
136
+ cwd: string,
137
+ task: TaskContract
138
+ ): TaskEvidenceCoverageRefresh {
139
+ const previousBaseline = task.baselineFileDigests ?? {};
140
+ const previousBaselineDigest = workingTreeEvidenceDigest(previousBaseline);
141
+ const unchanged = (
142
+ reason: string,
143
+ retainedMutationPaths: string[] = [],
144
+ ambiguousPaths: string[] = []
145
+ ): TaskEvidenceCoverageRefresh => ({
146
+ task,
147
+ refreshed: false,
148
+ reason,
149
+ addedPaths: [],
150
+ retainedMutationPaths,
151
+ ambiguousPaths,
152
+ previousBaselineDigest,
153
+ nextBaselineDigest: previousBaselineDigest
154
+ });
155
+
156
+ if (task.trace.outcome !== "pending" || task.changeMode !== "source-change") return unchanged("task-not-refreshable");
157
+ if (!previousBaseline || typeof previousBaseline !== "object" || Array.isArray(previousBaseline)) return unchanged("baseline-unavailable");
158
+ const childGitPrefixes = directChildGitEvidenceRoots(cwd).map((root) => normalizedGitPath(root.prefix)).filter(Boolean);
159
+ if (childGitPrefixes.length === 0) return unchanged("not-parent-multi-repo-workspace");
160
+
161
+ const current = workingTreeSnapshot(cwd) as Record<string, string>;
162
+ if (workingTreeSnapshotHasUnavailableEvidence(current)) return unchanged("current-evidence-unavailable");
163
+ const candidates = Object.keys(current)
164
+ .filter((file) => previousBaseline[file] === undefined && isLegacyLooseHiddenPath(file, childGitPrefixes))
165
+ .sort();
166
+ if (candidates.length === 0) return unchanged("coverage-current");
167
+
168
+ const journal = journalMutationPaths(cwd, task.taskRunId);
169
+ const provenance = provenanceMutationPaths(cwd, task.taskRunId);
170
+ if (journal.corruptions.length > 0 || provenance.corruptions.length > 0) {
171
+ return unchanged("mutation-evidence-unavailable");
172
+ }
173
+ const mutated = new Set([
174
+ ...strings(task.observedChangedFiles),
175
+ ...strings(task.changedFiles),
176
+ ...journal.paths,
177
+ ...provenance.paths
178
+ ]);
179
+ const retainedMutationPaths = candidates.filter((file) => mutated.has(file));
180
+ const unclaimedCandidates = candidates.filter((file) => !mutated.has(file));
181
+ // A current, integrity-validated baseline manifest is stronger than file
182
+ // timestamps: if adding the currently observed missing paths reconstructs
183
+ // its exact task-start tree digest, none of those paths changed after start.
184
+ // Legacy manifests that did not cover the paths simply fail this equality
185
+ // and retain the conservative timestamp fallback.
186
+ const manifestProven = new Set(manifestProvenBaselinePaths(cwd, task, previousBaseline, current, unclaimedCandidates));
187
+ const addedPaths = unclaimedCandidates.filter((file) => manifestProven.has(file) || fileStatePredatesTask(cwd, file, task.createdAt));
188
+ const added = new Set(addedPaths);
189
+ const ambiguousPaths = unclaimedCandidates.filter((file) => !added.has(file));
190
+ if (addedPaths.length === 0) {
191
+ return unchanged(
192
+ ambiguousPaths.length > 0 ? "coverage-candidates-ambiguous" : "coverage-candidates-have-mutation-evidence",
193
+ retainedMutationPaths,
194
+ ambiguousPaths
195
+ );
196
+ }
197
+
198
+ const baselineFileDigests = { ...previousBaseline };
199
+ for (const file of addedPaths) baselineFileDigests[file] = current[file];
200
+ const refreshed: TaskContract = {
201
+ ...task,
202
+ baselineChangedFiles: Object.keys(baselineFileDigests).sort(),
203
+ baselineFileDigests
204
+ };
205
+ return {
206
+ task: refreshed,
207
+ refreshed: true,
208
+ reason: ambiguousPaths.length > 0
209
+ ? "legacy-hidden-workspace-coverage-partially-expanded"
210
+ : "legacy-hidden-workspace-coverage-expanded",
211
+ addedPaths,
212
+ retainedMutationPaths,
213
+ ambiguousPaths,
214
+ previousBaselineDigest,
215
+ nextBaselineDigest: workingTreeEvidenceDigest(baselineFileDigests)
216
+ };
217
+ }
@@ -1,4 +1,6 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { stageContextDelivery } from "../context/context-delivery.ts";
3
+ import { semanticCompactionCancelled } from "../session/system-prompt.ts";
2
4
 
3
5
  type ExtensionContext = any;
4
6
 
@@ -10,7 +12,7 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
10
12
  contextIndexV2Status, crypto, emitRuntimeMessage, ensureContextIndexV2, estimateContextTokens,
11
13
  formatContextPreflight,
12
14
  formatCount, formatPercent, fs, helpersMode, loadProfileFromContext, memoryHandbookPath,
13
- policy, projectFilePath, resolveMemorySettings, resolveOrchestrationPolicy, searchContextIndex,
15
+ policy, projectFilePath, resolveMemorySettings, resolveOrchestrationPolicy, retrievalKey, runtimeState, searchContextIndex,
14
16
  searchContextIndexV2, selectRuntimeAction, semanticCompactionInstructions, telemetry
15
17
  } = deps;
16
18
  function emitContextIndexSearch(ctx: ExtensionContext, query: string): void {
@@ -123,6 +125,7 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
123
125
  });
124
126
  telemetry(ctx, {
125
127
  event: "context_pack",
128
+ turnId: runtimeState.currentTurn(ctx)?.turnId,
126
129
  queryHash: pack.queryHash,
127
130
  confidence: pack.confidence,
128
131
  candidates: pack.candidates,
@@ -131,12 +134,65 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
131
134
  selectedPaths: pack.selected.map((item) => item.path),
132
135
  source: "command"
133
136
  });
137
+ const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId());
138
+ const deliveryId = task?.trace?.outcome === "pending" && pack.selected.length > 0 ? crypto.randomUUID() : undefined;
139
+ if (deliveryId) {
140
+ stageContextDelivery(ctx, {
141
+ deliveryId,
142
+ taskRunId: task.taskRunId,
143
+ turnId: runtimeState.currentTurn(ctx)?.turnId,
144
+ entries: pack.selected.map((item) => ({
145
+ path: item.path,
146
+ reason: "Runtime confirmed delivery of a /context pack message."
147
+ })),
148
+ pack: {
149
+ retrievalKey: retrievalKey(ctx, query),
150
+ queryHash: pack.queryHash,
151
+ confidence: pack.confidence,
152
+ estimatedTokens: pack.estimatedTokens,
153
+ paths: pack.selected.map((item) => item.path)
154
+ },
155
+ injection: {
156
+ source: "context-command",
157
+ queryHash: pack.queryHash,
158
+ confidence: pack.confidence,
159
+ estimatedTokens: pack.estimatedTokens,
160
+ selectedItems: pack.selected.map((item) => ({
161
+ path: item.path,
162
+ estimatedTokens: item.estimatedTokens,
163
+ sources: item.sources,
164
+ links: item.links,
165
+ fileContentHash: item.fileContentHash,
166
+ ...(item.sanitizedContentDigest ? { sanitizedContentDigest: item.sanitizedContentDigest } : {}),
167
+ payloadHash: item.payloadHash,
168
+ representation: item.representation,
169
+ ranges: item.ranges,
170
+ generation: item.generation,
171
+ sensitiveContentRedacted: item.sensitiveContentRedacted
172
+ }))
173
+ }
174
+ }, { state: runtimeState, telemetry });
175
+ }
134
176
  emitRuntimeMessage(ctx, "piagent-context-pack-v2", pack.text, {
135
177
  queryHash: pack.queryHash,
136
178
  confidence: pack.confidence,
137
179
  estimatedTokens: pack.estimatedTokens,
138
180
  paths: pack.selected.map((item) => item.path),
139
- finderRecommended: pack.finderRecommended
181
+ selectedItems: pack.selected.map((item) => ({
182
+ path: item.path,
183
+ estimatedTokens: item.estimatedTokens,
184
+ sources: item.sources,
185
+ links: item.links,
186
+ fileContentHash: item.fileContentHash,
187
+ ...(item.sanitizedContentDigest ? { sanitizedContentDigest: item.sanitizedContentDigest } : {}),
188
+ payloadHash: item.payloadHash,
189
+ representation: item.representation,
190
+ ranges: item.ranges,
191
+ generation: item.generation,
192
+ sensitiveContentRedacted: item.sensitiveContentRedacted
193
+ })),
194
+ finderRecommended: pack.finderRecommended,
195
+ contextDelivery: deliveryId ? { schemaVersion: 1, deliveryId } : undefined
140
196
  });
141
197
  }
142
198
 
@@ -156,13 +212,19 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
156
212
  const report = buildContextEfficiencyReport(ctx.cwd);
157
213
  const task = activeSessionTask(ctx);
158
214
  const taskEfficiency = task ? buildTaskEfficiencyMetrics(ctx.cwd, task) : null;
215
+ const wasteScore = report.metrics.contextWasteScore === null
216
+ ? `unavailable (diagnostic estimate ${report.metrics.contextWasteScoreEstimate}/100; evidence ${formatPercent(report.metrics.contextWasteScoreEvidenceCoverage)}, ${report.coverage.wasteScore.status})`
217
+ : `${report.metrics.contextWasteScore}/100 (lower is better; evidence ${formatPercent(report.metrics.contextWasteScoreEvidenceCoverage)}, ${report.coverage.wasteScore.status})`;
159
218
  emitRuntimeMessage(ctx, "piagent-context-efficiency", [
160
- `contextWasteScore: ${report.metrics.contextWasteScore}/100 (lower is better)`,
219
+ `contextWasteScore: ${wasteScore}`,
161
220
  `activeTools: ${report.metrics.averageActiveTools}`,
162
- `toolSchemaShare: ${formatPercent(report.metrics.toolSchemaShare)}`,
163
- `duplicateReads: ${report.metrics.duplicateReads}/${report.metrics.readCalls}`,
221
+ `toolSchemaPrefixShare: ${formatPercent(report.metrics.toolSchemaPrefixShare)}`,
222
+ `toolSchemaToSystemRatio: ${report.metrics.toolSchemaToSystemRatio}`,
223
+ `duplicateReads: ${report.metrics.duplicateReads}/${report.metrics.comparableReadCalls} comparable (${report.metrics.readCalls} observed; coverage ${formatPercent(report.metrics.readEvidenceCoverage)})`,
224
+ `fallbackRereads: ${report.metrics.contextFallbackRereads}/${report.metrics.contextSelections}`,
164
225
  `duplicateOutput: ${formatPercent(report.metrics.duplicateOutputRate)}`,
165
226
  `lowConfidencePacks: ${report.metrics.lowConfidencePacks}/${report.sample.contextPacks}`,
227
+ `editRecovery: ${report.metrics.editRecoveryContextCount}/${report.metrics.editRecoveryFailures} failures returned context; ${report.metrics.editRecoverySuppressedFailures} suppressed; ${report.metrics.editRecoveryEstimatedTokens} estimated tokens; classification coverage ${formatPercent(report.metrics.editRecoveryFailureEvidenceCoverage)}`,
166
228
  `taskEfficiency: ${taskEfficiency ? `${taskEfficiency.solver.route}; verify=${taskEfficiency.verification.attempts}; outcome=${taskEfficiency.outcome.task}` : "no active task"}`,
167
229
  ...report.recommendations.map((recommendation) => `- ${recommendation}`)
168
230
  ].join("\n"), { ...report, taskEfficiency });
@@ -175,6 +237,19 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
175
237
  function compactCurrentSession(ctx: ExtensionContext): void {
176
238
  const sessionId = ctx.sessionManager.getSessionId();
177
239
  const instructions = semanticCompactionInstructions(ctx.cwd, sessionId);
240
+ if (semanticCompactionCancelled(instructions)) {
241
+ telemetry(ctx, {
242
+ event: "compaction_cancelled",
243
+ mode: "semantic",
244
+ reason: "lossless-task-truth-exceeds-carry-target",
245
+ hasTaskContract: Boolean(compactSessionTask(ctx.cwd, sessionId))
246
+ });
247
+ ctx.ui.notify("Piagent kept the current context because compacting it would lose required task details.", "warning");
248
+ emitRuntimeMessage(ctx, "piagent-context-compaction-cancelled", instructions, {
249
+ reason: "lossless-task-truth-exceeds-carry-target"
250
+ });
251
+ return;
252
+ }
178
253
  telemetry(ctx, {
179
254
  event: "compaction_requested",
180
255
  mode: "semantic",
@@ -367,10 +442,10 @@ export function registerContextCommands(pi: ExtensionAPI, deps: Record<string, a
367
442
  content: [
368
443
  `mode: ${orchestration.defaultMode}`,
369
444
  `helpersMode: ${helpersMode()}`,
370
- `subagents: bounded read-only scout/planning/review; max ${orchestration.maxConcurrentSubagents}`,
445
+ `helpers: max ${orchestration.maxConcurrentSubagents} fresh read-only total; requires >=30% projected net token saving`,
371
446
  `lenses: ${orchestration.defaultReviewLenses.join(", ")}`,
372
447
  `fieldGuide: ${orchestration.fieldGuide.enabled ? `${fieldGuidePath} (${fieldGuideExists ? "exists" : "missing"})` : "off"}`,
373
- "writer: single writer by default; parallel writers need explicit approval + isolation"
448
+ "writer: parent only; helper workers and retries are disabled"
374
449
  ].join("\n"),
375
450
  display: true,
376
451
  details: {
@@ -11,11 +11,11 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
11
11
  registerPiagentTool(pi, {
12
12
  name: "piagent_orchestration_policy",
13
13
  label: "Piagent Orchestration Policy",
14
- description: "Return solo-first subagent, review lens, model-role, and Field Guide policy for the current project.",
15
- promptSnippet: "Use this before planning medium/large tasks so orchestration stays single-agent-first and token-aware.",
14
+ description: "Return parent-direct helper limits, review lenses, model-role, and Field Guide policy for the current project.",
15
+ promptSnippet: "Use this before planning medium/large tasks so the parent stays direct and helper use remains a measured token optimization.",
16
16
  promptGuidelines: [
17
- "Default to the parent agent plus bounded subagents only when they reduce context risk or improve review quality.",
18
- "Use review lenses instead of spawning a broad swarm.",
17
+ "Default to the parent model working directly; review quality alone does not justify another model run.",
18
+ "Use at most one fresh read-only helper only with two independent lanes and at least 30% projected total token savings; never delegate implementation or retry a deterministic failure.",
19
19
  "Treat Field Guide memory as advisory and verify it against current repository files."
20
20
  ],
21
21
  parameters: Type.Object({
@@ -45,7 +45,9 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
45
45
  path: fieldGuidePath,
46
46
  exists: fieldGuideExists
47
47
  },
48
- stance: "single-agent-first; subagents are opt-in tools for bounded scout, planning, and review"
48
+ helperBudget: "1-readonly-total/0-retries/0-writers",
49
+ minimumProjectedNetSaving: 0.3,
50
+ stance: "parent-direct; one fresh read-only helper only when runtime-measured net token savings justify it"
49
51
  };
50
52
  const text = params.detail === "full"
51
53
  ? JSON.stringify(payload, null, 2)
@@ -53,6 +55,8 @@ export function registerKnowledgeTools(pi: ExtensionAPI, deps: Record<string, an
53
55
  `mode: ${payload.defaultMode}`,
54
56
  `maxConcurrentSubagents: ${payload.maxConcurrentSubagents}`,
55
57
  `helpersMode: ${payload.helpersMode}`,
58
+ `helperBudget: ${payload.helperBudget}`,
59
+ `minimumProjectedNetSaving: ${Math.round(payload.minimumProjectedNetSaving * 100)}%`,
56
60
  `reviewLenses: ${payload.defaultReviewLenses.join(", ")}`,
57
61
  `fieldGuide: ${payload.fieldGuide.enabled ? `${payload.fieldGuide.path} (${payload.fieldGuide.exists ? "exists" : "missing"})` : "off"}`,
58
62
  `fieldGuidePolicy: ${payload.fieldGuide.writePolicy}, maxLines=${payload.fieldGuide.maxLines}`,
@@ -0,0 +1,49 @@
1
+ import { OPERATOR_REQUEST_MAX_CHARS } from "../../extensions/task-state.js";
2
+
3
+ type SessionContext = {
4
+ sessionManager: {
5
+ getBranch?: () => unknown;
6
+ getEntries?: () => unknown;
7
+ };
8
+ };
9
+
10
+ function userMessageText(message: unknown): string {
11
+ if (!message || typeof message !== "object" || (message as Record<string, unknown>).role !== "user") return "";
12
+ const content = (message as Record<string, unknown>).content;
13
+ if (typeof content === "string") return content;
14
+ if (!Array.isArray(content)) return "";
15
+ // The host stores the exact operator prose in the first text block. Later
16
+ // text blocks may contain extracted attachment bodies and must not be copied
17
+ // into private task truth as if the operator typed them.
18
+ const first = content.find((block) => block && typeof block === "object"
19
+ && (block as Record<string, unknown>).type === "text"
20
+ && typeof (block as Record<string, unknown>).text === "string") as Record<string, unknown> | undefined;
21
+ return typeof first?.text === "string" ? first.text : "";
22
+ }
23
+
24
+ export function latestOperatorRequest(ctx: SessionContext): string | undefined {
25
+ let entries: unknown[] = [];
26
+ try {
27
+ const branch = ctx.sessionManager.getBranch?.();
28
+ entries = Array.isArray(branch) ? branch : [];
29
+ if (entries.length === 0) {
30
+ const all = ctx.sessionManager.getEntries?.();
31
+ entries = Array.isArray(all) ? all : [];
32
+ }
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
37
+ const entry = entries[index] as Record<string, any> | undefined;
38
+ if (entry?.type !== "message") continue;
39
+ const text = userMessageText(entry.message);
40
+ if (text.trim()) return text;
41
+ }
42
+ return undefined;
43
+ }
44
+
45
+ export function boundedOperatorRequest(value: unknown, redactText: (value: string) => string): string | undefined {
46
+ if (typeof value !== "string" || !value.trim()) return undefined;
47
+ const redacted = redactText(value);
48
+ return Array.from(redacted).length <= OPERATOR_REQUEST_MAX_CHARS ? redacted : undefined;
49
+ }
@@ -1,4 +1,6 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { stageContextDelivery } from "../context/context-delivery.ts";
3
+ import { formatContextEfficiencyReport } from "../context/context-efficiency-format.ts";
2
4
 
3
5
  type PiagentToolGroup = any;
4
6
  type TechStackManifest = any;
@@ -6,7 +8,7 @@ type TechStackManifest = any;
6
8
 
7
9
  export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>): void {
8
10
  const {
9
- PIAGENT_TOOL_NAMES, READ_ONLY_TOOL_NAMES, StringEnum, Type, activateToolGroups,
11
+ PIAGENT_TOOL_NAMES, READ_ONLY_TOOL_NAMES, StringEnum, Type, activateToolGroups, activeSessionTask,
10
12
  buildContextEfficiencyReport, buildContextIndexStatus, buildContextIndexV2, buildContextPack, buildContextPreflight,
11
13
  buildTestImpact, buildUsageSnapshot, candidateFileBudget, contextBudgetConfig, contextIndexExcludePatterns,
12
14
  contextIndexV2Status, crypto, dynamicToolsEnabled, effectiveProtectedPaths, ensureContextIndexV2,
@@ -28,7 +30,7 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
28
30
  "When recovery is necessary, load only the smallest group that resolves the reported missing evidence."
29
31
  ],
30
32
  parameters: Type.Object({
31
- groups: Type.Array(StringEnum(["intake", "governance", "task", "recovery", "policy", "retrieval", "knowledge", "onboarding", "usage"] as const), { minItems: 1 })
33
+ groups: Type.Array(StringEnum(["intake", "governance", "task", "recovery", "policy", "retrieval", "source", "knowledge", "onboarding", "usage"] as const), { minItems: 1 })
32
34
  }),
33
35
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
34
36
  const groups = [...new Set(params.groups)] as PiagentToolGroup[];
@@ -142,7 +144,48 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
142
144
  limit: 18,
143
145
  excludePatterns
144
146
  });
145
- result = { ...pack, text: undefined, status };
147
+ telemetry(ctx, { event: "context_pack", turnId: runtimeState.currentTurn(ctx)?.turnId, source: "context-tool", queryHash: pack.queryHash, confidence: pack.confidence, candidates: pack.candidates, selected: pack.selected.length, estimatedTokens: pack.estimatedTokens, selectedPaths: pack.selected.map((item) => item.path) });
148
+ const task = activeSessionTask(ctx.cwd, ctx.sessionManager.getSessionId());
149
+ const deliveryId = task?.trace?.outcome === "pending" && pack.selected.length > 0 ? crypto.randomUUID() : undefined;
150
+ if (deliveryId) {
151
+ const packRetrievalKey = retrievalKey(ctx, params.query);
152
+ stageContextDelivery(ctx, {
153
+ deliveryId,
154
+ taskRunId: task.taskRunId,
155
+ turnId: runtimeState.currentTurn(ctx)?.turnId,
156
+ entries: pack.selected.map((item) => ({
157
+ path: item.path,
158
+ reason: "Runtime confirmed delivery of an explicit Context Engine tool pack."
159
+ })),
160
+ pack: {
161
+ retrievalKey: packRetrievalKey,
162
+ queryHash: pack.queryHash,
163
+ confidence: pack.confidence,
164
+ estimatedTokens: pack.estimatedTokens,
165
+ paths: pack.selected.map((item) => item.path)
166
+ },
167
+ injection: {
168
+ source: "context-tool",
169
+ queryHash: pack.queryHash,
170
+ confidence: pack.confidence,
171
+ estimatedTokens: pack.estimatedTokens,
172
+ selectedItems: pack.selected.map((item) => ({
173
+ path: item.path,
174
+ estimatedTokens: item.estimatedTokens,
175
+ sources: item.sources,
176
+ links: item.links,
177
+ fileContentHash: item.fileContentHash,
178
+ ...(item.sanitizedContentDigest ? { sanitizedContentDigest: item.sanitizedContentDigest } : {}),
179
+ payloadHash: item.payloadHash,
180
+ representation: item.representation,
181
+ ranges: item.ranges,
182
+ generation: item.generation,
183
+ sensitiveContentRedacted: item.sensitiveContentRedacted
184
+ }))
185
+ }
186
+ }, { state: runtimeState, telemetry });
187
+ }
188
+ result = { ...pack, text: undefined, status, contextDelivery: deliveryId ? { schemaVersion: 1, deliveryId } : undefined };
146
189
  text = pack.text;
147
190
  } else if (params.action === "impact") {
148
191
  await ensureContextIndexV2(ctx.cwd, { excludePatterns, rebuildMissing: false });
@@ -156,15 +199,7 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
156
199
  } else {
157
200
  result = buildContextEfficiencyReport(ctx.cwd);
158
201
  const report = result as ReturnType<typeof buildContextEfficiencyReport>;
159
- text = [
160
- `contextWasteScore: ${report.metrics.contextWasteScore}/100 (lower is better)`,
161
- `activeTools: ${report.metrics.averageActiveTools}`,
162
- `toolSchemaShare: ${formatPercent(report.metrics.toolSchemaShare)}`,
163
- `duplicateReads: ${report.metrics.duplicateReads}/${report.metrics.readCalls}`,
164
- `duplicateOutput: ${formatPercent(report.metrics.duplicateOutputRate)}`,
165
- `lowConfidencePacks: ${report.metrics.lowConfidencePacks}/${report.sample.contextPacks}`,
166
- ...report.recommendations.map((recommendation) => `- ${recommendation}`)
167
- ].join("\n");
202
+ text = formatContextEfficiencyReport(report, formatPercent);
168
203
  }
169
204
  telemetry(ctx, {
170
205
  event: "context_engine_action",
@@ -394,8 +429,8 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
394
429
  registerPiagentTool(pi, {
395
430
  name: "piagent_task_gate_check",
396
431
  label: "Piagent Task Gate Check",
397
- description: "Check whether a governed task has enough context, verify evidence, and trace before claiming done.",
398
- promptSnippet: "Use this before final on source-changing tasks.",
432
+ description: "Check whether a governed task has enough context, current-tree exact verifier evidence, and trace before claiming done or rerunning verification.",
433
+ promptSnippet: "Use before final and before repeating an exact verifier; run only commands listed as missing for the current tree.",
399
434
  parameters: Type.Object({
400
435
  taskId: Type.String({ minLength: 1 }),
401
436
  changedFiles: Type.Optional(Type.Array(Type.String()))
@@ -416,7 +451,7 @@ export function registerPolicyTools(pi: ExtensionAPI, deps: Record<string, any>)
416
451
  ...verifierCommandInstructions(result.missingVerifyCommands),
417
452
  `warnings: ${result.warnings.join("; ") || "none"}`
418
453
  ].join("\n");
419
- return { content: [{ type: "text", text }], details: { ...result, task: projected } };
454
+ return { content: [{ type: "text", text }], details: { ...result, task: projected ? { taskId: projected.taskId, taskRunId: projected.taskRunId } : undefined } };
420
455
  }
421
456
  });
422
457
 
@@ -334,7 +334,7 @@ export function registerSessionCommands(pi: ExtensionAPI, deps: Record<string, a
334
334
  const approvals = feature ? [feature.externalAction ? "external-action" : "", feature.destructiveAction ? "destructive-action" : "", feature.permissionExpansion ? "permission-expansion" : ""].filter(Boolean) : [];
335
335
  const modelRoute = feature ? await evaluateModelRoute?.(ctx, feature, snapshot) : undefined;
336
336
  const retrievalRoute = feature ? await evaluateRetrievalRoute?.(ctx, feature) : undefined;
337
- return { runtime: snapshot, modelRoute: modelRoute?.status === "ok" ? modelRoute.decision : null, retrievalRoute: retrievalRoute ?? null, scope: task?.scope ?? [], protectedPaths: effectiveProtectedPaths(policy, profile).readProtectedPaths, activeToolGroups: pi.getActiveTools(), helperMode: helpersMode(), helperBudget: "2-concurrent/3-total", executionBackend: "host", executionBoundary: "host execution is not a sandbox", approvals, blockers: feature?.protectedTarget ? ["protected-target"] : [], controlMode: evaluation.status === "ok" && evaluation.decision.mode === "shadow" ? "shadow" : resolveRuntimePolicy(profile).finalGate === "enforce" ? "enforce" : "assist" };
337
+ return { runtime: snapshot, modelRoute: modelRoute?.status === "ok" ? modelRoute.decision : null, retrievalRoute: retrievalRoute ?? null, scope: task?.scope ?? [], protectedPaths: effectiveProtectedPaths(policy, profile).readProtectedPaths, activeToolGroups: pi.getActiveTools(), helperMode: helpersMode(), helperBudget: "1-readonly-total/0-retries/0-writers", executionBackend: "host", executionBoundary: "host execution is not a sandbox", approvals, blockers: feature?.protectedTarget ? ["protected-target"] : [], controlMode: evaluation.status === "ok" && evaluation.decision.mode === "shadow" ? "shadow" : resolveRuntimePolicy(profile).finalGate === "enforce" ? "enforce" : "assist" };
338
338
  }
339
339
  });
340
340