@piagent/platform 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CHANGELOG.md +56 -3
  2. package/README.md +3 -3
  3. package/architecture/layers.json +96 -1
  4. package/docs/README.md +1 -0
  5. package/docs/architecture.md +1 -0
  6. package/docs/authority-manifest.md +1 -0
  7. package/docs/auto-delegation-policy.md +1 -0
  8. package/docs/capability-packs.md +10 -2
  9. package/docs/command-reference-vietnamese.md +5 -4
  10. package/docs/context-engine.md +1 -0
  11. package/docs/context-window-policy.md +1 -0
  12. package/docs/distribution-standard.md +8 -7
  13. package/docs/herdr-workflow.md +1 -0
  14. package/docs/mcp-and-tools.md +14 -9
  15. package/docs/memory-policy.md +1 -0
  16. package/docs/model-options.md +1 -0
  17. package/docs/oauth-providers.md +1 -0
  18. package/docs/operator-manual-vietnamese.md +15 -12
  19. package/docs/package-architecture-notes.md +1 -0
  20. package/docs/project-adapters.md +1 -0
  21. package/docs/project-onboarding.md +1 -0
  22. package/docs/publishing-for-teams.md +1 -0
  23. package/docs/quality-benchmark.md +1 -0
  24. package/docs/quickstart-vietnamese.md +3 -2
  25. package/docs/release-install-policy.md +11 -7
  26. package/docs/runtime-harness-standard.md +1 -0
  27. package/docs/runtime-policy-design.md +1 -0
  28. package/docs/runtime-quality-baseline.md +1 -0
  29. package/docs/security-redaction-benchmark.md +1 -0
  30. package/docs/security-threat-model.md +2 -1
  31. package/docs/subagent-orchestration-capabilities.md +1 -0
  32. package/docs/subagents-and-multiagent.md +1 -0
  33. package/docs/task-implementation-contract.md +1 -0
  34. package/docs/task-lifecycle-vietnamese.md +1 -0
  35. package/docs/team-onboarding.md +3 -2
  36. package/docs/third-party-neutrality.md +26 -0
  37. package/docs/usage-observability.md +1 -0
  38. package/docs/vercel-docs-site.md +1 -0
  39. package/docs/workflow-recipes.md +1 -0
  40. package/docs-site/architecture.html +1 -1
  41. package/docs-site/benchmark.html +1 -1
  42. package/docs-site/commands.html +1 -1
  43. package/docs-site/context-engine.html +1 -1
  44. package/docs-site/en/architecture.html +1 -1
  45. package/docs-site/en/benchmark.html +1 -1
  46. package/docs-site/en/commands.html +1 -1
  47. package/docs-site/en/context-engine.html +1 -1
  48. package/docs-site/en/index.html +1 -1
  49. package/docs-site/en/mcp-approval.html +1 -1
  50. package/docs-site/en/mcp-auth.html +1 -1
  51. package/docs-site/en/mcp-servers.html +1 -1
  52. package/docs-site/en/mcp.html +1 -1
  53. package/docs-site/en/permissions.html +1 -1
  54. package/docs-site/en/profiles.html +1 -1
  55. package/docs-site/en/quickstart.html +1 -1
  56. package/docs-site/en/runtime.html +1 -1
  57. package/docs-site/en/scope.html +1 -1
  58. package/docs-site/en/security.html +1 -1
  59. package/docs-site/en/team.html +1 -1
  60. package/docs-site/en/whats-new.html +1 -1
  61. package/docs-site/en/workflows.html +1 -1
  62. package/docs-site/index.html +1 -1
  63. package/docs-site/mcp-approval.html +1 -1
  64. package/docs-site/mcp-auth.html +1 -1
  65. package/docs-site/mcp-servers.html +1 -1
  66. package/docs-site/mcp.html +1 -1
  67. package/docs-site/permissions.html +1 -1
  68. package/docs-site/profiles.html +1 -1
  69. package/docs-site/quickstart.html +1 -1
  70. package/docs-site/runtime.html +1 -1
  71. package/docs-site/scope.html +1 -1
  72. package/docs-site/security.html +1 -1
  73. package/docs-site/team.html +1 -1
  74. package/docs-site/whats-new.html +1 -1
  75. package/docs-site/workflows.html +1 -1
  76. package/evals/fixtures/mutation-provenance-record.invalid.json +17 -0
  77. package/evals/fixtures/mutation-provenance-record.valid.json +27 -0
  78. package/evals/fixtures/piagent-webui/approval-v1.invalid.json +54 -0
  79. package/evals/fixtures/piagent-webui/approval-v1.valid.json +58 -0
  80. package/evals/fixtures/piagent-webui/attachment-v1.invalid.json +25 -0
  81. package/evals/fixtures/piagent-webui/attachment-v1.valid.json +25 -0
  82. package/evals/fixtures/piagent-webui/capabilities-v1.invalid.json +51 -0
  83. package/evals/fixtures/piagent-webui/capabilities-v1.valid.json +122 -0
  84. package/evals/fixtures/piagent-webui/commit-summary-v1.invalid.json +10 -0
  85. package/evals/fixtures/piagent-webui/commit-summary-v1.valid.json +25 -0
  86. package/evals/fixtures/piagent-webui/control-command-v1.invalid.json +37 -0
  87. package/evals/fixtures/piagent-webui/control-command-v1.valid.json +35 -0
  88. package/evals/fixtures/piagent-webui/diff-v1.invalid.json +13 -0
  89. package/evals/fixtures/piagent-webui/diff-v1.valid.json +141 -0
  90. package/evals/fixtures/piagent-webui/gateway-capabilities-v1.invalid.json +35 -0
  91. package/evals/fixtures/piagent-webui/gateway-capabilities-v1.valid.json +35 -0
  92. package/evals/fixtures/piagent-webui/gateway-protocol-v1.invalid.json +14 -0
  93. package/evals/fixtures/piagent-webui/gateway-protocol-v1.valid.json +14 -0
  94. package/evals/fixtures/piagent-webui/handoff-history-v1.invalid.json +16 -0
  95. package/evals/fixtures/piagent-webui/handoff-history-v1.valid.json +16 -0
  96. package/evals/fixtures/piagent-webui/model-catalog-v1.invalid.json +31 -0
  97. package/evals/fixtures/piagent-webui/model-catalog-v1.valid.json +43 -0
  98. package/evals/fixtures/piagent-webui/queue-v1.invalid.json +29 -0
  99. package/evals/fixtures/piagent-webui/queue-v1.valid.json +44 -0
  100. package/evals/fixtures/piagent-webui/recovery-history-v1.invalid.json +17 -0
  101. package/evals/fixtures/piagent-webui/recovery-history-v1.valid.json +20 -0
  102. package/evals/fixtures/piagent-webui/release-monitor-v1.invalid.json +12 -0
  103. package/evals/fixtures/piagent-webui/release-monitor-v1.valid.json +29 -0
  104. package/evals/fixtures/piagent-webui/review-state-v1.invalid.json +25 -0
  105. package/evals/fixtures/piagent-webui/review-state-v1.valid.json +38 -0
  106. package/evals/fixtures/piagent-webui/runtime-event-v2.invalid.json +49 -0
  107. package/evals/fixtures/piagent-webui/runtime-event-v2.valid.json +49 -0
  108. package/evals/fixtures/piagent-webui/session-catalog-v1.invalid.json +17 -0
  109. package/evals/fixtures/piagent-webui/session-catalog-v1.valid.json +58 -0
  110. package/evals/fixtures/piagent-webui/session-command-v1.invalid.json +19 -0
  111. package/evals/fixtures/piagent-webui/session-command-v1.valid.json +19 -0
  112. package/evals/fixtures/piagent-webui/snapshot-v1.invalid.json +5 -0
  113. package/evals/fixtures/piagent-webui/snapshot-v1.valid.json +191 -0
  114. package/evals/fixtures/piagent-webui/source-change-v1.invalid.json +57 -0
  115. package/evals/fixtures/piagent-webui/source-change-v1.valid.json +98 -0
  116. package/evals/fixtures/piagent-webui/source-mutation-v1.invalid.json +19 -0
  117. package/evals/fixtures/piagent-webui/source-mutation-v1.valid.json +38 -0
  118. package/evals/fixtures/piagent-webui/source-revert-v1.invalid.json +20 -0
  119. package/evals/fixtures/piagent-webui/source-revert-v1.valid.json +62 -0
  120. package/evals/fixtures/piagent-webui/subagent-tree-v1.invalid.json +26 -0
  121. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +39 -0
  122. package/evals/fixtures/piagent-webui/task-index-v1.invalid.json +15 -0
  123. package/evals/fixtures/piagent-webui/task-index-v1.valid.json +15 -0
  124. package/evals/fixtures/piagent-webui/task-timeline-v1.invalid.json +14 -0
  125. package/evals/fixtures/piagent-webui/task-timeline-v1.valid.json +16 -0
  126. package/evals/fixtures/piagent-webui/transcript-v1.invalid.json +35 -0
  127. package/evals/fixtures/piagent-webui/transcript-v1.valid.json +55 -0
  128. package/evals/fixtures/task-baseline-manifest.invalid.json +16 -0
  129. package/evals/fixtures/task-baseline-manifest.valid.json +41 -0
  130. package/evals/fixtures/verifier-file-snapshot.invalid.json +21 -0
  131. package/evals/fixtures/verifier-file-snapshot.valid.json +21 -0
  132. package/package.json +30 -3
  133. package/packages/piagent-core/benchmark/benchmark-candidate.d.ts +1 -0
  134. package/packages/piagent-core/benchmark/benchmark-forensics.d.ts +1 -0
  135. package/packages/piagent-core/benchmark/benchmark-ledger.d.ts +4 -0
  136. package/packages/piagent-core/benchmark/benchmark-record-validation.d.ts +1 -0
  137. package/packages/piagent-core/benchmark/benchmark-runtime.d.ts +1 -0
  138. package/packages/piagent-core/capabilities/capability-core.js +2 -2
  139. package/packages/piagent-core/capabilities/capability-verification-cache.js +280 -0
  140. package/packages/piagent-core/capabilities/runtime-integrity.js +153 -1
  141. package/packages/piagent-core/extensions/document-intake.ts +24 -10
  142. package/packages/piagent-core/extensions/piagent-guard.ts +90 -73
  143. package/packages/piagent-core/extensions/redaction-core.d.ts +2 -0
  144. package/packages/piagent-core/extensions/state-retention.js +77 -44
  145. package/packages/piagent-core/extensions/task-journal.js +30 -1
  146. package/packages/piagent-core/extensions/task-state.js +33 -36
  147. package/packages/piagent-core/mcp/mcp-server-catalog.js +5 -5
  148. package/packages/piagent-core/package.json +1 -1
  149. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +2 -2
  150. package/packages/piagent-core/runtime/hooks/tool-call-hook.ts +5 -2
  151. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +20 -19
  152. package/packages/piagent-core/runtime/hooks/tool-result-metadata.ts +13 -0
  153. package/packages/piagent-core/runtime/input/chat-images.ts +2 -2
  154. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +94 -0
  155. package/packages/piagent-core/runtime/inspection/approval-broker.ts +318 -0
  156. package/packages/piagent-core/runtime/inspection/commit-summary-projection.ts +59 -0
  157. package/packages/piagent-core/runtime/inspection/context-telemetry-inspection.ts +37 -0
  158. package/packages/piagent-core/runtime/inspection/criteria-links.ts +295 -0
  159. package/packages/piagent-core/runtime/inspection/diff-projection.ts +499 -0
  160. package/packages/piagent-core/runtime/inspection/git-filter-safety.ts +38 -0
  161. package/packages/piagent-core/runtime/inspection/git-numstat-adapter.ts +71 -0
  162. package/packages/piagent-core/runtime/inspection/git-status-adapter.ts +493 -0
  163. package/packages/piagent-core/runtime/inspection/git-tree-adapter.ts +108 -0
  164. package/packages/piagent-core/runtime/inspection/mcp-control.ts +10 -0
  165. package/packages/piagent-core/runtime/inspection/mutation-provenance-contract.ts +131 -0
  166. package/packages/piagent-core/runtime/inspection/mutation-provenance-recorder.ts +48 -0
  167. package/packages/piagent-core/runtime/inspection/mutation-provenance-store.ts +161 -0
  168. package/packages/piagent-core/runtime/inspection/project-runtime-inspection.ts +36 -0
  169. package/packages/piagent-core/runtime/inspection/review-state-contract.ts +108 -0
  170. package/packages/piagent-core/runtime/inspection/review-state-projection.ts +93 -0
  171. package/packages/piagent-core/runtime/inspection/review-state-store.ts +112 -0
  172. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +262 -0
  173. package/packages/piagent-core/runtime/inspection/same-process-bridge-proof.ts +103 -0
  174. package/packages/piagent-core/runtime/inspection/source-change-projection.ts +471 -0
  175. package/packages/piagent-core/runtime/inspection/source-evidence-contract.ts +139 -0
  176. package/packages/piagent-core/runtime/inspection/source-evidence-store.ts +315 -0
  177. package/packages/piagent-core/runtime/inspection/source-handoff-store.ts +70 -0
  178. package/packages/piagent-core/runtime/inspection/source-inspection-plan.ts +34 -0
  179. package/packages/piagent-core/runtime/inspection/source-mutation-projection.ts +164 -0
  180. package/packages/piagent-core/runtime/inspection/source-mutation-store.ts +168 -0
  181. package/packages/piagent-core/runtime/inspection/source-open-target.ts +38 -0
  182. package/packages/piagent-core/runtime/inspection/source-revert-projection.ts +147 -0
  183. package/packages/piagent-core/runtime/inspection/task-baseline-start-capture.ts +22 -0
  184. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +160 -0
  185. package/packages/piagent-core/runtime/inspection/task-control-journal.ts +131 -0
  186. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +95 -0
  187. package/packages/piagent-core/runtime/inspection/task-provenance-projection.ts +60 -0
  188. package/packages/piagent-core/runtime/inspection/task-recovery-timeline.ts +88 -0
  189. package/packages/piagent-core/runtime/inspection/task-run-index.ts +87 -0
  190. package/packages/piagent-core/runtime/inspection/task-source-projection.ts +401 -0
  191. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +101 -0
  192. package/packages/piagent-core/runtime/inspection/text-diff.ts +60 -0
  193. package/packages/piagent-core/runtime/inspection/verifier-snapshot-contract.ts +114 -0
  194. package/packages/piagent-core/runtime/inspection/verifier-snapshot-store.ts +189 -0
  195. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +320 -0
  196. package/packages/piagent-core/runtime/inspection/workspace-file-reader.ts +132 -0
  197. package/packages/piagent-core/runtime/inspection/workspace-source-projection.ts +96 -0
  198. package/packages/piagent-core/runtime/inspection/zero-turn-conformance.ts +175 -0
  199. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +68 -4
  200. package/packages/piagent-core/runtime/policy/source-index-transaction.ts +173 -0
  201. package/packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts +42 -0
  202. package/packages/piagent-core/runtime/policy/source-mutation-guard.ts +184 -0
  203. package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +105 -0
  204. package/packages/piagent-core/runtime/product/activity-inspector-footer.ts +5 -3
  205. package/packages/piagent-core/runtime/product/activity-inspector.ts +85 -343
  206. package/packages/piagent-core/runtime/recovery/resume-state.ts +38 -1
  207. package/packages/piagent-core/runtime/registration/activity-inspector-command.ts +68 -14
  208. package/packages/piagent-core/runtime/registration/task-start-tool.ts +13 -14
  209. package/packages/piagent-core/runtime/session/model-authorship-state.ts +31 -3
  210. package/packages/piagent-core/runtime/session/runtime-state.ts +11 -1
  211. package/packages/piagent-webui/dist/client/assets/index-DjMntaK5.js +172 -0
  212. package/packages/piagent-webui/dist/client/assets/index-DwKCWbwd.css +1 -0
  213. package/packages/piagent-webui/dist/client/index.html +16 -0
  214. package/packages/piagent-webui/extension/attachment-store.ts +323 -0
  215. package/packages/piagent-webui/extension/held-message-queue.ts +311 -0
  216. package/packages/piagent-webui/extension/lifecycle-controller.ts +408 -0
  217. package/packages/piagent-webui/extension/lifecycle-event-adapter.ts +23 -0
  218. package/packages/piagent-webui/extension/piagent-webui.ts +470 -0
  219. package/packages/piagent-webui/extension/review-controller.ts +160 -0
  220. package/packages/piagent-webui/extension/same-session-bridge.ts +500 -0
  221. package/packages/piagent-webui/extension/session-options-controller.ts +281 -0
  222. package/packages/piagent-webui/extension/session-stream-adapter.ts +249 -0
  223. package/packages/piagent-webui/extension/source-mutation-controller.ts +215 -0
  224. package/packages/piagent-webui/extension/source-open-controller.ts +136 -0
  225. package/packages/piagent-webui/extension/source-revert-controller.ts +189 -0
  226. package/packages/piagent-webui/extension/terminal-session-adapter.ts +73 -0
  227. package/packages/piagent-webui/extension/vscode-handoff.ts +51 -0
  228. package/packages/piagent-webui/gateway/control-socket.ts +92 -0
  229. package/packages/piagent-webui/gateway/gateway-events.ts +60 -0
  230. package/packages/piagent-webui/gateway/gateway-protocol-service.ts +64 -0
  231. package/packages/piagent-webui/gateway/gateway-service.ts +215 -0
  232. package/packages/piagent-webui/gateway/gateway-session-stream.ts +110 -0
  233. package/packages/piagent-webui/gateway/mcp-auth-broker.ts +158 -0
  234. package/packages/piagent-webui/gateway/native-project-picker.ts +29 -0
  235. package/packages/piagent-webui/gateway/pi-host.ts +41 -0
  236. package/packages/piagent-webui/gateway/profile-state.ts +1 -0
  237. package/packages/piagent-webui/gateway/project-registry.ts +108 -0
  238. package/packages/piagent-webui/gateway/provider-auth-broker.ts +172 -0
  239. package/packages/piagent-webui/gateway/session-catalog.ts +128 -0
  240. package/packages/piagent-webui/gateway/session-command-controller.ts +237 -0
  241. package/packages/piagent-webui/gateway/session-command-store.ts +224 -0
  242. package/packages/piagent-webui/gateway/session-inspection-registry.ts +204 -0
  243. package/packages/piagent-webui/gateway/session-lease-store.ts +1 -0
  244. package/packages/piagent-webui/gateway/session-metadata-store.ts +152 -0
  245. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +485 -0
  246. package/packages/piagent-webui/ownership/gateway-runtime-context.ts +8 -0
  247. package/packages/piagent-webui/ownership/profile-state.ts +117 -0
  248. package/packages/piagent-webui/ownership/session-lease-store.ts +329 -0
  249. package/packages/piagent-webui/ownership/session-refs.ts +8 -0
  250. package/packages/piagent-webui/package.json +33 -0
  251. package/packages/piagent-webui/server/README.md +4 -0
  252. package/packages/piagent-webui/server/benchmark-release-monitor.ts +230 -0
  253. package/packages/piagent-webui/server/core-inspection-provider.ts +403 -0
  254. package/packages/piagent-webui/server/gateway-websocket.ts +143 -0
  255. package/packages/piagent-webui/server/http-security.ts +27 -0
  256. package/packages/piagent-webui/server/ipc-read-model-client.ts +89 -0
  257. package/packages/piagent-webui/server/loopback-server.ts +368 -0
  258. package/packages/piagent-webui/server/read-model-provider.ts +37 -0
  259. package/packages/piagent-webui/server/read-only-router.ts +105 -0
  260. package/packages/piagent-webui/server/session-auth.ts +88 -0
  261. package/packages/piagent-webui/server/sidecar-main.ts +86 -0
  262. package/packages/piagent-webui/server/sse-hub.ts +76 -0
  263. package/packages/piagent-webui/server/static-bundle.ts +40 -0
  264. package/packages/piagent-webui/server/transcript-projection.ts +110 -0
  265. package/schemas/mutation-provenance-record.schema.json +100 -0
  266. package/schemas/piagent-webui/approval-v1.schema.json +654 -0
  267. package/schemas/piagent-webui/attachment-v1.schema.json +267 -0
  268. package/schemas/piagent-webui/capabilities-v1.schema.json +729 -0
  269. package/schemas/piagent-webui/catalog-v1.json +240 -0
  270. package/schemas/piagent-webui/commit-summary-v1.schema.json +63 -0
  271. package/schemas/piagent-webui/common-v1.schema.json +479 -0
  272. package/schemas/piagent-webui/control-command-v1.schema.json +1780 -0
  273. package/schemas/piagent-webui/diff-v1.schema.json +826 -0
  274. package/schemas/piagent-webui/gateway-capabilities-v1.schema.json +136 -0
  275. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +456 -0
  276. package/schemas/piagent-webui/handoff-history-v1.schema.json +92 -0
  277. package/schemas/piagent-webui/model-catalog-v1.schema.json +95 -0
  278. package/schemas/piagent-webui/queue-v1.schema.json +99 -0
  279. package/schemas/piagent-webui/recovery-history-v1.schema.json +144 -0
  280. package/schemas/piagent-webui/release-monitor-v1.schema.json +173 -0
  281. package/schemas/piagent-webui/review-state-v1.schema.json +206 -0
  282. package/schemas/piagent-webui/runtime-event-v2.schema.json +5206 -0
  283. package/schemas/piagent-webui/session-catalog-v1.schema.json +222 -0
  284. package/schemas/piagent-webui/session-command-v1.schema.json +390 -0
  285. package/schemas/piagent-webui/snapshot-v1.schema.json +2622 -0
  286. package/schemas/piagent-webui/source-change-v1.schema.json +763 -0
  287. package/schemas/piagent-webui/source-mutation-v1.schema.json +96 -0
  288. package/schemas/piagent-webui/source-revert-v1.schema.json +130 -0
  289. package/schemas/piagent-webui/subagent-tree-v1.schema.json +88 -0
  290. package/schemas/piagent-webui/task-index-v1.schema.json +74 -0
  291. package/schemas/piagent-webui/task-timeline-v1.schema.json +49 -0
  292. package/schemas/piagent-webui/transcript-v1.schema.json +243 -0
  293. package/schemas/task-baseline-manifest.schema.json +122 -0
  294. package/schemas/verifier-file-snapshot.schema.json +81 -0
  295. package/scripts/check-architecture.mjs +1 -0
  296. package/scripts/check-doc-languages.mjs +19 -2
  297. package/scripts/check-third-party-neutrality.mjs +43 -0
  298. package/scripts/install-global.sh +1 -1
  299. package/scripts/piagent-cli.mjs +17 -3
  300. package/scripts/piagent-dashboard.mjs +208 -0
  301. package/scripts/piagent-webui-launcher.mjs +121 -0
  302. package/scripts/typescript-loader.mjs +26 -1
  303. package/scripts/verify-local.sh +253 -0
  304. package/scripts/verify-release-identity.mjs +4 -1
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+ import { spawn, spawnSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ import { requestGatewayControl } from "../packages/piagent-webui/gateway/control-socket.ts";
9
+ import { installedPiHostRoot } from "../packages/piagent-webui/gateway/pi-host.ts";
10
+ import { gatewayProfileState, profileRef, readGatewayDescriptor, readOrCreateCatalogKey,
11
+ removeGatewayDescriptor } from "../packages/piagent-webui/gateway/profile-state.ts";
12
+ import { startPiagentGateway } from "../packages/piagent-webui/gateway/gateway-service.ts";
13
+
14
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
15
+ const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
16
+ const expectedPiVersion = packageJson.peerDependencies["@earendil-works/pi-coding-agent"];
17
+ const args = process.argv.slice(2);
18
+ const command = ["--help", "-h"].includes(args[0]) ? args.shift()
19
+ : args[0] && !args[0].startsWith("-") ? args.shift() : "open";
20
+ const noOpen = args.includes("--no-open");
21
+ const json = args.includes("--json");
22
+ const repair = args.includes("--repair");
23
+ const agentDirIndex = args.indexOf("--agent-dir");
24
+ const agentDir = agentDirIndex >= 0 ? args[agentDirIndex + 1] : undefined;
25
+
26
+ function output(value) {
27
+ process.stdout.write(`${json ? JSON.stringify(value) : value}\n`);
28
+ }
29
+
30
+ function openBrowser(url) {
31
+ if (noOpen) return;
32
+ const opener = process.platform === "darwin" ? ["open", [url]]
33
+ : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]]
34
+ : ["xdg-open", [url]];
35
+ const result = spawnSync(opener[0], opener[1], { stdio: "ignore" });
36
+ if (result.status !== 0) process.stderr.write(`Dashboard is ready at ${url}\n`);
37
+ }
38
+
39
+ function processAlive(pid) {
40
+ try { process.kill(pid, 0); return true; }
41
+ catch (error) { return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH"); }
42
+ }
43
+
44
+ function ownerOnlyDirectory(target) {
45
+ try {
46
+ const stat = fs.lstatSync(target);
47
+ return stat.isDirectory() && !stat.isSymbolicLink() && (stat.mode & 0o077) === 0
48
+ && (typeof process.getuid !== "function" || stat.uid === process.getuid());
49
+ } catch { return false; }
50
+ }
51
+
52
+ function removeOwnedRegularFile(target) {
53
+ try {
54
+ const stat = fs.lstatSync(target);
55
+ if (!stat.isFile() || stat.isSymbolicLink()
56
+ || typeof process.getuid === "function" && stat.uid !== process.getuid()) return false;
57
+ fs.unlinkSync(target); return true;
58
+ } catch { return false; }
59
+ }
60
+
61
+ function removeOwnedSocket(target) {
62
+ if (process.platform === "win32") return false;
63
+ try {
64
+ const stat = fs.lstatSync(target);
65
+ if (!stat.isSocket() || stat.isSymbolicLink()
66
+ || typeof process.getuid === "function" && stat.uid !== process.getuid()) return false;
67
+ fs.unlinkSync(target); return true;
68
+ } catch { return false; }
69
+ }
70
+
71
+ async function control(action, timeoutMs = 1_500) {
72
+ const state = gatewayProfileState(agentDir);
73
+ return await requestGatewayControl(state.controlSocket, { action }, timeoutMs);
74
+ }
75
+
76
+ async function currentLaunchUrl() {
77
+ try {
78
+ const response = await control("issue-launch-url");
79
+ if (response.ok && response.value && typeof response.value === "object"
80
+ && typeof response.value.launchUrl === "string") return response.value.launchUrl;
81
+ } catch {
82
+ // Gateway is not ready yet.
83
+ }
84
+ return null;
85
+ }
86
+
87
+ async function ensureStarted() {
88
+ const existing = await currentLaunchUrl();
89
+ if (existing) return existing;
90
+ const effectiveAgentDir = path.resolve(agentDir ?? process.env.PI_CODING_AGENT_DIR ?? path.join(os.homedir(), ".pi", "agent"));
91
+ const child = spawn(process.execPath, [
92
+ "--disable-warning=ExperimentalWarning",
93
+ "--import", path.join(packageRoot, "scripts", "register-typescript-loader.mjs"),
94
+ fileURLToPath(import.meta.url),
95
+ "serve",
96
+ ...(agentDir ? ["--agent-dir", agentDir] : [])
97
+ ], { cwd: process.cwd(), env: { ...process.env,
98
+ PIAGENT_PINNED_TS_TRANSFORM_ROOT: path.join(effectiveAgentDir, "npm", "node_modules", "pi-mcp-adapter") },
99
+ detached: true, stdio: "ignore" });
100
+ child.unref();
101
+ const deadline = Date.now() + 12_000;
102
+ while (Date.now() < deadline) {
103
+ const url = await currentLaunchUrl();
104
+ if (url) return url;
105
+ await new Promise((resolve) => setTimeout(resolve, 80));
106
+ }
107
+ throw new Error("gateway-start-timeout");
108
+ }
109
+
110
+ async function serve() {
111
+ const gateway = await startPiagentGateway({ packageRoot, expectedPiVersion, agentDir });
112
+ const shutdown = () => { void gateway.close(); };
113
+ process.once("SIGINT", shutdown);
114
+ process.once("SIGTERM", shutdown);
115
+ await gateway.wait();
116
+ }
117
+
118
+ async function main() {
119
+ if (command === "serve") return await serve();
120
+ if (["help", "--help", "-h"].includes(command)) {
121
+ output("Usage: piagent dashboard [open|status|stop|restart|doctor] [--no-open] [--json] [--repair]");
122
+ return;
123
+ }
124
+ if (command === "open" || command === "start") {
125
+ const launchUrl = await ensureStarted();
126
+ output(json ? { state: "running", launchUrl } : launchUrl);
127
+ openBrowser(launchUrl);
128
+ return;
129
+ }
130
+ if (command === "status") {
131
+ try {
132
+ const response = await control("health");
133
+ if (!response.ok) throw new Error(response.error);
134
+ output(json ? { state: "running", gateway: response.value } : "Piagent Gateway is running.");
135
+ } catch {
136
+ const descriptor = readGatewayDescriptor(gatewayProfileState(agentDir));
137
+ output(json ? { state: "stopped", staleDescriptor: Boolean(descriptor) } : "Piagent Gateway is stopped.");
138
+ process.exitCode = 1;
139
+ }
140
+ return;
141
+ }
142
+ if (command === "stop") {
143
+ try {
144
+ const response = await control("stop");
145
+ if (!response.ok) throw new Error(response.error);
146
+ output(json ? { state: "stopping" } : "Piagent Gateway is stopping.");
147
+ } catch {
148
+ output(json ? { state: "stopped" } : "Piagent Gateway is already stopped.");
149
+ }
150
+ return;
151
+ }
152
+ if (command === "restart") {
153
+ try { await control("stop"); } catch { /* already stopped */ }
154
+ await new Promise((resolve) => setTimeout(resolve, 150));
155
+ const launchUrl = await ensureStarted();
156
+ output(json ? { state: "running", launchUrl } : launchUrl);
157
+ openBrowser(launchUrl);
158
+ return;
159
+ }
160
+ if (command === "doctor") {
161
+ const state = gatewayProfileState(agentDir);
162
+ const key = readOrCreateCatalogKey(state), descriptor = readGatewayDescriptor(state);
163
+ let actual = null;
164
+ try {
165
+ const hostRoot = installedPiHostRoot();
166
+ actual = JSON.parse(fs.readFileSync(path.join(hostRoot, "package.json"), "utf8")).version;
167
+ } catch { /* reported as a failed check below */ }
168
+ let health = null;
169
+ try {
170
+ const response = await control("health", 1_000);
171
+ health = response.ok ? "ready" : "unavailable";
172
+ } catch { health = "unavailable"; }
173
+ const staleDescriptor = Boolean(descriptor && !processAlive(descriptor.pid));
174
+ const invalidDescriptor = fs.existsSync(state.descriptorFile) && !descriptor;
175
+ const repairs = [];
176
+ if (repair && staleDescriptor && descriptor) {
177
+ removeGatewayDescriptor(state, descriptor.gatewayInstanceRef); repairs.push("stale-descriptor-removed");
178
+ } else if (repair && invalidDescriptor && removeOwnedRegularFile(state.descriptorFile)) repairs.push("invalid-descriptor-removed");
179
+ if (repair && health !== "ready" && removeOwnedSocket(state.controlSocket)) repairs.push("stale-control-socket-removed");
180
+ const checks = {
181
+ piHostVersion: actual === expectedPiVersion,
182
+ clientBuild: fs.existsSync(path.join(packageRoot, "packages", "piagent-webui", "dist", "client", "index.html")),
183
+ profileStateOwnerOnly: ownerOnlyDirectory(state.root),
184
+ descriptor: !invalidDescriptor && !staleDescriptor,
185
+ gatewayHealth: descriptor ? health === "ready" : true
186
+ };
187
+ const result = {
188
+ ok: Object.values(checks).every(Boolean),
189
+ expectedPiVersion,
190
+ actualPiVersion: actual,
191
+ profileRef: profileRef(state, key),
192
+ gateway: descriptor ? health === "ready" ? "ready" : staleDescriptor ? "stale" : "unavailable" : "not-registered",
193
+ checks,
194
+ repaired: repairs
195
+ };
196
+ output(json ? result : result.ok ? "Piagent Gateway doctor passed." : repair && repairs.length
197
+ ? "Piagent Gateway doctor repaired stale local state; run doctor again."
198
+ : "Piagent Gateway doctor found a version, build, ownership, or lifecycle mismatch.");
199
+ if (!result.ok) process.exitCode = 1;
200
+ return;
201
+ }
202
+ throw new Error(`unknown-dashboard-command:${command}`);
203
+ }
204
+
205
+ main().catch((error) => {
206
+ process.stderr.write(`Piagent dashboard failed: ${error instanceof Error ? error.message : String(error)}\n`);
207
+ process.exitCode = 1;
208
+ });
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import net from "node:net";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+
8
+ const REF = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,159}$/;
9
+
10
+ function help() {
11
+ return [
12
+ "Usage: piagent-webui [--session <opaque-session-ref>] [--no-open]",
13
+ "",
14
+ "Opens a WebUI already owned by the current Pi process.",
15
+ "Start it first with /piagent-webui inside the Pi terminal.",
16
+ "This command never creates a second Pi runtime."
17
+ ].join("\n");
18
+ }
19
+
20
+ function parse(argv) {
21
+ let session = null, open = true;
22
+ for (let index = 0; index < argv.length; index += 1) {
23
+ const value = argv[index];
24
+ if (value === "--help" || value === "-h") return { help: true, session, open };
25
+ if (value === "--no-open") { open = false; continue; }
26
+ if (value === "--session" && typeof argv[index + 1] === "string" && REF.test(argv[index + 1])) { session = argv[index += 1]; continue; }
27
+ throw new Error(`Unknown or invalid option: ${value}`);
28
+ }
29
+ return { help: false, session, open };
30
+ }
31
+
32
+ function launcherDirectory(start) {
33
+ let current = path.resolve(start);
34
+ while (true) {
35
+ const candidate = path.join(current, ".pi", "piagent-state", "webui-launcher");
36
+ try { if (fs.lstatSync(candidate).isDirectory()) return candidate; } catch { /* keep walking */ }
37
+ const parent = path.dirname(current);
38
+ if (parent === current) return null;
39
+ current = parent;
40
+ }
41
+ }
42
+
43
+ function readDescriptor(file) {
44
+ const stat = fs.lstatSync(file);
45
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 8_192 || process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
46
+ throw new Error("WebUI launcher descriptor is unsafe");
47
+ }
48
+ const value = JSON.parse(fs.readFileSync(file, "utf8"));
49
+ const temporary = fs.realpathSync(os.tmpdir());
50
+ const socket = path.resolve(String(value.controlSocket ?? ""));
51
+ const canonicalSocket = fs.realpathSync(socket);
52
+ if (value.schemaVersion !== 1 || !REF.test(String(value.sessionRef ?? "")) || !REF.test(String(value.projectRef ?? ""))
53
+ || typeof value.controlToken !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(value.controlToken)
54
+ || path.relative(temporary, canonicalSocket).startsWith("..") || path.isAbsolute(path.relative(temporary, canonicalSocket))) {
55
+ throw new Error("WebUI launcher descriptor is invalid");
56
+ }
57
+ return { ...value, controlSocket: socket };
58
+ }
59
+
60
+ function requestLaunch(descriptor) {
61
+ return new Promise((resolve, reject) => {
62
+ const socket = net.createConnection(descriptor.controlSocket);
63
+ let bytes = Buffer.alloc(0), settled = false;
64
+ const fail = (error) => { if (!settled) { settled = true; socket.destroy(); reject(error); } };
65
+ socket.setTimeout(2_000, () => fail(new Error("WebUI sidecar did not respond")));
66
+ socket.once("error", () => fail(new Error("WebUI sidecar is not running; use /piagent-webui in Pi")));
67
+ socket.once("connect", () => socket.write(`${JSON.stringify({ action: "launch", token: descriptor.controlToken })}\n`));
68
+ socket.on("data", (chunk) => {
69
+ if (settled) return;
70
+ bytes = Buffer.concat([bytes, chunk]);
71
+ if (bytes.length > 8_192) { fail(new Error("WebUI sidecar response exceeded its limit")); return; }
72
+ const newline = bytes.indexOf(10);
73
+ if (newline < 0) return;
74
+ try {
75
+ const response = JSON.parse(bytes.subarray(0, newline).toString("utf8"));
76
+ if (!response.ok || typeof response.launchUrl !== "string" || !/^http:\/\/127\.0\.0\.1:\d+\/#bootstrap=/.test(response.launchUrl)) {
77
+ fail(new Error("WebUI sidecar rejected the launch request")); return;
78
+ }
79
+ settled = true; socket.end(); resolve(response.launchUrl);
80
+ } catch { fail(new Error("WebUI sidecar returned an invalid response")); }
81
+ });
82
+ });
83
+ }
84
+
85
+ function openUrl(url) {
86
+ const command = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
87
+ if (!command) return false;
88
+ const child = spawn(command, [url], { detached: true, stdio: "ignore" });
89
+ child.unref(); return true;
90
+ }
91
+
92
+ async function main(argv = process.argv.slice(2)) {
93
+ let options;
94
+ try { options = parse(argv); }
95
+ catch (error) { process.stderr.write(`${error.message}\n${help()}\n`); return 2; }
96
+ if (options.help) { process.stdout.write(`${help()}\n`); return 0; }
97
+ const directory = launcherDirectory(process.cwd());
98
+ if (!directory) { process.stderr.write("No running Piagent WebUI was found. Run /piagent-webui inside the Pi terminal first.\n"); return 1; }
99
+ let descriptors;
100
+ try {
101
+ descriptors = fs.readdirSync(directory).filter((name) => /^[a-f0-9]{64}\.json$/.test(name))
102
+ .map((name) => readDescriptor(path.join(directory, name)))
103
+ .filter((value) => !options.session || value.sessionRef === options.session);
104
+ } catch (error) { process.stderr.write(`${error instanceof Error ? error.message : "WebUI launcher state is unavailable"}\n`); return 1; }
105
+ if (descriptors.length !== 1) {
106
+ process.stderr.write(descriptors.length === 0
107
+ ? "No matching running WebUI session was found.\n"
108
+ : "More than one WebUI session is running; pass --session <opaque-session-ref>.\n");
109
+ return 1;
110
+ }
111
+ try {
112
+ const url = await requestLaunch(descriptors[0]);
113
+ if (!options.open || !openUrl(url)) process.stdout.write(`${url}\n`);
114
+ else process.stdout.write(`Opened Piagent WebUI for ${descriptors[0].sessionRef}.\n`);
115
+ return 0;
116
+ } catch (error) { process.stderr.write(`${error instanceof Error ? error.message : "WebUI could not be opened"}\n`); return 1; }
117
+ }
118
+
119
+ if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(new URL(import.meta.url))) process.exit(await main());
120
+
121
+ export { help, main, parse, readDescriptor, requestLaunch };
@@ -1,12 +1,37 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { stripTypeScriptTypes } from "node:module";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ // Repository TypeScript must remain erasable: the integrity lock hashes the
7
+ // source bytes and `strip` fails closed if a future change needs codegen.
8
+ // pi-mcp-adapter@2.15.0 is the sole exception because its published OAuth
9
+ // implementation contains parameter properties. Keep that transform isolated
10
+ // to the exact installed adapter root and Node release reviewed below. A Node
11
+ // or adapter upgrade must deliberately update these pins before OAuth loads.
12
+ export const PINNED_EXTERNAL_TRANSFORM = Object.freeze({
13
+ nodeVersion: "24.11.1",
14
+ packageName: "pi-mcp-adapter",
15
+ packageVersion: "2.15.0"
16
+ });
17
+
18
+ async function pinnedTransformRoot(url) {
19
+ const configured = process.env.PIAGENT_PINNED_TS_TRANSFORM_ROOT;
20
+ if (!configured || process.versions.node !== PINNED_EXTERNAL_TRANSFORM.nodeVersion) return false;
21
+ const root = await fs.realpath(configured), target = await fs.realpath(fileURLToPath(url));
22
+ const relative = path.relative(root, target);
23
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return false;
24
+ const metadata = JSON.parse(await fs.readFile(path.join(root, "package.json"), "utf8"));
25
+ return metadata.name === PINNED_EXTERNAL_TRANSFORM.packageName && metadata.version === PINNED_EXTERNAL_TRANSFORM.packageVersion;
26
+ }
3
27
 
4
28
  export async function load(url, context, nextLoad) {
5
29
  if (!url.endsWith(".ts")) return nextLoad(url, context);
6
30
  const source = await fs.readFile(new URL(url), "utf8");
31
+ const mode = await pinnedTransformRoot(url) ? "transform" : "strip";
7
32
  return {
8
33
  format: "module",
9
34
  shortCircuit: true,
10
- source: stripTypeScriptTypes(source, { mode: "strip", sourceUrl: url })
35
+ source: stripTypeScriptTypes(source, { mode, sourceUrl: url })
11
36
  };
12
37
  }