@pasko70/pibo 1.1.0 → 1.3.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 (439) hide show
  1. package/README.md +106 -254
  2. package/context/codex-base-prompt.md +4 -0
  3. package/context/compute-worker.md +23 -0
  4. package/context/pibo-native-tooling.md +18 -0
  5. package/context/pibo-system-prompt.md +69 -8
  6. package/dist/api/simple-agent-api.js +136 -0
  7. package/dist/app-context.js +4 -0
  8. package/dist/apps/chat/agent-profiles.js +18 -3
  9. package/dist/apps/chat/agent-store.js +113 -16
  10. package/dist/apps/chat/chat-api-routes.js +311 -0
  11. package/dist/apps/chat/chat-capability-routes.js +90 -0
  12. package/dist/apps/chat/chat-files.js +178 -0
  13. package/dist/apps/chat/chat-request-normalizers.js +681 -0
  14. package/dist/apps/chat/chat-settings-routes.js +75 -0
  15. package/dist/apps/chat/chat-trace-helpers.js +94 -0
  16. package/dist/apps/chat/chat-user-skill-routes.js +149 -0
  17. package/dist/apps/chat/cron-api.js +213 -0
  18. package/dist/apps/chat/data/chat-data-mappers.js +74 -0
  19. package/dist/apps/chat/data/event-command-service.js +75 -0
  20. package/dist/apps/chat/data/navigation-query-service.js +11 -0
  21. package/dist/apps/chat/data/project-service.js +844 -0
  22. package/dist/apps/chat/data/read-state-service.js +42 -0
  23. package/dist/apps/chat/data/room-service.js +52 -0
  24. package/dist/apps/chat/data/session-query-service.js +63 -0
  25. package/dist/apps/chat/data/timeline-query-service.js +96 -0
  26. package/dist/apps/chat/event-log.js +1 -307
  27. package/dist/apps/chat/model-catalog.js +7 -2
  28. package/dist/apps/chat/output-compactor.js +126 -0
  29. package/dist/apps/chat/output-event-policy.js +17 -0
  30. package/dist/apps/chat/project-workflow-human-actions.js +347 -0
  31. package/dist/apps/chat/project-workflow-sessions.js +260 -0
  32. package/dist/apps/chat/provider-auth-actions.js +43 -0
  33. package/dist/apps/chat/ralph-api.js +156 -0
  34. package/dist/apps/chat/read-model.js +1 -218
  35. package/dist/apps/chat/rooms.js +1 -321
  36. package/dist/apps/chat/static-assets.js +996 -0
  37. package/dist/apps/chat/stream.js +29 -6
  38. package/dist/apps/chat/trace.js +179 -1003
  39. package/dist/apps/chat/types/event-store.js +1 -0
  40. package/dist/apps/chat/types/read-model.js +1 -0
  41. package/dist/apps/chat/types/rooms.js +36 -0
  42. package/dist/apps/chat/web-app.js +3131 -1959
  43. package/dist/apps/chat/workflow-catalog.js +572 -0
  44. package/dist/apps/chat/workflow-edge-compatibility.js +51 -0
  45. package/dist/apps/chat/workflow-json-schema-validation.js +335 -0
  46. package/dist/apps/chat/workflow-persistence-model.js +140 -0
  47. package/dist/apps/chat/workflow-persistence.js +633 -0
  48. package/dist/apps/chat/workflow-registered-ref-params.js +130 -0
  49. package/dist/apps/chat/workflow-registered-ref-pickers.js +179 -0
  50. package/dist/apps/chat/workflow-registered-ref-validation.js +210 -0
  51. package/dist/apps/chat/workflow-v2-security-validation.js +93 -0
  52. package/dist/apps/chat/workflow-validation-helpers.js +114 -0
  53. package/dist/apps/chat-ui/assets/{dist-CNLWH2qh.js → dist-B4lw9z4F.js} +1 -1
  54. package/dist/apps/chat-ui/assets/{dist-8e4L8ciw.js → dist-BbTaHWON.js} +1 -1
  55. package/dist/apps/chat-ui/assets/{dist-CMZOa5ik.js → dist-BcT_c1EJ.js} +1 -1
  56. package/dist/apps/chat-ui/assets/{dist-DNcnQRPF.js → dist-CGINd7XU.js} +1 -1
  57. package/dist/apps/chat-ui/assets/{dist-CqqUC8Ce.js → dist-CrBzZkUp.js} +1 -1
  58. package/dist/apps/chat-ui/assets/{dist-D2N4-LIc.js → dist-CyyISotB.js} +1 -1
  59. package/dist/apps/chat-ui/assets/{dist-CUqpST5w.js → dist-DFbnFNpX.js} +1 -1
  60. package/dist/apps/chat-ui/assets/{dist-CspjOeBj.js → dist-DGLWQLX8.js} +1 -1
  61. package/dist/apps/chat-ui/assets/{dist-B7bbI3Qz.js → dist-DfcIkFSN.js} +1 -1
  62. package/dist/apps/chat-ui/assets/{dist-v4pjHlB7.js → dist-sKDHTwaT.js} +1 -1
  63. package/dist/apps/chat-ui/assets/{dist-D9L4MSRF.js → dist-y9D6la48.js} +1 -1
  64. package/dist/apps/chat-ui/assets/index-C25VYnyb.css +1 -0
  65. package/dist/apps/chat-ui/assets/index-CUkuvI3v.js +165 -0
  66. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-144x144.png +0 -0
  67. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-192x192.png +0 -0
  68. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-48x48.png +0 -0
  69. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-512x512.png +0 -0
  70. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-72x72.png +0 -0
  71. package/dist/apps/chat-ui/assets/pwa-images/android/launchericon-96x96.png +0 -0
  72. package/dist/apps/chat-ui/assets/pwa-images/ios/100.png +0 -0
  73. package/dist/apps/chat-ui/assets/pwa-images/ios/1024.png +0 -0
  74. package/dist/apps/chat-ui/assets/pwa-images/ios/114.png +0 -0
  75. package/dist/apps/chat-ui/assets/pwa-images/ios/120.png +0 -0
  76. package/dist/apps/chat-ui/assets/pwa-images/ios/128.png +0 -0
  77. package/dist/apps/chat-ui/assets/pwa-images/ios/144.png +0 -0
  78. package/dist/apps/chat-ui/assets/pwa-images/ios/152.png +0 -0
  79. package/dist/apps/chat-ui/assets/pwa-images/ios/16.png +0 -0
  80. package/dist/apps/chat-ui/assets/pwa-images/ios/167.png +0 -0
  81. package/dist/apps/chat-ui/assets/pwa-images/ios/180.png +0 -0
  82. package/dist/apps/chat-ui/assets/pwa-images/ios/192.png +0 -0
  83. package/dist/apps/chat-ui/assets/pwa-images/ios/20.png +0 -0
  84. package/dist/apps/chat-ui/assets/pwa-images/ios/256.png +0 -0
  85. package/dist/apps/chat-ui/assets/pwa-images/ios/29.png +0 -0
  86. package/dist/apps/chat-ui/assets/pwa-images/ios/32.png +0 -0
  87. package/dist/apps/chat-ui/assets/pwa-images/ios/40.png +0 -0
  88. package/dist/apps/chat-ui/assets/pwa-images/ios/50.png +0 -0
  89. package/dist/apps/chat-ui/assets/pwa-images/ios/512.png +0 -0
  90. package/dist/apps/chat-ui/assets/pwa-images/ios/57.png +0 -0
  91. package/dist/apps/chat-ui/assets/pwa-images/ios/58.png +0 -0
  92. package/dist/apps/chat-ui/assets/pwa-images/ios/60.png +0 -0
  93. package/dist/apps/chat-ui/assets/pwa-images/ios/64.png +0 -0
  94. package/dist/apps/chat-ui/assets/pwa-images/ios/72.png +0 -0
  95. package/dist/apps/chat-ui/assets/pwa-images/ios/76.png +0 -0
  96. package/dist/apps/chat-ui/assets/pwa-images/ios/80.png +0 -0
  97. package/dist/apps/chat-ui/assets/pwa-images/ios/87.png +0 -0
  98. package/dist/apps/chat-ui/assets/pwa-images/windows/LargeTile.scale-100.png +0 -0
  99. package/dist/apps/chat-ui/assets/pwa-images/windows/LargeTile.scale-125.png +0 -0
  100. package/dist/apps/chat-ui/assets/pwa-images/windows/LargeTile.scale-150.png +0 -0
  101. package/dist/apps/chat-ui/assets/pwa-images/windows/LargeTile.scale-200.png +0 -0
  102. package/dist/apps/chat-ui/assets/pwa-images/windows/LargeTile.scale-400.png +0 -0
  103. package/dist/apps/chat-ui/assets/pwa-images/windows/SmallTile.scale-100.png +0 -0
  104. package/dist/apps/chat-ui/assets/pwa-images/windows/SmallTile.scale-125.png +0 -0
  105. package/dist/apps/chat-ui/assets/pwa-images/windows/SmallTile.scale-150.png +0 -0
  106. package/dist/apps/chat-ui/assets/pwa-images/windows/SmallTile.scale-200.png +0 -0
  107. package/dist/apps/chat-ui/assets/pwa-images/windows/SmallTile.scale-400.png +0 -0
  108. package/dist/apps/chat-ui/assets/pwa-images/windows/SplashScreen.scale-100.png +0 -0
  109. package/dist/apps/chat-ui/assets/pwa-images/windows/SplashScreen.scale-125.png +0 -0
  110. package/dist/apps/chat-ui/assets/pwa-images/windows/SplashScreen.scale-150.png +0 -0
  111. package/dist/apps/chat-ui/assets/pwa-images/windows/SplashScreen.scale-200.png +0 -0
  112. package/dist/apps/chat-ui/assets/pwa-images/windows/SplashScreen.scale-400.png +0 -0
  113. package/dist/apps/chat-ui/assets/pwa-images/windows/Square150x150Logo.scale-100.png +0 -0
  114. package/dist/apps/chat-ui/assets/pwa-images/windows/Square150x150Logo.scale-125.png +0 -0
  115. package/dist/apps/chat-ui/assets/pwa-images/windows/Square150x150Logo.scale-150.png +0 -0
  116. package/dist/apps/chat-ui/assets/pwa-images/windows/Square150x150Logo.scale-200.png +0 -0
  117. package/dist/apps/chat-ui/assets/pwa-images/windows/Square150x150Logo.scale-400.png +0 -0
  118. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-16.png +0 -0
  119. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-20.png +0 -0
  120. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-24.png +0 -0
  121. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-256.png +0 -0
  122. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-30.png +0 -0
  123. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-32.png +0 -0
  124. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-36.png +0 -0
  125. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-40.png +0 -0
  126. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-44.png +0 -0
  127. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-48.png +0 -0
  128. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-60.png +0 -0
  129. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-64.png +0 -0
  130. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-72.png +0 -0
  131. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-80.png +0 -0
  132. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-lightunplated_targetsize-96.png +0 -0
  133. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-16.png +0 -0
  134. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-20.png +0 -0
  135. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-24.png +0 -0
  136. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-256.png +0 -0
  137. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-30.png +0 -0
  138. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-32.png +0 -0
  139. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-36.png +0 -0
  140. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-40.png +0 -0
  141. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-44.png +0 -0
  142. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-48.png +0 -0
  143. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-60.png +0 -0
  144. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-64.png +0 -0
  145. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-72.png +0 -0
  146. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-80.png +0 -0
  147. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.altform-unplated_targetsize-96.png +0 -0
  148. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.scale-100.png +0 -0
  149. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.scale-125.png +0 -0
  150. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.scale-150.png +0 -0
  151. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.scale-200.png +0 -0
  152. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.scale-400.png +0 -0
  153. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-16.png +0 -0
  154. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-20.png +0 -0
  155. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-24.png +0 -0
  156. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-256.png +0 -0
  157. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-30.png +0 -0
  158. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-32.png +0 -0
  159. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-36.png +0 -0
  160. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-40.png +0 -0
  161. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-44.png +0 -0
  162. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-48.png +0 -0
  163. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-60.png +0 -0
  164. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-64.png +0 -0
  165. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-72.png +0 -0
  166. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-80.png +0 -0
  167. package/dist/apps/chat-ui/assets/pwa-images/windows/Square44x44Logo.targetsize-96.png +0 -0
  168. package/dist/apps/chat-ui/assets/pwa-images/windows/StoreLogo.scale-100.png +0 -0
  169. package/dist/apps/chat-ui/assets/pwa-images/windows/StoreLogo.scale-125.png +0 -0
  170. package/dist/apps/chat-ui/assets/pwa-images/windows/StoreLogo.scale-150.png +0 -0
  171. package/dist/apps/chat-ui/assets/pwa-images/windows/StoreLogo.scale-200.png +0 -0
  172. package/dist/apps/chat-ui/assets/pwa-images/windows/StoreLogo.scale-400.png +0 -0
  173. package/dist/apps/chat-ui/assets/pwa-images/windows/Wide310x150Logo.scale-100.png +0 -0
  174. package/dist/apps/chat-ui/assets/pwa-images/windows/Wide310x150Logo.scale-125.png +0 -0
  175. package/dist/apps/chat-ui/assets/pwa-images/windows/Wide310x150Logo.scale-150.png +0 -0
  176. package/dist/apps/chat-ui/assets/pwa-images/windows/Wide310x150Logo.scale-200.png +0 -0
  177. package/dist/apps/chat-ui/assets/pwa-images/windows/Wide310x150Logo.scale-400.png +0 -0
  178. package/dist/apps/chat-ui/index.html +8 -3
  179. package/dist/apps/chat-ui/manifest.webmanifest +25 -0
  180. package/dist/apps/chat-ui/sw.js +41 -0
  181. package/dist/apps/chat-vscode-web/assets/index-B5QK07zO.css +2 -0
  182. package/dist/apps/chat-vscode-web/assets/index-C3GTPyDo.js +41 -0
  183. package/dist/apps/chat-vscode-web/index.html +14 -0
  184. package/dist/apps/cli-ui/InkSessionApp.js +1218 -0
  185. package/dist/apps/cli-ui/InkTerminalLine.js +53 -0
  186. package/dist/apps/cli-ui/InkTerminalRow.js +304 -0
  187. package/dist/apps/cli-ui/InkTerminalView.js +25 -0
  188. package/dist/apps/cli-ui/cliSessionsCommand.js +232 -0
  189. package/dist/apps/cli-ui/index.js +8 -0
  190. package/dist/apps/cli-ui/inkColors.js +67 -0
  191. package/dist/apps/cli-ui/inkJson.js +244 -0
  192. package/dist/apps/cli-ui/inkMarkdown.js +122 -0
  193. package/dist/apps/context-files-ui/assets/{dist-DFi2YGux.js → dist-BOiGdrG-.js} +1 -1
  194. package/dist/apps/context-files-ui/assets/{dist-DyfSeBMu.js → dist-DK01b7x4.js} +1 -1
  195. package/dist/apps/context-files-ui/assets/{dist-CwbH_3RN.js → dist-DPG9jOQ-.js} +1 -1
  196. package/dist/apps/context-files-ui/assets/{dist-dTBsFYAl.js → dist-Dp5AH00p.js} +1 -1
  197. package/dist/apps/context-files-ui/assets/{dist-CxRLRmMx.js → dist-DqYw0LH7.js} +1 -1
  198. package/dist/apps/context-files-ui/assets/{dist-MyZgX9K2.js → dist-TF5iO5ML.js} +1 -1
  199. package/dist/apps/context-files-ui/assets/{dist-DQXzt2Ng.js → dist-TbjAhles.js} +1 -1
  200. package/dist/apps/context-files-ui/assets/{dist-YwR9dpb-.js → dist-ZsBbt0uY.js} +1 -1
  201. package/dist/apps/context-files-ui/assets/{dist-CKQF1zEu.js → dist-kmsvtHkH.js} +1 -1
  202. package/dist/apps/context-files-ui/assets/{dist-BJW3917N.js → dist-vnd7PMu3.js} +1 -1
  203. package/dist/apps/context-files-ui/assets/{index-BwRDoHMC.css → index-B92tL3Pp.css} +1 -1
  204. package/dist/apps/context-files-ui/assets/{index-GwvjXytt.js → index-BhAWD3dv.js} +2 -2
  205. package/dist/apps/context-files-ui/index.html +2 -2
  206. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  207. package/dist/apps/vscode-artifacts/pibo-vscode-1.3.0.vsix +0 -0
  208. package/dist/auth/better-auth.js +2 -1
  209. package/dist/auth/login-actions.js +251 -0
  210. package/dist/auth/openai-codex-usage.js +147 -0
  211. package/dist/bin/pibo.js +0 -0
  212. package/dist/bin/rg.js +15 -0
  213. package/dist/cli-session/fakeSessionSource.js +804 -0
  214. package/dist/cli-session/index.js +3 -0
  215. package/dist/cli-session/localSessionSource.js +1144 -0
  216. package/dist/cli-session/sessionSource.js +13 -0
  217. package/dist/cli.js +159 -14
  218. package/dist/compute/cli.js +415 -0
  219. package/dist/compute/docker.js +827 -0
  220. package/dist/compute/resource-health.js +333 -0
  221. package/dist/compute/resource-policy.js +98 -0
  222. package/dist/config/config.js +9 -5
  223. package/dist/core/context-build.js +677 -0
  224. package/dist/core/model-defaults.js +51 -9
  225. package/dist/core/pibo-home.js +8 -0
  226. package/dist/core/profiles.js +48 -0
  227. package/dist/core/provider-telemetry.js +222 -0
  228. package/dist/core/routed-session.js +441 -47
  229. package/dist/core/runtime-telemetry.js +853 -0
  230. package/dist/core/runtime.js +103 -15
  231. package/dist/core/session-errors.js +47 -0
  232. package/dist/core/session-model.js +39 -0
  233. package/dist/core/session-router.js +292 -62
  234. package/dist/core/shared-app.js +17 -0
  235. package/dist/core/skill-expansion.js +43 -0
  236. package/dist/core/system-prompt-template.js +9 -9
  237. package/dist/core/telemetry-staleness.js +128 -0
  238. package/dist/core/test-output-compaction.js +145 -0
  239. package/dist/core/user-settings.js +73 -0
  240. package/dist/cron/channel.js +28 -0
  241. package/dist/cron/cli.js +230 -0
  242. package/dist/cron/plugin.js +11 -0
  243. package/dist/cron/schedule.js +214 -0
  244. package/dist/cron/service.js +177 -0
  245. package/dist/cron/store.js +349 -0
  246. package/dist/cron/types.js +1 -0
  247. package/dist/data/cli.js +184 -0
  248. package/dist/data/event-log.js +106 -0
  249. package/dist/data/final-app-space-cutover-migration.js +728 -0
  250. package/dist/data/ingest-service.js +389 -0
  251. package/dist/data/message-store.js +62 -0
  252. package/dist/data/navigation-store.js +75 -0
  253. package/dist/data/observation-store.js +46 -0
  254. package/dist/data/payload-store.js +144 -0
  255. package/dist/data/pibo-store.js +59 -0
  256. package/dist/data/schema.js +434 -0
  257. package/dist/data/session-store.js +63 -0
  258. package/dist/data/shared-app-migration.js +757 -0
  259. package/dist/data/sqlite-schema.js +12 -0
  260. package/dist/data/telemetry-preview.js +65 -0
  261. package/dist/data/telemetry-queries.js +212 -0
  262. package/dist/data/telemetry-retention.js +50 -0
  263. package/dist/data/telemetry-rows.js +158 -0
  264. package/dist/data/telemetry.js +357 -0
  265. package/dist/debug/delta-compaction.js +99 -0
  266. package/dist/debug/detail-format.js +86 -0
  267. package/dist/debug/events.js +149 -29
  268. package/dist/debug/failures.js +162 -0
  269. package/dist/debug/index.js +655 -30
  270. package/dist/debug/messages.js +185 -0
  271. package/dist/debug/next-commands.js +6 -0
  272. package/dist/debug/payloads.js +123 -0
  273. package/dist/debug/pty.js +924 -0
  274. package/dist/debug/session.js +45 -15
  275. package/dist/debug/sql.js +1 -1
  276. package/dist/debug/stores.js +18 -13
  277. package/dist/debug/summary.js +72 -0
  278. package/dist/debug/telemetry.js +649 -0
  279. package/dist/debug/tools.js +168 -0
  280. package/dist/debug/trace-render-check.js +151 -0
  281. package/dist/debug/trace.js +152 -27
  282. package/dist/debug/web-artifacts.js +63 -0
  283. package/dist/debug/web-options.js +313 -0
  284. package/dist/debug/web-render-analysis.js +310 -0
  285. package/dist/debug/web-snapshot-browser-scripts.js +303 -0
  286. package/dist/debug/web-streaming-benchmark-analysis.js +605 -0
  287. package/dist/debug/web-streaming-browser-library.js +928 -0
  288. package/dist/debug/web-streaming-browser-scripts.js +242 -0
  289. package/dist/debug/web-streaming-provider-telemetry.js +194 -0
  290. package/dist/debug/web-streaming-report.js +493 -0
  291. package/dist/debug/web-streaming-types.js +1 -0
  292. package/dist/debug/web.js +574 -0
  293. package/dist/gateway/backup.js +83 -0
  294. package/dist/gateway/cli.js +511 -0
  295. package/dist/gateway/fallback.js +155 -0
  296. package/dist/gateway/pidfile.js +84 -0
  297. package/dist/gateway/protocol.js +16 -0
  298. package/dist/gateway/server.js +172 -17
  299. package/dist/gateway/tool.js +54 -51
  300. package/dist/gateway/web.js +74 -2
  301. package/dist/index.js +3 -1
  302. package/dist/local/client.js +3 -3
  303. package/dist/local/extension.js +8 -5
  304. package/dist/plugins/builtin.js +251 -49
  305. package/dist/plugins/chat-vscode-web.js +28 -0
  306. package/dist/plugins/codex-compat.js +0 -41
  307. package/dist/plugins/context-files.js +98 -18
  308. package/dist/plugins/dev-auth.js +124 -0
  309. package/dist/plugins/native-tooling.js +15 -0
  310. package/dist/plugins/registry.js +49 -1
  311. package/dist/plugins/web-annotations.js +27 -0
  312. package/dist/providers/minimax.js +70 -0
  313. package/dist/providers/openai-compat.js +48 -0
  314. package/dist/ralph/channel.js +8 -0
  315. package/dist/ralph/cli.js +152 -0
  316. package/dist/ralph/plugin.js +14 -0
  317. package/dist/ralph/service.js +271 -0
  318. package/dist/ralph/stopping.js +151 -0
  319. package/dist/ralph/store.js +450 -0
  320. package/dist/ralph/templates.js +200 -0
  321. package/dist/ralph/types.js +1 -0
  322. package/dist/reliability/store.js +42 -26
  323. package/dist/runs/registry.js +72 -35
  324. package/dist/session-ui/commandCatalog.js +193 -0
  325. package/dist/session-ui/commandResults.js +95 -0
  326. package/dist/session-ui/index.js +7 -0
  327. package/dist/session-ui/ownerViewModel.js +27 -0
  328. package/dist/session-ui/roomSessionViewModel.js +69 -0
  329. package/dist/session-ui/statusViewModel.js +127 -0
  330. package/dist/session-ui/terminalCards.js +210 -0
  331. package/dist/session-ui/terminalRows.js +937 -0
  332. package/dist/session-ui/terminalValue.js +45 -0
  333. package/dist/sessions/pibo-data-store.js +178 -0
  334. package/dist/sessions/sqlite-store.js +78 -12
  335. package/dist/sessions/store.js +11 -4
  336. package/dist/sessions/workflow-session-kind.js +20 -0
  337. package/dist/setup/cli.js +567 -0
  338. package/dist/shared/trace-async-agent-runs.js +127 -0
  339. package/dist/shared/trace-engine.js +96 -0
  340. package/dist/shared/trace-event-projection.js +679 -0
  341. package/dist/shared/trace-live-reducer.js +191 -0
  342. package/dist/shared/trace-nodes.js +76 -0
  343. package/dist/shared/trace-order.js +11 -10
  344. package/dist/shared/trace-patch-nodes.js +83 -0
  345. package/dist/shared/trace-run-notifications.js +69 -0
  346. package/dist/shared/trace-subagent-links.js +46 -0
  347. package/dist/shared/trace-transcript.js +303 -0
  348. package/dist/shared/trace-types.js +1 -0
  349. package/dist/shared-app.js +4 -0
  350. package/dist/signals/aggregate.js +65 -0
  351. package/dist/signals/events.js +1 -0
  352. package/dist/signals/projector.js +158 -0
  353. package/dist/signals/registry.js +513 -0
  354. package/dist/signals/store.js +1 -0
  355. package/dist/signals/types.js +1 -0
  356. package/dist/skills/cli.js +117 -0
  357. package/dist/tools/agent-browser-leases.js +219 -0
  358. package/dist/tools/agent-browser-wrapper.js +99 -0
  359. package/dist/tools/browser-pool.js +623 -0
  360. package/dist/tools/browser-use-cdp.js +56 -4
  361. package/dist/tools/browser-use-leases.js +215 -37
  362. package/dist/tools/browser-use-wrapper.js +508 -65
  363. package/dist/tools/cdp-client.js +189 -0
  364. package/dist/tools/guides.js +203 -4
  365. package/dist/tools/index.js +666 -16
  366. package/dist/tools/npm-runtime.js +136 -0
  367. package/dist/tools/registry.js +107 -8
  368. package/dist/tools/runtime/index.js +3 -0
  369. package/dist/tools/runtime/node-backend.js +225 -0
  370. package/dist/tools/runtime/node-worker-source.js +206 -0
  371. package/dist/tools/runtime/python-backend.js +227 -0
  372. package/dist/tools/runtime/python-worker-source.js +178 -0
  373. package/dist/tools/runtime/registry.js +255 -0
  374. package/dist/tools/runtime/tool.js +168 -0
  375. package/dist/tools/runtime/types.js +1 -0
  376. package/dist/tools/web-search.js +2 -1
  377. package/dist/user-skills/installer.js +39 -36
  378. package/dist/user-skills/store.js +50 -11
  379. package/dist/vscode/cli.js +90 -0
  380. package/dist/vscode/code-cli.js +119 -0
  381. package/dist/vscode/install.js +177 -0
  382. package/dist/vscode/status.js +80 -0
  383. package/dist/vscode/types.js +14 -0
  384. package/dist/vscode/uninstall.js +49 -0
  385. package/dist/vscode/vsix-fetcher.js +131 -0
  386. package/dist/web/auth.js +5 -1
  387. package/dist/web/channel.js +86 -0
  388. package/dist/web/http.js +79 -14
  389. package/dist/web-annotations/api.js +348 -0
  390. package/dist/web-annotations/attachments.js +201 -0
  391. package/dist/web-annotations/cdp.js +1107 -0
  392. package/dist/web-annotations/index.js +7 -0
  393. package/dist/web-annotations/store.js +407 -0
  394. package/dist/web-annotations/tools.js +350 -0
  395. package/dist/web-annotations/types.js +34 -0
  396. package/dist/web-annotations/validation.js +288 -0
  397. package/docs/README.md +23 -0
  398. package/docs/ops/install-developer-host.md +112 -0
  399. package/docs/ops/install-user-host.md +76 -0
  400. package/docs/ops/upgrade-user-to-developer-host.md +69 -0
  401. package/docs/ops/vscode-extension-release.md +138 -0
  402. package/package.json +24 -9
  403. package/skills/builtin/pi-agent-harness/SKILL.md +24 -24
  404. package/skills/builtin/pibo-docker-system/SKILL.md +170 -0
  405. package/skills/builtin/pibo-spec-writing/SKILL.md +330 -0
  406. package/skills/builtin/prd/SKILL.md +143 -0
  407. package/skills/builtin/ralph-loop/SKILL.md +359 -0
  408. package/skills/builtin/ralph-prd-json/SKILL.md +123 -0
  409. package/skills/builtin/skill-creator/LICENSE.txt +202 -0
  410. package/skills/builtin/skill-creator/SKILL.md +513 -0
  411. package/skills/builtin/skill-creator/agents/analyzer.md +274 -0
  412. package/skills/builtin/skill-creator/agents/comparator.md +202 -0
  413. package/skills/builtin/skill-creator/agents/grader.md +223 -0
  414. package/skills/builtin/skill-creator/assets/eval_review.html +146 -0
  415. package/skills/builtin/skill-creator/eval-viewer/generate_review.py +471 -0
  416. package/skills/builtin/skill-creator/eval-viewer/viewer.html +1325 -0
  417. package/skills/builtin/skill-creator/references/schemas.md +430 -0
  418. package/skills/builtin/skill-creator/scripts/__init__.py +0 -0
  419. package/skills/builtin/skill-creator/scripts/aggregate_benchmark.py +401 -0
  420. package/skills/builtin/skill-creator/scripts/generate_report.py +326 -0
  421. package/skills/builtin/skill-creator/scripts/improve_description.py +247 -0
  422. package/skills/builtin/skill-creator/scripts/package_skill.py +136 -0
  423. package/skills/builtin/skill-creator/scripts/quick_validate.py +103 -0
  424. package/skills/builtin/skill-creator/scripts/run_eval.py +310 -0
  425. package/skills/builtin/skill-creator/scripts/run_loop.py +328 -0
  426. package/skills/builtin/skill-creator/scripts/utils.py +47 -0
  427. package/skills/builtin/web-annotations/SKILL.md +93 -0
  428. package/dist/apps/chat-ui/assets/index-D2Qxambd.js +0 -151
  429. package/dist/apps/chat-ui/assets/index-DXx368XK.css +0 -1
  430. package/dist/env.js +0 -32
  431. package/dist/plugins/core-tools.js +0 -94
  432. package/dist/plugins/example.js +0 -67
  433. package/dist/plugins/remote-agent.js +0 -9
  434. package/dist/remote/channel.js +0 -176
  435. package/dist/remote/client.js +0 -92
  436. package/dist/remote/examples/tui-controller.js +0 -263
  437. package/dist/remote/protocol.js +0 -55
  438. package/dist/remote/session-client.js +0 -189
  439. package/dist/sessions/bindings.js +0 -41
