@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,394 @@
1
+ import {
2
+ REDACTION,
3
+ keyLooksSensitive,
4
+ redactSensitiveTextInternal
5
+ } from "./sensitive-text.js";
6
+ import {
7
+ balancedSourceExpression,
8
+ hasStandaloneKeywordSourceLiteral,
9
+ hasStandaloneNumericSourceLiteral,
10
+ maskSourceLookupStrings,
11
+ rewriteSensitiveSegment,
12
+ rewriteSensitiveStarts,
13
+ sourceReference
14
+ } from "./sensitive-source-expression.js";
15
+
16
+ function nextSourceComment(source, cursor, { hashComments }) {
17
+ let quote = "", escaped = false;
18
+ for (let index = cursor; index < source.length; index += 1) {
19
+ const character = source[index], next = source[index + 1] ?? "";
20
+ if (quote) {
21
+ if (escaped) escaped = false;
22
+ else if (character === "\\") escaped = true;
23
+ else if (character === quote) quote = "";
24
+ continue;
25
+ }
26
+ if (character === "\"" || character === "'" || character === "`") {
27
+ quote = character;
28
+ continue;
29
+ }
30
+ if (character === "/" && next === "*") return { index, kind: "block" };
31
+ if (character === "/" && next === "/") return { index, kind: "line" };
32
+ if (((hashComments && character === "#") || (character === "-" && next === "-"))
33
+ && (index === cursor || /\s/.test(source[index - 1] ?? ""))) return { index, kind: "line" };
34
+ }
35
+ return undefined;
36
+ }
37
+
38
+ function shieldExecutableSourceReferences(input, { shellOnly = false, hashComments = true } = {}) {
39
+ const references = [];
40
+ let nonce = 0;
41
+ let markerPrefix;
42
+ do {
43
+ markerPrefix = `__PIAGENT_DSR_${nonce}_`;
44
+ nonce += 1;
45
+ } while (String(input ?? "").includes(markerPrefix));
46
+ const placeholder = (value) => {
47
+ const id = `${markerPrefix}${references.length}__`;
48
+ references.push({ id, value });
49
+ return id;
50
+ };
51
+ const rewriteCode = (segment) => rewriteSensitiveSegment(segment, {
52
+ allowDynamic: true, shellOnly, placeholder
53
+ });
54
+ const rewriteComment = (segment) => rewriteSensitiveSegment(segment, {
55
+ allowDynamic: false, shellOnly, placeholder
56
+ });
57
+ const source = String(input ?? "");
58
+ let result = "", cursor = 0, blockComment = false;
59
+ while (cursor < source.length) {
60
+ if (blockComment) {
61
+ const end = source.indexOf("*/", cursor);
62
+ if (end < 0) {
63
+ result += rewriteComment(source.slice(cursor));
64
+ cursor = source.length;
65
+ } else {
66
+ const body = rewriteComment(source.slice(cursor, end));
67
+ result += `${body}*/`;
68
+ cursor = end + 2;
69
+ blockComment = false;
70
+ }
71
+ continue;
72
+ }
73
+ const comment = nextSourceComment(source, cursor, { hashComments });
74
+ if (!comment) {
75
+ result += rewriteCode(source.slice(cursor));
76
+ cursor = source.length;
77
+ continue;
78
+ }
79
+ result += rewriteCode(source.slice(cursor, comment.index));
80
+ if (comment.kind === "line") {
81
+ const lineEnd = source.indexOf("\n", comment.index);
82
+ const end = lineEnd < 0 ? source.length : lineEnd;
83
+ result += rewriteComment(source.slice(comment.index, end));
84
+ if (lineEnd >= 0) result += "\n";
85
+ cursor = lineEnd < 0 ? source.length : lineEnd + 1;
86
+ } else {
87
+ result += "/*";
88
+ cursor = comment.index + 2;
89
+ blockComment = true;
90
+ }
91
+ }
92
+ return {
93
+ text: result,
94
+ restore(value) {
95
+ let restored = value;
96
+ for (const reference of references) restored = restored.replaceAll(reference.id, reference.value);
97
+ return restored;
98
+ }
99
+ };
100
+ }
101
+
102
+ const SENSITIVE_SOURCE_QUOTED_HEREDOC_PATTERN = /(^[ \t]*(?:(?:const|let|var)\s+)?([$@%]?[A-Za-z_$][A-Za-z0-9_$]*)\s*(?:=|<-|:=)\s*)<<[-~]?(["'])([A-Za-z_][A-Za-z0-9_]*)\3[^\r\n]*(?:\r\n|\n|\r)([\s\S]*?)(^[ \t]*\4[ \t]*$)/gm;
103
+ const SENSITIVE_SOURCE_BARE_HEREDOC_PATTERN = /(^[ \t]*(?:(?:const|let|var)\s+)?([$@%]?[A-Za-z_$][A-Za-z0-9_$]*)\s*(?:=|<-|:=)\s*)<<[-~]?([A-Za-z_][A-Za-z0-9_]*)[^\r\n]*(?:\r\n|\n|\r)([\s\S]*?)(^[ \t]*\3[ \t]*$)/gm;
104
+
105
+ function redactSensitiveSourceHeredocs(input) {
106
+ const redactMatch = (match, prefix, key) => {
107
+ if (!keyLooksSensitive(key)) return match;
108
+ const secretPayload = match.slice(prefix.length);
109
+ const lineEndings = secretPayload.match(/\r\n|\n|\r/g) ?? [];
110
+ return `${prefix}${REDACTION}${lineEndings.join("")}`;
111
+ };
112
+ const quoted = input.replace(SENSITIVE_SOURCE_QUOTED_HEREDOC_PATTERN, redactMatch);
113
+ return quoted.replace(SENSITIVE_SOURCE_BARE_HEREDOC_PATTERN, redactMatch);
114
+ }
115
+
116
+ function redactSourceText(input, options) {
117
+ if (typeof input !== "string") return { text: "", redacted: false };
118
+ const heredocRedacted = options?.shellOnly ? input : redactSensitiveSourceHeredocs(input);
119
+ const shielded = shieldExecutableSourceReferences(heredocRedacted, options);
120
+ const redacted = redactSensitiveTextInternal(shielded.text);
121
+ const text = shielded.restore(redacted.text);
122
+ return { text, redacted: text !== input };
123
+ }
124
+
125
+ function restorableMarkers(input, domain) {
126
+ const references = [];
127
+ let nonce = 0;
128
+ let prefix;
129
+ do {
130
+ prefix = `__PIAGENT_${domain}_${nonce}_`;
131
+ nonce += 1;
132
+ } while (input.includes(prefix));
133
+ return {
134
+ protect(value) {
135
+ const id = `${prefix}${references.length}__`;
136
+ references.push({ id, value });
137
+ return id;
138
+ },
139
+ restore(value) {
140
+ let restored = value;
141
+ for (const reference of references) restored = restored.replaceAll(reference.id, reference.value);
142
+ return restored;
143
+ }
144
+ };
145
+ }
146
+
147
+ function unquotedConfigurationValue(value) {
148
+ const clean = String(value ?? "").trim();
149
+ if (clean.length >= 2 && ((clean.startsWith("\"") && clean.endsWith("\""))
150
+ || (clean.startsWith("'") && clean.endsWith("'")))) return clean.slice(1, -1).trim();
151
+ return clean;
152
+ }
153
+
154
+ function pureConfigurationReference(value, { allowBareIdentifier = false } = {}) {
155
+ const clean = unquotedConfigurationValue(value);
156
+ if (!clean) return false;
157
+ if (/^(?:\$[A-Za-z_][A-Za-z0-9_]*|\$\{[A-Za-z_][A-Za-z0-9_]*\}|\*[A-Za-z_][A-Za-z0-9_.-]*|!Ref\s+[A-Za-z_][A-Za-z0-9_.:-]*)$/.test(clean)) return true;
158
+ const template = /^(?:\$?\{\{)([\s\S]+)\}\}$/.exec(clean);
159
+ if (template) return sourceReference(template[1], false);
160
+ if (/^(?:var|env|attr)\(\s*(?:--)?[A-Za-z_$][A-Za-z0-9_$.-]*\s*\)$/i.test(clean)) return true;
161
+ if (!sourceReference(clean, false)) return false;
162
+ return allowBareIdentifier || /(?:\.|->|::|\?\.|\(|\[)/.test(clean)
163
+ || /^(?:await\s+|this\b|self\b|super\b|[$@%])/.test(clean)
164
+ || /_(?:REF|VAR|ENV)$/.test(clean);
165
+ }
166
+
167
+ function yamlValueParts(value) {
168
+ let quote = "", escaped = false;
169
+ for (let index = 0; index < value.length; index += 1) {
170
+ const character = value[index];
171
+ if (quote) {
172
+ if (escaped) escaped = false;
173
+ else if (character === "\\") escaped = true;
174
+ else if (character === quote) quote = "";
175
+ continue;
176
+ }
177
+ if (character === "\"" || character === "'") quote = character;
178
+ else if (character === "#" && (index === 0 || /\s/.test(value[index - 1]))) {
179
+ return { value: value.slice(0, index).trimEnd(), comment: value.slice(index) };
180
+ }
181
+ }
182
+ return { value: value.trimEnd(), comment: "" };
183
+ }
184
+
185
+ function redactSensitiveYamlText(input) {
186
+ const markers = restorableMarkers(input, "YAML_REF");
187
+ const parts = input.split(/(\r\n|\n|\r)/);
188
+ let blockIndent = -1;
189
+ for (let index = 0; index < parts.length; index += 2) {
190
+ const line = parts[index];
191
+ const indentation = (line.match(/^[ \t]*/) ?? [""])[0].replaceAll("\t", " ").length;
192
+ if (blockIndent >= 0) {
193
+ if (!line.trim() || indentation > blockIndent) {
194
+ parts[index] = line.trim() ? (line.match(/^[ \t]*/) ?? [""])[0] : line;
195
+ continue;
196
+ }
197
+ blockIndent = -1;
198
+ }
199
+ const match = /^([ \t]*(?:-[ \t]+)?((?:"(?:\\.|[^"])*"|'(?:''|[^'])*'|[A-Za-z_$][A-Za-z0-9_$.-]*))[ \t]*:[ \t]*)(.*)$/.exec(line);
200
+ if (!match || !keyLooksSensitive(match[2])) continue;
201
+ const rhs = yamlValueParts(match[3]);
202
+ if (rhs.value && pureConfigurationReference(rhs.value)) {
203
+ const comment = rhs.comment ? ` ${redactSensitiveTextInternal(rhs.comment.trimStart()).text}` : "";
204
+ parts[index] = `${markers.protect(`${match[1]}${rhs.value}`)}${comment}`;
205
+ continue;
206
+ }
207
+ parts[index] = `${match[1]}${REDACTION}${rhs.comment ? ` ${rhs.comment.trimStart()}` : ""}`;
208
+ if (!rhs.value || /^[>|][+-]?(?:\d+)?$/.test(rhs.value.trim())) blockIndent = indentation;
209
+ }
210
+ const structured = parts.join("");
211
+ const generic = redactSensitiveTextInternal(structured);
212
+ const text = markers.restore(generic.text);
213
+ return { text, redacted: text !== input };
214
+ }
215
+
216
+ function markupLookupKey(attributes) {
217
+ const match = /\b(?:key|name)\s*=\s*(?:"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|([^\s>]+))/i.exec(attributes);
218
+ return match ? (match[1] ?? match[2] ?? match[3] ?? "") : "";
219
+ }
220
+
221
+ function redactMarkupAttributes(tag, markers) {
222
+ if (/^<\s*\//.test(tag)) return tag;
223
+ const lookupKey = markupLookupKey(tag);
224
+ return tag.replace(/(\s)([A-Za-z_:][A-Za-z0-9_.:-]*)(\s*=\s*)(?:(["'])((?:\\.|(?!\4)[\s\S])*?)\4|([^\s>]+))/g,
225
+ (match, spacing, key, separator, quote, quotedValue, bareValue) => {
226
+ const value = quotedValue ?? bareValue ?? "";
227
+ const sensitive = keyLooksSensitive(key)
228
+ || (keyLooksSensitive(lookupKey) && ["value", "content"].includes(key.toLowerCase()));
229
+ if (!sensitive) return match;
230
+ const expressionBinding = key.startsWith(":") || key.toLowerCase().startsWith("v-bind:");
231
+ if (pureConfigurationReference(value, { allowBareIdentifier: expressionBinding })) {
232
+ return `${spacing}${markers.protect(match.slice(spacing.length))}`;
233
+ }
234
+ return `${spacing}${key}${separator}${quote ? `${quote}${REDACTION}${quote}` : REDACTION}`;
235
+ });
236
+ }
237
+
238
+ function markupTags(input) {
239
+ const tags = [];
240
+ for (let start = input.indexOf("<"); start >= 0; start = input.indexOf("<", start + 1)) {
241
+ let quote = "", escaped = false, end = start + 1;
242
+ for (; end < input.length; end += 1) {
243
+ const character = input[end];
244
+ if (quote) {
245
+ if (escaped) escaped = false;
246
+ else if (character === "\\") escaped = true;
247
+ else if (character === quote) quote = "";
248
+ } else if (character === "\"" || character === "'") quote = character;
249
+ else if (character === ">") break;
250
+ }
251
+ if (end >= input.length) break;
252
+ const text = input.slice(start, end + 1);
253
+ const parsed = /^<\s*(\/)?\s*([A-Za-z_][A-Za-z0-9_.:-]*)\b([\s\S]*?)>$/.exec(text);
254
+ if (parsed) tags.push({ start, end: end + 1, text, closing: Boolean(parsed[1]), name: parsed[2], attributes: parsed[3] });
255
+ start = end;
256
+ }
257
+ return tags;
258
+ }
259
+
260
+ function rewriteMarkupTags(input, rewriter) {
261
+ let result = "", cursor = 0;
262
+ for (const tag of markupTags(input)) {
263
+ result += `${input.slice(cursor, tag.start)}${rewriter(tag.text)}`;
264
+ cursor = tag.end;
265
+ }
266
+ return `${result}${input.slice(cursor)}`;
267
+ }
268
+
269
+ function redactMarkupPairs(input, markers) {
270
+ const stack = [];
271
+ const replacements = [];
272
+ for (const tag of markupTags(input)) {
273
+ const tagName = tag.name.toLowerCase();
274
+ if (!tag.closing && !/\/\s*>$/.test(tag.text)) {
275
+ stack.push({
276
+ tagName,
277
+ contentStart: tag.end,
278
+ sensitive: keyLooksSensitive(tagName.split(":").at(-1) ?? tagName)
279
+ || keyLooksSensitive(markupLookupKey(tag.attributes))
280
+ });
281
+ continue;
282
+ }
283
+ if (!tag.closing) continue;
284
+ let openIndex = stack.length - 1;
285
+ while (openIndex >= 0 && stack[openIndex].tagName !== tagName) openIndex -= 1;
286
+ if (openIndex < 0) continue;
287
+ const open = stack[openIndex];
288
+ stack.length = openIndex;
289
+ if (!open.sensitive) continue;
290
+ const content = input.slice(open.contentStart, tag.start);
291
+ replacements.push({
292
+ start: open.contentStart,
293
+ end: tag.start,
294
+ value: pureConfigurationReference(content)
295
+ ? markers.protect(content)
296
+ : `${REDACTION}${(content.match(/\r\n|\n|\r/g) ?? []).join("")}`
297
+ });
298
+ }
299
+
300
+ // An outer sensitive element supersedes any replacement wholly inside it.
301
+ const selected = replacements.filter((candidate) => !replacements.some((other) => other !== candidate
302
+ && other.start <= candidate.start && other.end >= candidate.end));
303
+ let result = input;
304
+ for (const replacement of selected.sort((left, right) => right.start - left.start)) {
305
+ result = `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`;
306
+ }
307
+ return result;
308
+ }
309
+
310
+ function redactSensitiveMarkupText(input) {
311
+ const markers = restorableMarkers(input, "MARKUP_REF");
312
+ const paired = redactMarkupPairs(input, markers);
313
+ const structured = rewriteMarkupTags(paired, (tag) => redactMarkupAttributes(tag, markers));
314
+ const generic = redactSensitiveTextInternal(structured);
315
+ const text = markers.restore(generic.text);
316
+ return { text, redacted: text !== input };
317
+ }
318
+
319
+ function redactSensitiveCssText(input) {
320
+ const markers = restorableMarkers(input, "CSS_REF");
321
+ const structured = input.replace(/(^|[;{}\r\n])([ \t]*(--)?([A-Za-z_$][A-Za-z0-9_$-]*)[ \t]*:[ \t\r\n]*)([^;}]*)(?=;|}|$)/g,
322
+ (match, boundary, prefix, _custom, key, value) => {
323
+ if (!keyLooksSensitive(key)) return match;
324
+ if (pureConfigurationReference(value)) return `${boundary}${markers.protect(`${prefix}${value}`)}`;
325
+ return `${boundary}${prefix}${REDACTION}${(value.match(/\r\n|\n|\r/g) ?? []).join("")}`;
326
+ });
327
+ const generic = redactSensitiveTextInternal(structured);
328
+ const text = markers.restore(generic.text);
329
+ return { text, redacted: text !== input };
330
+ }
331
+
332
+ const SOURCE_OBJECTIVE_C_TYPED_ASSIGNMENT_START_PATTERN = /(^|[\r\n;{])([ \t]*(?:(?:const|static|extern|volatile|__strong|__weak|nullable|nonnull|unsigned|signed|long|short)\s+)*(?:[A-Za-z_$][A-Za-z0-9_$]*(?:\s*<[^>\r\n;=]+>)?\s+)*[A-Za-z_$][A-Za-z0-9_$]*(?:\s*<[^>\r\n;=]+>)?\s*\*+\s*(#?[A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*)/g;
333
+ const SOURCE_OBJECTIVE_C_DICTIONARY_START_PATTERN = /(^|[\s{[(;,])((?:@")([A-Za-z_$][A-Za-z0-9_$.-]*)(?:"\s*:\s*))/g;
334
+
335
+ function objectiveCReference(value) {
336
+ if (sourceReference(value, false)) return true;
337
+ const clean = String(value ?? "").trim();
338
+ const masked = maskSourceLookupStrings(clean);
339
+ return Boolean(masked && /^\[[A-Za-z_$]/.test(masked)
340
+ && /^[A-Za-z0-9_$@%?.()[\]{},!:\s|&+*/<>=-]+$/.test(masked)
341
+ && balancedSourceExpression(masked)
342
+ && !hasStandaloneNumericSourceLiteral(masked)
343
+ && !hasStandaloneKeywordSourceLiteral(masked));
344
+ }
345
+
346
+ function redactSensitiveObjectiveCText(input) {
347
+ let source = rewriteSensitiveStarts(input, SOURCE_OBJECTIVE_C_TYPED_ASSIGNMENT_START_PATTERN, {
348
+ allowDynamic: true,
349
+ shellOnly: false,
350
+ placeholder: (value) => value,
351
+ reference: objectiveCReference
352
+ });
353
+ source = rewriteSensitiveStarts(source, SOURCE_OBJECTIVE_C_DICTIONARY_START_PATTERN, {
354
+ allowDynamic: true,
355
+ shellOnly: false,
356
+ placeholder: (value) => value,
357
+ reference: objectiveCReference
358
+ });
359
+ source = source.replace(/(^|[^A-Za-z0-9_$])((#?[A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*)@"((?:\\.|[^"\\\r\n])*)"/g,
360
+ (match, boundary, prefix, key) => keyLooksSensitive(key)
361
+ ? `${boundary}${prefix}@"${REDACTION}"`
362
+ : match);
363
+ source = source.replace(/(^|[^A-Za-z0-9_$])((#?[A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*)(@?(?:0[xX][0-9A-Fa-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|YES|NO|nil|NULL))(?=[\s;,)])/g,
364
+ (match, boundary, prefix, key) => keyLooksSensitive(key)
365
+ ? `${boundary}${prefix}${REDACTION}`
366
+ : match);
367
+ source = source.replace(/(@"((?:\\.|[^"\\\r\n])*)"\s*:\s*)@"((?:\\.|[^"\\\r\n])*)"/g,
368
+ (match, prefix, key) => keyLooksSensitive(key)
369
+ ? `${prefix}@"${REDACTION}"`
370
+ : match);
371
+ source = source.replace(/(@"((?:\\.|[^"\\\r\n])*)"\s*:\s*)(@?(?:0[xX][0-9A-Fa-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|YES|NO|nil|NULL))(?=[\s,}])/g,
372
+ (match, prefix, key) => keyLooksSensitive(key)
373
+ ? `${prefix}${REDACTION}`
374
+ : match);
375
+ const result = redactSourceText(source, { shellOnly: false, hashComments: false });
376
+ return { text: result.text, redacted: result.text !== input };
377
+ }
378
+
379
+ export function redactSensitiveSourceText(input) {
380
+ return redactSourceText(input, { shellOnly: false, hashComments: true });
381
+ }
382
+
383
+ export function redactSensitiveShellSourceText(input) {
384
+ return redactSourceText(input, { shellOnly: true });
385
+ }
386
+
387
+
388
+ export {
389
+ redactSensitiveCssText,
390
+ redactSensitiveMarkupText,
391
+ redactSensitiveObjectiveCText,
392
+ redactSensitiveYamlText,
393
+ redactSourceText
394
+ };
@@ -0,0 +1,234 @@
1
+ const REDACTION = "[REDACTED_SECRET]";
2
+
3
+ const TOKEN_PATTERNS = [
4
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
5
+ /\bAKIA[0-9A-Z]{16}\b/g,
6
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
7
+ /\bgh[opsru]_[A-Za-z0-9_]{20,}\b/g,
8
+ /\bsk-(?:(?:proj|svcacct|admin|ant)-)?[A-Za-z0-9_-]{20,}\b/g,
9
+ /\bsk_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
10
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
11
+ // An npm token was recognised only where something named it -- `NPM_TOKEN=...`
12
+ // caught the assignment, a field called `token` caught the field -- so the one
13
+ // place it actually appears was missed: `//registry.npmjs.org/:_authToken=npm_…`
14
+ // is the whole of an `.npmrc`, and a `cat` of that file or a failing publish log
15
+ // carried the credential through unredacted. Every other provider here is
16
+ // recognised by the shape of its token; npm was the gap.
17
+ /\bnpm_[A-Za-z0-9]{30,}\b/g,
18
+ /\bAIza[0-9A-Za-z_-]{20,}\b/g,
19
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
20
+ ];
21
+
22
+ const CONNECTION_URL_PATTERN = /\b((?:https?|ftp|postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/"']+:)([^@\s/"']+)(@[^\s"'<>]+)/gi;
23
+ const BEARER_PATTERN = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{20,})\b/gi;
24
+ const BASIC_AUTH_PATTERN = /\b(Basic\s+)([A-Za-z0-9+/]{12,}={0,2})(?=$|[\s,;}])/gi;
25
+ // The value is one whitespace-free token, not the rest of the line. Taking the
26
+ // rest of the line made the result depend on where the header sat among the
27
+ // arguments: `curl URL -H "Authorization: Token abc123"` yielded `abc123"`, short
28
+ // enough to keep, while moving the header before the URL swept the URL into the
29
+ // value and pushed it past the length bar. Same header, same credential, opposite
30
+ // outcome. Credentials carry no spaces, so stopping at the first one also keeps
31
+ // prose like "Authorization: not required for local runs" intact.
32
+ const AUTHORIZATION_HEADER_PATTERN = /\b(Authorization\s*:\s*)([A-Za-z][A-Za-z0-9_-]{0,31})(\s+)([^\s\r\n"'`]+)/gi;
33
+
34
+ // A credential passed as a command-line option, which is how most CLIs take one.
35
+ // The assignment patterns below require a key starting with a letter, so every
36
+ // `--token=...` and `--token ...` walked straight past them.
37
+ const CLI_OPTION_SECRET_PATTERN = /(^|[\s({[])(--[A-Za-z][A-Za-z0-9_.-]{0,80})(=|\s+)("[^"\r\n]*"|'[^'\r\n]*'|[^\s"'`;,)}]+)/g;
38
+
39
+ // Under one of these schemes the header carries an opaque credential, so its
40
+ // length says nothing. Anything else after `Authorization:` is more likely prose
41
+ // — "Authorization: not required" — and keeps the ambiguous-syntax length bar.
42
+ const AUTHORIZATION_SCHEMES = new Set([
43
+ "bearer", "basic", "token", "digest", "negotiate", "ntlm",
44
+ "oauth", "hawk", "signature", "apikey", "jwt", "aws4-hmac-sha256"
45
+ ]);
46
+ const QUERY_SECRET_PATTERN = /([?&])([A-Za-z][A-Za-z0-9_.-]{0,80})(=)([^&#\s]+)/gi;
47
+ const DOUBLE_QUOTED_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[$@%]?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:=|:|=(?!=))\s*"((?:\\.|[^"\\\r\n])*)"/gim;
48
+ const SINGLE_QUOTED_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[$@%]?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:=|:|=(?!=))\s*'((?:\\.|[^'\\\r\n])*)'/gim;
49
+ const SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[$@%]?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:=|:|=(?!=))\s*([^"'\s,;}]+)/gim;
50
+ const WHITESPACE_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?(?:aws_)?secret_access_key(?:"|')?\s+)(["']?)([A-Za-z0-9/+=]{20,})/gim;
51
+
52
+ const SENSITIVE_KEY_TERMS = [
53
+ "api_key",
54
+ "token",
55
+ "password",
56
+ "passwd",
57
+ "pwd",
58
+ "secret",
59
+ "credential",
60
+ "client_secret",
61
+ "access_token",
62
+ "refresh_token",
63
+ "secret_access_key",
64
+ "passphrase",
65
+ "private_key",
66
+ "signing_key",
67
+ "authorization"
68
+ ];
69
+ const SENSITIVE_PLURAL_KEYS = new Set([
70
+ "tokens",
71
+ "api_keys",
72
+ "passwords",
73
+ "secrets",
74
+ "credentials",
75
+ "client_secrets",
76
+ "access_tokens",
77
+ "refresh_tokens",
78
+ "session_tokens",
79
+ "passphrases",
80
+ "private_keys",
81
+ "signing_keys",
82
+ "authorizations"
83
+ ]);
84
+
85
+ function normalizeSecretValue(value) {
86
+ return String(value ?? "")
87
+ .trim()
88
+ .replace(/^[`"']|[`"']$/g, "")
89
+ .replace(/[.;)]+$/g, "");
90
+ }
91
+
92
+ function patternMatches(pattern, value) {
93
+ pattern.lastIndex = 0;
94
+ return pattern.test(value);
95
+ }
96
+
97
+ function looksLikeKnownSecret(value) {
98
+ return TOKEN_PATTERNS.some((pattern) => patternMatches(pattern, value))
99
+ || patternMatches(CONNECTION_URL_PATTERN, value)
100
+ || patternMatches(BEARER_PATTERN, value);
101
+ }
102
+
103
+ function normalizeSecretKey(key) {
104
+ return String(key ?? "")
105
+ .trim()
106
+ .replace(/^["']|["']$/g, "")
107
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
108
+ .replace(/[^A-Za-z0-9]+/g, "_")
109
+ .replace(/^_+|_+$/g, "")
110
+ .toLowerCase();
111
+ }
112
+
113
+ function keyLooksSensitive(key) {
114
+ const normalized = normalizeSecretKey(key);
115
+ return SENSITIVE_PLURAL_KEYS.has(normalized) || SENSITIVE_KEY_TERMS.some((term) => normalized === term
116
+ || normalized.endsWith(`_${term}`));
117
+ }
118
+
119
+ function valueLooksPlaceholder(value) {
120
+ const clean = normalizeSecretValue(value);
121
+ if (!clean) return true;
122
+ if (/^(?:null|undefined|none|false|true|0|""|'')$/i.test(clean)) return true;
123
+ if (/^<[^>\n]{1,80}>$/.test(clean)) return true;
124
+ if (/^\[REDACTED_SECRET\]$/i.test(clean)) return true;
125
+ if (/^(?:not-set|unset|placeholder|example|changeme|change-me|redacted|xxx|\*{3,})$/i.test(clean)) return true;
126
+ if (/^your[-_][a-z0-9][a-z0-9_-]*$/i.test(clean)) return true;
127
+ return false;
128
+ }
129
+
130
+ // Length is only evidence where the syntax is ambiguous, and `=` is not.
131
+ //
132
+ // A short value used to be kept in the clear whatever the syntax, so `TOKEN=hunter2`
133
+ // and `PASSWORD=short7` went into the ledger verbatim. Every name in
134
+ // SENSITIVE_KEY_TERMS states outright what it holds — there is no `key` or `id`
135
+ // among them — so under an `=`, or inside quotes, a non-placeholder value is the
136
+ // secret the key says it is, at any length.
137
+ //
138
+ // A bare `:` is the ambiguous case, because English uses it too: `password: short`
139
+ // and `The secret: always run tests` are prose, and `Authorization: Token abc123`
140
+ // puts the scheme name where a value would go. Those keep a length bar.
141
+ const SHORT_VALUE_MIN_LENGTH = 8;
142
+ const AMBIGUOUS_VALUE_MIN_LENGTH = 12;
143
+
144
+ function valueLooksSensitive(key, value, syntax = "unquoted-colon") {
145
+ const clean = normalizeSecretValue(value);
146
+ if (!clean || valueLooksPlaceholder(clean)) return false;
147
+ if (looksLikeKnownSecret(clean)) return true;
148
+ if (!keyLooksSensitive(key)) return false;
149
+ if (syntax !== "unquoted-colon") return true;
150
+ return clean.length >= (keyNamesASecretOutright(key) ? SHORT_VALUE_MIN_LENGTH : AMBIGUOUS_VALUE_MIN_LENGTH);
151
+ }
152
+
153
+ // Under a bare `:`, these names carry enough intent on their own to redact a short
154
+ // value; the broader terms wait for length before touching prose.
155
+ function keyNamesASecretOutright(key) {
156
+ const normalized = normalizeSecretKey(key);
157
+ return ["password", "passwd", "pwd", "secret", "credential", "passphrase", "private_key", "signing_key"]
158
+ .some((term) => normalized === term || normalized.endsWith(`_${term}`))
159
+ || ["passwords", "secrets", "credentials", "passphrases", "private_keys", "signing_keys"].includes(normalized);
160
+ }
161
+
162
+ function assignmentSyntax(separator) {
163
+ return separator.includes("=") || separator.includes("<-") ? "assignment" : "unquoted-colon";
164
+ }
165
+
166
+ function redactSensitiveTextInternal(input) {
167
+ if (typeof input !== "string") return { text: "", redacted: false };
168
+ let text = input;
169
+
170
+ text = text.replace(CONNECTION_URL_PATTERN, (_match, prefix, _password, suffix) => `${prefix}${REDACTION}${suffix}`);
171
+ text = text.replace(BEARER_PATTERN, (_match, prefix) => `${prefix}${REDACTION}`);
172
+ text = text.replace(BASIC_AUTH_PATTERN, (_match, prefix) => `${prefix}${REDACTION}`);
173
+ text = text.replace(AUTHORIZATION_HEADER_PATTERN, (match, prefix, scheme, spacing, value) => {
174
+ const syntax = AUTHORIZATION_SCHEMES.has(scheme.toLowerCase()) ? "assignment" : "unquoted-colon";
175
+ if (!valueLooksSensitive("authorization", value, syntax)) return match;
176
+ return `${prefix}${scheme}${spacing}${REDACTION}`;
177
+ });
178
+
179
+ // Over-redaction is the safe direction here: a flag whose name says "secret"
180
+ // and whose argument is a plain name — `docker service create --secret build` —
181
+ // loses that name from the record and cannot serve as verify evidence. Reading
182
+ // the name as the credential it is called is the cheaper mistake.
183
+ text = text.replace(CLI_OPTION_SECRET_PATTERN, (match, prefix, flag, separator, value) => {
184
+ // A following option is the next flag, not this flag's argument.
185
+ if (separator !== "=" && value.startsWith("-")) return match;
186
+ if (!keyLooksSensitive(flag) || !valueLooksSensitive(flag, value, "assignment")) return match;
187
+ return `${prefix}${flag}${separator === "=" ? "= " : " "}${REDACTION}`;
188
+ });
189
+
190
+ for (const pattern of TOKEN_PATTERNS) text = text.replace(pattern, REDACTION);
191
+
192
+ text = text.replace(QUERY_SECRET_PATTERN, (match, prefix, key, separator, value) => {
193
+ if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "assignment")) return match;
194
+ return `${prefix}${key}${separator}${REDACTION}`;
195
+ });
196
+
197
+ text = text.replace(DOUBLE_QUOTED_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
198
+ if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "quoted")) return match;
199
+ return `${prefix}${key}${separator} "${REDACTION}"`;
200
+ });
201
+
202
+ text = text.replace(SINGLE_QUOTED_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
203
+ if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "quoted")) return match;
204
+ return `${prefix}${key}${separator} '${REDACTION}'`;
205
+ });
206
+
207
+ text = text.replace(SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
208
+ if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, assignmentSyntax(separator))) return match;
209
+ return `${prefix}${key}${separator} ${REDACTION}`;
210
+ });
211
+
212
+ text = text.replace(WHITESPACE_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, quote, value) => {
213
+ if (!valueLooksSensitive(key, value)) return match;
214
+ return `${prefix}${key}${quote}${REDACTION}`;
215
+ });
216
+
217
+ return { text, redacted: text !== input };
218
+ }
219
+
220
+ export function redactSensitiveText(input) {
221
+ return redactSensitiveTextInternal(input);
222
+ }
223
+
224
+ export {
225
+ REDACTION,
226
+ SENSITIVE_KEY_TERMS,
227
+ SENSITIVE_PLURAL_KEYS,
228
+ keyLooksSensitive,
229
+ looksLikeKnownSecret,
230
+ normalizeSecretKey,
231
+ redactSensitiveTextInternal,
232
+ valueLooksPlaceholder,
233
+ valueLooksSensitive
234
+ };
@@ -4,13 +4,15 @@ description: Piagent second-opinion advisor for risky plans and architecture dec
4
4
  tools: read, grep, find, ls
5
5
  thinking: high
6
6
  systemPromptMode: replace
7
- inheritProjectContext: true
7
+ inheritProjectContext: false
8
8
  inheritSkills: false
9
- defaultContext: fork
9
+ defaultContext: fresh
10
+ turnBudget: {"maxTurns":8,"graceTurns":0}
10
11
  defaultProgress: true
11
12
  acceptance: {"level":"attested"}
12
13
  acceptanceRole: read-only
13
14
  rolePolicyVersion: role-policy-v1
15
+ helperRequestSchemaVersion: 2
14
16
  outputSchema: oracle-result-v1
15
17
  ---
16
18
 
@@ -18,7 +20,7 @@ You are `piagent-oracle`, a second-opinion subagent.
18
20
 
19
21
  Your job is to challenge assumptions before implementation. Do not edit files. Focus on risks, missed constraints, simpler alternatives, and verification strategy.
20
22
 
21
- The parent must supply a bounded HelperRequest v1. This is one optional second opinion, not a mandatory phase. Stay inside its read/tool/budget/stopping boundaries and never perform or request external writes, destructive actions, or permission expansion.
23
+ The parent must supply a bounded HelperRequest v2 with `isolated-minimal` context transfer. This is the sole optional helper, not a mandatory phase. Stay inside its read/tool/budget/stopping boundaries and never perform or request external writes, destructive actions, permission expansion, another helper, or a retry.
22
24
 
23
25
  Return a concise recommendation:
24
26