@@ -0,0 +1,827 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { createHash } from "node:crypto";
4
+ import { readFile, readdir, writeFile, mkdir } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { COMPUTE_RESOURCE_POLICY_LABELS, buildComputeResourcePolicyLabels, buildDockerResourcePolicyArgs, resolveComputeResourcePolicy, } from "./resource-policy.js";
7
+ const execFileAsync = promisify(execFile);
8
+ export const IMAGE_NAME = "pibo:latest";
9
+ export const LABEL_ROLE = "pibo.compute.role";
10
+ export const LABEL_CREATED_AT = "pibo.compute.createdAt";
11
+ export const LABEL_HOLDER = "pibo.compute.holder";
12
+ export const LABEL_WORKTREE = "pibo.compute.worktree";
13
+ export const LABEL_WORKTREE_PATH = "pibo.compute.worktreePath";
14
+ export const LABEL_PORT_BLOCK = "pibo.compute.portBlock";
15
+ export const LABEL_TTL_SECONDS = "pibo.compute.ttlSeconds";
16
+ export const LABEL_IDLE_SECONDS = "pibo.compute.idleSeconds";
17
+ export const LABEL_LAST_USED_AT = "pibo.compute.lastUsedAt";
18
+ export const LABEL_CLEANUP_STATE = "pibo.compute.cleanupState";
19
+ export const LABEL_DIRTY_REASON = "pibo.compute.dirtyReason";
20
+ export const LABEL_RALPH_JOB_ID = "pibo.ralph.jobId";
21
+ export const LABEL_RALPH_RUN_ID = "pibo.ralph.runId";
22
+ export async function dockerBuild(workspaceDir) {
23
+ const { stderr } = await execFileAsync("docker", ["build", "-t", IMAGE_NAME, "."], {
24
+ cwd: workspaceDir,
25
+ maxBuffer: 50 * 1024 * 1024,
26
+ });
27
+ if (stderr)
28
+ console.error(stderr);
29
+ }
30
+ export async function imageExists(name) {
31
+ try {
32
+ await execFileAsync("docker", ["inspect", "--type=image", name]);
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export async function getDependencyHash(workspaceDir) {
40
+ const hash = createHash("sha256");
41
+ const files = ["package.json", "package-lock.json", "Dockerfile"];
42
+ for (const file of files) {
43
+ try {
44
+ const content = await readFile(path.join(workspaceDir, file));
45
+ hash.update(content);
46
+ }
47
+ catch {
48
+ // file missing, skip
49
+ }
50
+ }
51
+ return hash.digest("hex");
52
+ }
53
+ export async function getSourceHash(workspaceDir) {
54
+ const hash = createHash("sha256");
55
+ const files = [];
56
+ async function walk(dir) {
57
+ const entries = await readdir(dir, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ const fullPath = path.join(dir, entry.name);
60
+ if (entry.isDirectory()) {
61
+ if (entry.name === "node_modules" ||
62
+ entry.name === ".git" ||
63
+ entry.name === "dist" ||
64
+ entry.name === ".pibo" ||
65
+ entry.name === "plans" ||
66
+ entry.name === "Reports") {
67
+ continue;
68
+ }
69
+ await walk(fullPath);
70
+ }
71
+ else if (entry.isFile()) {
72
+ if (entry.name.endsWith(".ts") ||
73
+ entry.name.endsWith(".tsx") ||
74
+ entry.name === "package.json" ||
75
+ entry.name === "package-lock.json" ||
76
+ entry.name === "Dockerfile") {
77
+ files.push(fullPath);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ await walk(workspaceDir);
83
+ files.sort();
84
+ for (const file of files) {
85
+ const content = await readFile(file);
86
+ hash.update(content);
87
+ }
88
+ return hash.digest("hex");
89
+ }
90
+ export async function shouldRebuild(workspaceDir, hashFile) {
91
+ const currentHash = await getSourceHash(workspaceDir);
92
+ try {
93
+ const savedHash = await readFile(hashFile, "utf-8");
94
+ return savedHash.trim() !== currentHash;
95
+ }
96
+ catch {
97
+ return true;
98
+ }
99
+ }
100
+ export async function shouldRebuildDeps(workspaceDir, hashFile) {
101
+ const currentHash = await getDependencyHash(workspaceDir);
102
+ try {
103
+ const savedHash = await readFile(hashFile, "utf-8");
104
+ return savedHash.trim() !== currentHash;
105
+ }
106
+ catch {
107
+ return true;
108
+ }
109
+ }
110
+ export async function saveHash(workspaceDir, hashFile) {
111
+ const hash = await getSourceHash(workspaceDir);
112
+ await mkdir(path.dirname(hashFile), { recursive: true });
113
+ await writeFile(hashFile, hash, "utf-8");
114
+ }
115
+ export async function saveDepHash(workspaceDir, hashFile) {
116
+ const hash = await getDependencyHash(workspaceDir);
117
+ await mkdir(path.dirname(hashFile), { recursive: true });
118
+ await writeFile(hashFile, hash, "utf-8");
119
+ }
120
+ const DEV_PORT_BASE = 4800;
121
+ const DEV_PORT_BLOCK_SIZE = 10;
122
+ async function findNextPortBlock() {
123
+ const { stdout } = await execFileAsync("docker", [
124
+ "ps",
125
+ "--filter",
126
+ `label=${LABEL_ROLE}=dev`,
127
+ "--format",
128
+ "{{.Labels}}",
129
+ ]);
130
+ const usedBlocks = new Set();
131
+ for (const line of stdout.trim().split("\n")) {
132
+ const match = line.match(/pibo\.compute\.portBlock=(\d+)/);
133
+ if (match)
134
+ usedBlocks.add(Number(match[1]));
135
+ }
136
+ let block = 0;
137
+ while (usedBlocks.has(block))
138
+ block++;
139
+ return block;
140
+ }
141
+ export const DEFAULT_COMPUTE_WORKER_TTL_SECONDS = 60 * 60;
142
+ export const DEFAULT_COMPUTE_WORKER_IDLE_SECONDS = 30 * 60;
143
+ export function resolveComputeWorkerLifecycle(options = {}, env = process.env) {
144
+ return {
145
+ ttlSeconds: positiveIntegerOption(options.ttlSeconds, env.PIBO_COMPUTE_TTL_SECONDS, DEFAULT_COMPUTE_WORKER_TTL_SECONDS),
146
+ idleSeconds: positiveIntegerOption(options.idleSeconds, env.PIBO_COMPUTE_IDLE_SECONDS, DEFAULT_COMPUTE_WORKER_IDLE_SECONDS),
147
+ };
148
+ }
149
+ const SAFE_RALPH_LABEL_VALUE = /^[A-Za-z0-9._:-]{1,128}$/;
150
+ function safeRalphLabel(label, value) {
151
+ if (!value || !SAFE_RALPH_LABEL_VALUE.test(value))
152
+ return [];
153
+ return [`${label}=${value}`];
154
+ }
155
+ function buildComputeWorkerMetadataLabels(options) {
156
+ return [
157
+ `${LABEL_ROLE}=${options.role}`,
158
+ `${LABEL_CREATED_AT}=${options.createdAt}`,
159
+ `${LABEL_PORT_BLOCK}=${options.portBlock}`,
160
+ `${LABEL_TTL_SECONDS}=${options.ttlSeconds}`,
161
+ `${LABEL_IDLE_SECONDS}=${options.idleSeconds}`,
162
+ ...(options.holder ? [`${LABEL_HOLDER}=${options.holder}`] : []),
163
+ ...(options.worktree ? [`${LABEL_WORKTREE}=${options.worktree}`] : []),
164
+ ...(options.worktreePath ? [`${LABEL_WORKTREE_PATH}=${options.worktreePath}`] : []),
165
+ ...Object.entries(options.ports).map(([name, value]) => `pibo.compute.port.${name}=${value}`),
166
+ ...safeRalphLabel(LABEL_RALPH_JOB_ID, options.ralphJobId),
167
+ ...safeRalphLabel(LABEL_RALPH_RUN_ID, options.ralphRunId),
168
+ ];
169
+ }
170
+ function positiveIntegerOption(optionValue, envValue, fallback) {
171
+ if (typeof optionValue === "number" && Number.isInteger(optionValue) && optionValue > 0)
172
+ return optionValue;
173
+ if (envValue === undefined || envValue.trim() === "")
174
+ return fallback;
175
+ const parsed = Number(envValue);
176
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
177
+ }
178
+ export async function createWorktree(repoDir, name) {
179
+ const worktreePath = path.join(repoDir, ".worktrees", name);
180
+ try {
181
+ await execFileAsync("git", ["worktree", "add", worktreePath, "-b", name], { cwd: repoDir });
182
+ }
183
+ catch (err) {
184
+ if (err.stderr?.includes("already exists")) {
185
+ // branch or worktree exists; try without -b
186
+ await execFileAsync("git", ["worktree", "add", worktreePath, name], { cwd: repoDir });
187
+ }
188
+ else {
189
+ throw err;
190
+ }
191
+ }
192
+ return worktreePath;
193
+ }
194
+ export function buildDevWorkerDockerRunArgs(options) {
195
+ const policy = options.policy ?? resolveComputeResourcePolicy();
196
+ const lifecycle = resolveComputeWorkerLifecycle(options);
197
+ return [
198
+ "run",
199
+ "-d",
200
+ "--name",
201
+ options.id,
202
+ ...buildDockerResourcePolicyArgs(policy),
203
+ "-p",
204
+ `${options.gatewayPort}:4789`,
205
+ "-p",
206
+ `${options.cdpPort}:56663`,
207
+ "-p",
208
+ `${options.webPort}:4788`,
209
+ "-p",
210
+ `${options.webUIPortChat}:4790`,
211
+ "-p",
212
+ `${options.webUIPortContext}:4791`,
213
+ "-v",
214
+ `${options.worktreePath}:/workspace`,
215
+ ...(options.hostNodeModules ? ["-v", `${options.hostNodeModules}:/workspace/node_modules`] : []),
216
+ "-w",
217
+ "/workspace",
218
+ ...buildComputeWorkerMetadataLabels({
219
+ role: "dev",
220
+ createdAt: options.createdAt,
221
+ holder: options.holder,
222
+ worktree: options.worktreeName,
223
+ worktreePath: options.worktreePath,
224
+ portBlock: String(options.block),
225
+ ports: {
226
+ gateway: String(options.gatewayPort),
227
+ cdp: String(options.cdpPort),
228
+ web: String(options.webPort),
229
+ chatUi: String(options.webUIPortChat),
230
+ contextUi: String(options.webUIPortContext),
231
+ },
232
+ ttlSeconds: lifecycle.ttlSeconds,
233
+ idleSeconds: lifecycle.idleSeconds,
234
+ ralphJobId: options.ralphJobId,
235
+ ralphRunId: options.ralphRunId,
236
+ }).flatMap((label) => ["--label", label]),
237
+ ...buildComputeResourcePolicyLabels(policy).flatMap((label) => ["--label", label]),
238
+ "--entrypoint",
239
+ "/bin/sh",
240
+ IMAGE_NAME,
241
+ "-c",
242
+ "tail -f /dev/null",
243
+ ];
244
+ }
245
+ export async function spawnDevWorker(options) {
246
+ const worktreePath = path.join(options.repoDir, ".worktrees", options.worktreeName);
247
+ await createWorktree(options.repoDir, options.worktreeName);
248
+ const block = await findNextPortBlock();
249
+ const base = DEV_PORT_BASE + block * DEV_PORT_BLOCK_SIZE;
250
+ const gatewayPort = base;
251
+ const cdpPort = base + 1;
252
+ const webPort = base + 2;
253
+ const webUIPortChat = base + 3;
254
+ const webUIPortContext = base + 4;
255
+ const id = `pibo-dev-${options.worktreeName}`;
256
+ const createdAt = new Date().toISOString();
257
+ // Mount host node_modules into the container so dependencies are immediately available
258
+ const hostNodeModules = path.join(options.repoDir, "node_modules");
259
+ let nodeModulesMount;
260
+ try {
261
+ const { stdout } = await execFileAsync("ls", ["-d", hostNodeModules]);
262
+ if (stdout.trim())
263
+ nodeModulesMount = hostNodeModules;
264
+ }
265
+ catch {
266
+ // host node_modules does not exist; skip mount
267
+ }
268
+ const args = buildDevWorkerDockerRunArgs({
269
+ id,
270
+ worktreePath,
271
+ worktreeName: options.worktreeName,
272
+ block,
273
+ gatewayPort,
274
+ cdpPort,
275
+ webPort,
276
+ webUIPortChat,
277
+ webUIPortContext,
278
+ createdAt,
279
+ holder: options.holder,
280
+ ttlSeconds: options.ttlSeconds,
281
+ idleSeconds: options.idleSeconds,
282
+ ralphJobId: options.ralphJobId,
283
+ ralphRunId: options.ralphRunId,
284
+ hostNodeModules: nodeModulesMount,
285
+ });
286
+ await execFileAsync("docker", args, { cwd: options.repoDir });
287
+ const host = await detectHost();
288
+ return {
289
+ id,
290
+ image: IMAGE_NAME,
291
+ gatewayHost: host,
292
+ gatewayPort,
293
+ cdpPort,
294
+ webPort,
295
+ webUIPortChat,
296
+ webUIPortContext,
297
+ connect: `docker exec -it ${id} bash`,
298
+ worktree: worktreePath,
299
+ };
300
+ }
301
+ export function buildWorkerDockerRunArgs(options) {
302
+ const policy = options.policy ?? resolveComputeResourcePolicy();
303
+ const lifecycle = resolveComputeWorkerLifecycle(options);
304
+ return [
305
+ "run",
306
+ "-d",
307
+ "--name",
308
+ options.id,
309
+ ...buildDockerResourcePolicyArgs(policy),
310
+ "-p",
311
+ "4789",
312
+ "-p",
313
+ "56663",
314
+ "-p",
315
+ "4788",
316
+ ...buildComputeWorkerMetadataLabels({
317
+ role: "worker",
318
+ createdAt: options.createdAt,
319
+ holder: options.holder,
320
+ worktree: options.worktreePath ? path.basename(options.worktreePath) : undefined,
321
+ worktreePath: options.worktreePath,
322
+ portBlock: "dynamic",
323
+ ports: { gateway: "4789", cdp: "56663", web: "4788" },
324
+ ttlSeconds: lifecycle.ttlSeconds,
325
+ idleSeconds: lifecycle.idleSeconds,
326
+ ralphJobId: options.ralphJobId,
327
+ ralphRunId: options.ralphRunId,
328
+ }).flatMap((label) => ["--label", label]),
329
+ ...buildComputeResourcePolicyLabels(policy).flatMap((label) => ["--label", label]),
330
+ IMAGE_NAME,
331
+ "gateway:web",
332
+ ];
333
+ }
334
+ export async function spawnWorker(options) {
335
+ const id = options.name || `pibo-worker-${Math.random().toString(36).slice(2, 10)}`;
336
+ const createdAt = new Date().toISOString();
337
+ const args = buildWorkerDockerRunArgs({
338
+ id,
339
+ createdAt,
340
+ holder: options.holder,
341
+ worktreePath: options.workspaceDir,
342
+ ttlSeconds: options.ttlSeconds,
343
+ idleSeconds: options.idleSeconds,
344
+ ralphJobId: options.ralphJobId,
345
+ ralphRunId: options.ralphRunId,
346
+ });
347
+ await execFileAsync("docker", args, { cwd: options.workspaceDir });
348
+ // Get assigned ports
349
+ const { stdout: port4789 } = await execFileAsync("docker", ["port", id, "4789"]);
350
+ const { stdout: port56663 } = await execFileAsync("docker", ["port", id, "56663"]);
351
+ const { stdout: port4788 } = await execFileAsync("docker", ["port", id, "4788"]);
352
+ const gatewayPort = parseHostPort(port4789);
353
+ const cdpPort = parseHostPort(port56663);
354
+ const webPort = parseHostPort(port4788);
355
+ // Detect host IP
356
+ const host = await detectHost();
357
+ return {
358
+ id,
359
+ image: IMAGE_NAME,
360
+ gatewayHost: host,
361
+ gatewayPort,
362
+ cdpPort,
363
+ webPort,
364
+ connect: `docker exec -it ${id} bash`,
365
+ };
366
+ }
367
+ function parseHostPort(dockerPortOutput) {
368
+ const lines = dockerPortOutput.trim().split("\n");
369
+ for (const line of lines) {
370
+ const parts = line.trim().split(":");
371
+ if (parts.length >= 2) {
372
+ const port = parseInt(parts[parts.length - 1], 10);
373
+ if (!Number.isNaN(port) && port > 0) {
374
+ return port;
375
+ }
376
+ }
377
+ }
378
+ return 0;
379
+ }
380
+ async function detectHost() {
381
+ try {
382
+ const { stdout } = await execFileAsync("hostname", ["-I"]);
383
+ const ips = stdout.trim().split(/\s+/);
384
+ const nonLocal = ips.find((ip) => !ip.startsWith("127."));
385
+ if (nonLocal)
386
+ return nonLocal;
387
+ return ips[0] || "127.0.0.1";
388
+ }
389
+ catch {
390
+ return "127.0.0.1";
391
+ }
392
+ }
393
+ export function parseDockerLabels(labelsStr) {
394
+ const labels = {};
395
+ for (const part of (labelsStr ?? "").split(",")) {
396
+ const index = part.indexOf("=");
397
+ if (index <= 0)
398
+ continue;
399
+ labels[part.slice(0, index)] = part.slice(index + 1);
400
+ }
401
+ return labels;
402
+ }
403
+ function numberLabel(value) {
404
+ if (value === undefined || value.trim() === "")
405
+ return undefined;
406
+ const parsed = Number(value);
407
+ return Number.isFinite(parsed) ? parsed : undefined;
408
+ }
409
+ function resourcePolicyFromLabels(labels) {
410
+ const policy = {};
411
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.memory])
412
+ policy.memory = labels[COMPUTE_RESOURCE_POLICY_LABELS.memory];
413
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.memorySwap])
414
+ policy.memorySwap = labels[COMPUTE_RESOURCE_POLICY_LABELS.memorySwap];
415
+ const pidsLimit = numberLabel(labels[COMPUTE_RESOURCE_POLICY_LABELS.pidsLimit]);
416
+ if (pidsLimit !== undefined)
417
+ policy.pidsLimit = pidsLimit;
418
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.shmSize])
419
+ policy.shmSize = labels[COMPUTE_RESOURCE_POLICY_LABELS.shmSize];
420
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.init])
421
+ policy.init = labels[COMPUTE_RESOURCE_POLICY_LABELS.init] === "true";
422
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.restart] === "no")
423
+ policy.restart = "no";
424
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.logDriver] === "json-file")
425
+ policy.logDriver = "json-file";
426
+ if (labels[COMPUTE_RESOURCE_POLICY_LABELS.logMaxSize])
427
+ policy.logMaxSize = labels[COMPUTE_RESOURCE_POLICY_LABELS.logMaxSize];
428
+ const logMaxFile = numberLabel(labels[COMPUTE_RESOURCE_POLICY_LABELS.logMaxFile]);
429
+ if (logMaxFile !== undefined)
430
+ policy.logMaxFile = logMaxFile;
431
+ return Object.keys(policy).length > 0 ? policy : undefined;
432
+ }
433
+ function portMapFromLabels(labels) {
434
+ const portMap = {};
435
+ for (const [key, value] of Object.entries(labels)) {
436
+ if (!key.startsWith("pibo.compute.port."))
437
+ continue;
438
+ portMap[key.slice("pibo.compute.port.".length)] = value;
439
+ }
440
+ return portMap;
441
+ }
442
+ function portMapFromInspectPorts(ports) {
443
+ const portMap = {};
444
+ if (!ports)
445
+ return portMap;
446
+ for (const [containerPort, bindings] of Object.entries(ports)) {
447
+ if (!bindings?.length)
448
+ continue;
449
+ portMap[containerPort] = bindings.map((binding) => `${binding.HostIp ?? ""}:${binding.HostPort ?? ""}`).join(",");
450
+ }
451
+ return portMap;
452
+ }
453
+ function cleanupEligibilityForWorker(worker) {
454
+ const reasons = [];
455
+ const normalizedState = worker.state.toLowerCase();
456
+ const stopped = ["exited", "dead", "created"].includes(normalizedState) || /^exited\b/i.test(worker.status);
457
+ if (worker.oomKilled)
458
+ reasons.push("oom-killed");
459
+ if (worker.cleanupState === "dirty" || worker.dirtyReason)
460
+ reasons.push("dirty");
461
+ if (stopped)
462
+ reasons.push("stopped");
463
+ if (normalizedState === "restarting")
464
+ reasons.push("restarting");
465
+ if (worker.role === "dev" && reasons.length > 0) {
466
+ return {
467
+ eligible: false,
468
+ reasons: ["dev-worker-preserved", ...reasons],
469
+ nextCommands: ["pibo compute reap --include-dev --max-age-minutes <n>"],
470
+ };
471
+ }
472
+ if (reasons.length === 0) {
473
+ return { eligible: false, reasons: ["running-or-retained"], nextCommands: ["pibo compute list --all --json"] };
474
+ }
475
+ return {
476
+ eligible: true,
477
+ reasons,
478
+ nextCommands: ["pibo compute reap --max-age-minutes <n>"],
479
+ };
480
+ }
481
+ function workerFromLabels(base) {
482
+ const role = base.labels[LABEL_ROLE] ?? base.fallbackRole ?? "unknown";
483
+ const portMap = portMapFromLabels(base.labels);
484
+ const worker = {
485
+ id: base.id,
486
+ name: base.name,
487
+ role,
488
+ state: base.state || stateFromStatus(base.status),
489
+ status: base.status ?? "",
490
+ ports: base.ports ?? "",
491
+ portMap,
492
+ hostPid: base.hostPid,
493
+ createdAt: base.labels[LABEL_CREATED_AT] ?? "unknown",
494
+ lastUsedAt: base.labels[LABEL_LAST_USED_AT],
495
+ holder: base.labels[LABEL_HOLDER],
496
+ worktree: base.labels[LABEL_WORKTREE],
497
+ worktreePath: base.labels[LABEL_WORKTREE_PATH],
498
+ ralphJobId: base.labels[LABEL_RALPH_JOB_ID],
499
+ ralphRunId: base.labels[LABEL_RALPH_RUN_ID],
500
+ cleanupState: base.labels[LABEL_CLEANUP_STATE],
501
+ dirtyReason: base.labels[LABEL_DIRTY_REASON],
502
+ oomKilled: base.oomKilled,
503
+ exitCode: base.exitCode,
504
+ resourcePolicy: resourcePolicyFromLabels(base.labels),
505
+ cleanupEligibility: { eligible: false, reasons: [], nextCommands: [] },
506
+ };
507
+ worker.cleanupEligibility = cleanupEligibilityForWorker(worker);
508
+ return worker;
509
+ }
510
+ function stateFromStatus(status) {
511
+ const lower = status.toLowerCase();
512
+ if (lower.startsWith("up"))
513
+ return "running";
514
+ if (lower.startsWith("exited"))
515
+ return "exited";
516
+ if (lower.includes("restarting"))
517
+ return "restarting";
518
+ if (lower.startsWith("created"))
519
+ return "created";
520
+ if (lower.startsWith("dead"))
521
+ return "dead";
522
+ return "unknown";
523
+ }
524
+ export function parseDockerWorkerListLine(line, fallbackRole) {
525
+ const [containerId, name, stateOrStatus, statusOrPorts, portsOrLabels, labelsMaybe] = line.split("\t");
526
+ if (!containerId || !name)
527
+ return undefined;
528
+ const hasStateColumn = labelsMaybe !== undefined;
529
+ const state = hasStateColumn ? stateOrStatus : stateFromStatus(stateOrStatus ?? "");
530
+ const status = hasStateColumn ? statusOrPorts : stateOrStatus;
531
+ const ports = hasStateColumn ? portsOrLabels : statusOrPorts;
532
+ const labels = parseDockerLabels(hasStateColumn ? labelsMaybe : portsOrLabels);
533
+ return workerFromLabels({ id: containerId, name, state: state ?? "unknown", status: status ?? "", ports: ports ?? "", labels, fallbackRole });
534
+ }
535
+ export function parseDockerWorkerInspect(value, fallback) {
536
+ const labels = value.Config?.Labels ?? {};
537
+ const role = labels[LABEL_ROLE] ?? fallback?.role;
538
+ if (!role)
539
+ return fallback;
540
+ const state = value.State?.Status ?? fallback?.state ?? "unknown";
541
+ const status = statusFromInspectState(value.State, fallback?.status ?? state);
542
+ const portMap = { ...portMapFromInspectPorts(value.NetworkSettings?.Ports), ...portMapFromLabels(labels) };
543
+ const worker = workerFromLabels({
544
+ id: value.Id ?? fallback?.id ?? "unknown",
545
+ name: (value.Name ?? fallback?.name ?? "unknown").replace(/^\//, ""),
546
+ state,
547
+ status,
548
+ ports: fallback?.ports ?? portsTextFromPortMap(portMap),
549
+ labels,
550
+ fallbackRole: role,
551
+ oomKilled: value.State?.OOMKilled,
552
+ exitCode: value.State?.ExitCode,
553
+ hostPid: value.State?.Pid,
554
+ });
555
+ worker.portMap = portMap;
556
+ if (value.Created && worker.createdAt === "unknown")
557
+ worker.createdAt = value.Created;
558
+ return worker;
559
+ }
560
+ function statusFromInspectState(state, fallback) {
561
+ if (!state)
562
+ return fallback;
563
+ if (state.Status === "running" && state.StartedAt)
564
+ return `running since ${state.StartedAt}`;
565
+ if (state.Status === "exited")
566
+ return `exited (${state.ExitCode ?? "unknown"})`;
567
+ if (state.Status)
568
+ return state.Status;
569
+ return fallback;
570
+ }
571
+ function portsTextFromPortMap(portMap) {
572
+ const entries = Object.entries(portMap);
573
+ return entries.length ? entries.map(([key, value]) => `${key}=${value}`).join(",") : "-";
574
+ }
575
+ async function enrichWorkersWithInspect(workers) {
576
+ if (workers.length === 0)
577
+ return workers;
578
+ try {
579
+ const { stdout } = await execFileAsync("docker", ["inspect", ...workers.map((worker) => worker.id)]);
580
+ const inspected = JSON.parse(stdout);
581
+ const fallbackById = new Map(workers.map((worker) => [worker.id, worker]));
582
+ const fallbackByName = new Map(workers.map((worker) => [worker.name, worker]));
583
+ return inspected.map((container) => parseDockerWorkerInspect(container, fallbackById.get(container.Id ?? "") ?? fallbackByName.get((container.Name ?? "").replace(/^\//, "")))).filter((worker) => Boolean(worker));
584
+ }
585
+ catch {
586
+ return workers;
587
+ }
588
+ }
589
+ export async function listWorkers(options = {}) {
590
+ const roles = options.includeDev === false ? ["worker"] : ["worker", "dev"];
591
+ const workers = [];
592
+ for (const role of roles) {
593
+ const { stdout } = await execFileAsync("docker", [
594
+ "ps",
595
+ ...(options.all ? ["--all"] : []),
596
+ "--filter",
597
+ `label=${LABEL_ROLE}=${role}`,
598
+ "--format",
599
+ "{{.ID}}\t{{.Names}}\t{{.State}}\t{{.Status}}\t{{.Ports}}\t{{.Labels}}",
600
+ ]);
601
+ if (!stdout.trim())
602
+ continue;
603
+ const lines = stdout.trim().split("\n");
604
+ for (const line of lines) {
605
+ const worker = parseDockerWorkerListLine(line, role);
606
+ if (worker)
607
+ workers.push(worker);
608
+ }
609
+ }
610
+ return enrichWorkersWithInspect(workers);
611
+ }
612
+ function isStoppedWorker(worker) {
613
+ const normalizedState = worker.state.toLowerCase();
614
+ return ["exited", "dead", "created"].includes(normalizedState) || /^exited\b/i.test(worker.status) || normalizedState === "restarting";
615
+ }
616
+ function isDirtyWorker(worker) {
617
+ return worker.cleanupState === "dirty" || Boolean(worker.dirtyReason) || worker.oomKilled === true;
618
+ }
619
+ export function buildComputeWorkerReapPlan(workers, options = {}) {
620
+ const now = options.now ?? new Date();
621
+ const maxAgeMinutes = options.maxAgeMinutes ?? 60;
622
+ const resolved = {
623
+ includeDev: options.includeDev === true,
624
+ includeStopped: options.includeStopped !== false,
625
+ includeDirty: options.includeDirty !== false,
626
+ maxAgeMinutes,
627
+ };
628
+ const items = workers.map((worker) => {
629
+ const reasons = [];
630
+ const skipReasons = [];
631
+ const created = new Date(worker.createdAt).getTime();
632
+ const ageMinutes = Number.isNaN(created) ? undefined : (now.getTime() - created) / 1000 / 60;
633
+ if (worker.role === "dev" && !resolved.includeDev)
634
+ skipReasons.push("dev-worker-preserved");
635
+ if (worker.role !== "worker" && worker.role !== "dev")
636
+ skipReasons.push("unsupported-role");
637
+ if (resolved.includeStopped && isStoppedWorker(worker))
638
+ reasons.push("stopped");
639
+ if (resolved.includeDirty && isDirtyWorker(worker))
640
+ reasons.push(worker.oomKilled ? "oom-killed" : "dirty");
641
+ if (ageMinutes !== undefined && ageMinutes > resolved.maxAgeMinutes)
642
+ reasons.push("old");
643
+ if (reasons.length === 0)
644
+ skipReasons.push("not-selected");
645
+ const action = skipReasons.length === 0 ? "remove" : "skip";
646
+ return {
647
+ worker,
648
+ action,
649
+ reasons,
650
+ skipReasons,
651
+ ageMinutes,
652
+ preservesWorktree: Boolean(worker.worktreePath),
653
+ };
654
+ });
655
+ return {
656
+ createdAt: now.toISOString(),
657
+ options: resolved,
658
+ items,
659
+ summary: {
660
+ selected: items.filter((item) => item.action === "remove").length,
661
+ skipped: items.filter((item) => item.action === "skip").length,
662
+ worktreesPreserved: items.filter((item) => item.action === "remove" && item.preservesWorktree).length,
663
+ },
664
+ nextCommands: [
665
+ "pibo compute reap --dry-run --max-age-minutes <n>",
666
+ "pibo compute reap --apply --max-age-minutes <n>",
667
+ "pibo compute list --all --json",
668
+ ],
669
+ };
670
+ }
671
+ export async function releaseWorker(id) {
672
+ try {
673
+ await execFileAsync("docker", ["stop", "-t", "10", id]);
674
+ }
675
+ catch {
676
+ // might already be stopped
677
+ }
678
+ await execFileAsync("docker", ["rm", id]);
679
+ }
680
+ export async function applyComputeWorkerReapPlan(plan, options = {}) {
681
+ const release = options.release ?? releaseWorker;
682
+ const removed = [];
683
+ for (const item of plan.items) {
684
+ if (item.action !== "remove")
685
+ continue;
686
+ await release(item.worker.name);
687
+ removed.push(item.worker.name);
688
+ }
689
+ return removed;
690
+ }
691
+ export async function planReapWorkers(options = {}) {
692
+ const workers = await listWorkers({ all: true });
693
+ return buildComputeWorkerReapPlan(workers, options);
694
+ }
695
+ export async function reapWorkers(maxAgeMinutes, options = {}) {
696
+ const plan = await planReapWorkers({ ...options, maxAgeMinutes });
697
+ return applyComputeWorkerReapPlan(plan);
698
+ }
699
+ function normalizeDockerSystemDfKind(type) {
700
+ const normalized = (type ?? "").trim().toLowerCase();
701
+ if (normalized === "images")
702
+ return "images";
703
+ if (normalized === "containers")
704
+ return "containers";
705
+ if (normalized === "local volumes" || normalized === "volumes")
706
+ return "localVolumes";
707
+ if (normalized === "build cache" || normalized === "buildkit cache")
708
+ return "buildCache";
709
+ return "unknown";
710
+ }
711
+ function parseOptionalNumber(value) {
712
+ if (value === undefined || value.trim() === "")
713
+ return undefined;
714
+ const parsed = Number(value);
715
+ return Number.isFinite(parsed) ? parsed : undefined;
716
+ }
717
+ export function parseDockerSizeBytes(value) {
718
+ const match = (value ?? "").trim().match(/^([0-9]+(?:\.[0-9]+)?)\s*([a-zA-Z]+)?/);
719
+ if (!match)
720
+ return undefined;
721
+ const amount = Number(match[1]);
722
+ if (!Number.isFinite(amount))
723
+ return undefined;
724
+ const unit = (match[2] ?? "B").toLowerCase();
725
+ const multipliers = {
726
+ b: 1,
727
+ kb: 1000,
728
+ mb: 1000 ** 2,
729
+ gb: 1000 ** 3,
730
+ tb: 1000 ** 4,
731
+ kib: 1024,
732
+ mib: 1024 ** 2,
733
+ gib: 1024 ** 3,
734
+ tib: 1024 ** 4,
735
+ };
736
+ const multiplier = multipliers[unit];
737
+ if (!multiplier)
738
+ return undefined;
739
+ return Math.round(amount * multiplier);
740
+ }
741
+ export function parseDockerSystemDfLines(output) {
742
+ const rows = [];
743
+ for (const line of output.trim().split("\n")) {
744
+ if (!line.trim())
745
+ continue;
746
+ let raw;
747
+ try {
748
+ const parsed = JSON.parse(line);
749
+ raw = Object.fromEntries(Object.entries(parsed).map(([key, value]) => [key, String(value ?? "")]));
750
+ }
751
+ catch {
752
+ continue;
753
+ }
754
+ const label = raw.Type ?? raw.type ?? "unknown";
755
+ const size = raw.Size ?? raw.size ?? "";
756
+ const reclaimable = raw.Reclaimable ?? raw.reclaimable ?? "";
757
+ rows.push({
758
+ kind: normalizeDockerSystemDfKind(label),
759
+ label,
760
+ totalCount: parseOptionalNumber(raw.TotalCount ?? raw.totalCount),
761
+ active: parseOptionalNumber(raw.Active ?? raw.active),
762
+ size,
763
+ sizeBytes: parseDockerSizeBytes(size),
764
+ reclaimable,
765
+ reclaimableBytes: parseDockerSizeBytes(reclaimable),
766
+ raw,
767
+ });
768
+ }
769
+ return rows;
770
+ }
771
+ export function buildComputeDiskDiagnostics(rows, options = {}) {
772
+ const usage = {
773
+ images: undefined,
774
+ containers: undefined,
775
+ localVolumes: undefined,
776
+ buildCache: undefined,
777
+ unknown: undefined,
778
+ };
779
+ for (const row of rows) {
780
+ usage[row.kind] = row;
781
+ }
782
+ const sizeBytes = rows.reduce((sum, row) => row.sizeBytes === undefined ? sum : sum + row.sizeBytes, 0);
783
+ const reclaimableBytes = rows.reduce((sum, row) => row.reclaimableBytes === undefined ? sum : sum + row.reclaimableBytes, 0);
784
+ return {
785
+ generatedAt: (options.now ?? new Date()).toISOString(),
786
+ readOnly: true,
787
+ dockerAvailable: options.dockerAvailable !== false,
788
+ dockerError: options.dockerError,
789
+ usage,
790
+ rows,
791
+ totals: {
792
+ sizeBytes: rows.some((row) => row.sizeBytes !== undefined) ? sizeBytes : undefined,
793
+ reclaimableBytes: rows.some((row) => row.reclaimableBytes !== undefined) ? reclaimableBytes : undefined,
794
+ },
795
+ suggestions: [
796
+ {
797
+ kind: "container-cleanup",
798
+ reason: "Remove stopped, dirty, or old Pibo compute containers without deleting Git worktrees.",
799
+ nextCommands: ["pibo compute reap --dry-run --json", "pibo compute reap --apply --max-age-minutes <n>"],
800
+ },
801
+ {
802
+ kind: "image-cleanup",
803
+ reason: "Review unused Docker images separately from Pibo compute containers.",
804
+ nextCommands: ["docker image ls", "docker image prune"],
805
+ },
806
+ {
807
+ kind: "build-cache-prune",
808
+ reason: "Review BuildKit cache before pruning retained build layers.",
809
+ nextCommands: ["docker builder du", "docker builder prune"],
810
+ },
811
+ {
812
+ kind: "worktree-cleanup",
813
+ reason: "Clean Git worktrees explicitly; compute container cleanup does not remove them.",
814
+ nextCommands: ["git worktree list", "git worktree prune"],
815
+ },
816
+ ],
817
+ };
818
+ }
819
+ export async function getComputeDiskDiagnostics(options = {}) {
820
+ try {
821
+ const { stdout } = await execFileAsync("docker", ["system", "df", "--format", "{{json .}}"], { maxBuffer: 10 * 1024 * 1024 });
822
+ return buildComputeDiskDiagnostics(parseDockerSystemDfLines(stdout), { now: options.now, dockerAvailable: true });
823
+ }
824
+ catch (error) {
825
+ return buildComputeDiskDiagnostics([], { now: options.now, dockerAvailable: false, dockerError: String(error?.message ?? error) });
826
+ }
827
+ }