gsd-pi 2.64.0 → 2.65.0-dev.5c8557b

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 (363) hide show
  1. package/dist/headless.js +3 -1
  2. package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +22 -7
  3. package/dist/resources/extensions/bg-shell/process-manager.js +6 -1
  4. package/dist/resources/extensions/gsd/auto-dashboard.js +5 -5
  5. package/dist/resources/extensions/gsd/auto-post-unit.js +98 -1
  6. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  7. package/dist/resources/extensions/gsd/auto.js +5 -0
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +24 -13
  9. package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
  10. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -0
  11. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +16 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +20 -0
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +104 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  17. package/dist/resources/extensions/gsd/notification-overlay.js +256 -0
  18. package/dist/resources/extensions/gsd/notification-store.js +273 -0
  19. package/dist/resources/extensions/gsd/notification-widget.js +56 -0
  20. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  21. package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
  22. package/dist/resources/extensions/gsd/preferences-types.js +4 -0
  23. package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
  24. package/dist/resources/extensions/gsd/preferences.js +4 -0
  25. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  26. package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
  27. package/dist/web/standalone/.next/BUILD_ID +1 -1
  28. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -15
  29. package/dist/web/standalone/.next/build-manifest.json +3 -3
  30. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  31. package/dist/web/standalone/.next/required-server-files.json +4 -4
  32. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  33. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  34. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  36. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  44. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  60. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  72. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/notifications/route.js +3 -0
  92. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
  93. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
  94. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  103. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  109. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  125. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  127. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  129. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/index.html +1 -1
  139. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  140. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  141. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  142. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  144. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/page.js +2 -2
  146. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -15
  148. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  149. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  150. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  151. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/middleware.js +2 -2
  153. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  155. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  156. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  157. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  158. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
  159. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  160. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
  161. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
  162. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
  163. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
  164. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
  165. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
  166. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
  167. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
  168. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
  169. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
  170. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
  171. package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
  172. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
  175. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
  176. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
  177. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
  178. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
  179. package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
  180. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
  181. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
  182. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
  183. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
  184. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
  185. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
  186. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
  187. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
  188. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
  189. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
  190. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
  191. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
  192. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
  193. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
  194. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
  195. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
  196. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
  197. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
  198. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
  199. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
  200. package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
  201. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
  202. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  203. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
  206. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
  207. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  208. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
  209. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
  210. package/dist/web/standalone/.next/static/qq3YfHPfyqvh3DIMVmsRH/_buildManifest.js +1 -0
  211. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  212. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  213. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  214. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  215. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  216. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  217. package/dist/web/standalone/server.js +1 -1
  218. package/package.json +1 -1
  219. package/packages/pi-agent-core/dist/agent-loop.js +26 -9
  220. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  221. package/packages/pi-agent-core/src/agent-loop.test.ts +100 -4
  222. package/packages/pi-agent-core/src/agent-loop.ts +43 -12
  223. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
  224. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
  226. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
  229. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
  231. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
  233. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -1
  236. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -0
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +36 -0
  244. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  245. package/packages/pi-coding-agent/package.json +1 -1
  246. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
  247. package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
  248. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
  249. package/packages/pi-coding-agent/src/core/resource-loader.ts +5 -1
  250. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +9 -0
  251. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +33 -0
  252. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
  253. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
  254. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
  255. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
  256. package/packages/pi-tui/dist/components/loader.d.ts +4 -2
  257. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
  258. package/packages/pi-tui/dist/components/loader.js +27 -9
  259. package/packages/pi-tui/dist/components/loader.js.map +1 -1
  260. package/packages/pi-tui/dist/components/text.d.ts.map +1 -1
  261. package/packages/pi-tui/dist/components/text.js +2 -0
  262. package/packages/pi-tui/dist/components/text.js.map +1 -1
  263. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  264. package/packages/pi-tui/dist/overlay-layout.js +12 -1
  265. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  266. package/packages/pi-tui/dist/tui.d.ts +4 -0
  267. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  268. package/packages/pi-tui/dist/tui.js +35 -0
  269. package/packages/pi-tui/dist/tui.js.map +1 -1
  270. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
  271. package/packages/pi-tui/src/components/loader.ts +27 -10
  272. package/packages/pi-tui/src/components/text.ts +1 -0
  273. package/packages/pi-tui/src/overlay-layout.ts +13 -1
  274. package/packages/pi-tui/src/tui.ts +34 -0
  275. package/pkg/package.json +1 -1
  276. package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +19 -7
  277. package/src/resources/extensions/bg-shell/process-manager.ts +8 -2
  278. package/src/resources/extensions/gsd/auto-dashboard.ts +5 -4
  279. package/src/resources/extensions/gsd/auto-post-unit.ts +122 -0
  280. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  281. package/src/resources/extensions/gsd/auto.ts +4 -0
  282. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +25 -13
  283. package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
  284. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  285. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +20 -0
  286. package/src/resources/extensions/gsd/bootstrap/system-context.ts +28 -0
  287. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  288. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  289. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +140 -0
  290. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  291. package/src/resources/extensions/gsd/notification-overlay.ts +295 -0
  292. package/src/resources/extensions/gsd/notification-store.ts +293 -0
  293. package/src/resources/extensions/gsd/notification-widget.ts +68 -0
  294. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  295. package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
  296. package/src/resources/extensions/gsd/preferences-types.ts +28 -0
  297. package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
  298. package/src/resources/extensions/gsd/preferences.ts +4 -0
  299. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
  300. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +36 -0
  301. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
  302. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  303. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +73 -0
  304. package/src/resources/extensions/gsd/tests/notification-store.test.ts +282 -0
  305. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  306. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  307. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
  308. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  309. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  310. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
  311. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  312. package/src/resources/extensions/gsd/workflow-logger.ts +13 -0
  313. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
  314. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
  315. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
  316. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  317. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  318. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
  319. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
  320. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
  321. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
  322. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
  323. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
  324. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
  325. package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
  326. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
  327. package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
  328. package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
  329. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
  330. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
  331. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
  332. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
  333. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
  334. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
  335. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
  336. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
  337. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
  338. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
  339. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
  340. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
  341. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
  342. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
  343. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
  344. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
  345. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
  346. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
  347. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
  348. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  349. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
  350. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  351. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
  352. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
  353. package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
  354. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
  355. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  356. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  357. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
  358. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
  359. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  360. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
  361. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
  362. package/dist/web/standalone/.next/static/eebXKteM9EaWyseHKTjqp/_buildManifest.js +0 -1
  363. /package/dist/web/standalone/.next/static/{eebXKteM9EaWyseHKTjqp → qq3YfHPfyqvh3DIMVmsRH}/_ssgManifest.js +0 -0
@@ -0,0 +1,73 @@
1
+ // GSD Extension — Notification Overlay Tests
2
+ // Tests for message wrapping and content-fit sizing in the notification panel.
3
+
4
+ import { describe, test } from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ // The wrapText function is private to the module, so we test the overlay's
8
+ // render output indirectly. We also extract and test wrapText logic directly.
9
+
10
+ // ── wrapText logic (mirrors the private function) ───────────────────────────
11
+
12
+ function wrapText(text: string, maxWidth: number): string[] {
13
+ if (text.length <= maxWidth) return [text];
14
+ const words = text.split(/\s+/);
15
+ const lines: string[] = [];
16
+ let current = "";
17
+ for (const word of words) {
18
+ if (current.length === 0) {
19
+ current = word;
20
+ } else if (current.length + 1 + word.length <= maxWidth) {
21
+ current += " " + word;
22
+ } else {
23
+ lines.push(current);
24
+ current = word;
25
+ }
26
+ }
27
+ if (current.length > 0) lines.push(current);
28
+ return lines.map((l) => l.length > maxWidth ? l.slice(0, maxWidth - 1) + "…" : l);
29
+ }
30
+
31
+ describe("notification overlay — wrapText", () => {
32
+ test("short text returns single line", () => {
33
+ const result = wrapText("hello world", 80);
34
+ assert.deepStrictEqual(result, ["hello world"]);
35
+ });
36
+
37
+ test("long text wraps at word boundaries", () => {
38
+ const text = "This is a long notification message that should wrap across multiple lines";
39
+ const result = wrapText(text, 40);
40
+ assert.ok(result.length > 1, `expected multiple lines, got ${result.length}`);
41
+ for (const line of result) {
42
+ assert.ok(line.length <= 40, `line exceeds maxWidth: "${line}" (${line.length})`);
43
+ }
44
+ });
45
+
46
+ test("single word exceeding maxWidth is truncated", () => {
47
+ const result = wrapText("superlongwordthatexceedsmaxwidth", 10);
48
+ assert.equal(result.length, 1);
49
+ assert.equal(result[0]!.length, 10);
50
+ assert.ok(result[0]!.endsWith("…"));
51
+ });
52
+
53
+ test("empty string returns single empty line", () => {
54
+ const result = wrapText("", 80);
55
+ assert.deepStrictEqual(result, [""]);
56
+ });
57
+
58
+ test("exact-fit text returns single line", () => {
59
+ const text = "exactly twenty chars";
60
+ const result = wrapText(text, 20);
61
+ assert.deepStrictEqual(result, [text]);
62
+ });
63
+
64
+ test("preserves all words across wrapped lines", () => {
65
+ const words = ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot"];
66
+ const text = words.join(" ");
67
+ const result = wrapText(text, 15);
68
+ const rejoined = result.join(" ");
69
+ for (const w of words) {
70
+ assert.ok(rejoined.includes(w), `missing word: ${w}`);
71
+ }
72
+ });
73
+ });
@@ -0,0 +1,282 @@
1
+ // GSD Extension — Notification Store Tests
2
+
3
+ import { describe, test, beforeEach, afterEach } from "node:test";
4
+ import assert from "node:assert/strict";
5
+ import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { tmpdir } from "node:os";
8
+
9
+ import {
10
+ initNotificationStore,
11
+ appendNotification,
12
+ readNotifications,
13
+ markAllRead,
14
+ clearNotifications,
15
+ getUnreadCount,
16
+ getLineCount,
17
+ suppressPersistence,
18
+ unsuppressPersistence,
19
+ _resetNotificationStore,
20
+ } from "../notification-store.js";
21
+
22
+ describe("notification-store", () => {
23
+ let tmp: string;
24
+
25
+ beforeEach(() => {
26
+ tmp = mkdtempSync(join(tmpdir(), "gsd-notif-test-"));
27
+ mkdirSync(join(tmp, ".gsd"), { recursive: true });
28
+ _resetNotificationStore();
29
+ });
30
+
31
+ afterEach(() => {
32
+ _resetNotificationStore();
33
+ rmSync(tmp, { recursive: true, force: true });
34
+ });
35
+
36
+ test("appendNotification creates file and writes entry", () => {
37
+ initNotificationStore(tmp);
38
+ appendNotification("test message", "info");
39
+
40
+ const filePath = join(tmp, ".gsd", "notifications.jsonl");
41
+ assert.ok(existsSync(filePath));
42
+
43
+ const content = readFileSync(filePath, "utf-8").trim();
44
+ const entry = JSON.parse(content);
45
+ assert.equal(entry.message, "test message");
46
+ assert.equal(entry.severity, "info");
47
+ assert.equal(entry.source, "notify");
48
+ assert.equal(entry.read, false);
49
+ assert.ok(entry.id);
50
+ assert.ok(entry.ts);
51
+ });
52
+
53
+ test("readNotifications returns newest-first", () => {
54
+ initNotificationStore(tmp);
55
+ appendNotification("first", "info");
56
+ appendNotification("second", "warning");
57
+ appendNotification("third", "error");
58
+
59
+ const entries = readNotifications();
60
+ assert.equal(entries.length, 3);
61
+ assert.equal(entries[0].message, "third");
62
+ assert.equal(entries[1].message, "second");
63
+ assert.equal(entries[2].message, "first");
64
+ });
65
+
66
+ test("getUnreadCount tracks appends", () => {
67
+ initNotificationStore(tmp);
68
+ assert.equal(getUnreadCount(), 0);
69
+
70
+ appendNotification("msg1", "info");
71
+ assert.equal(getUnreadCount(), 1);
72
+
73
+ appendNotification("msg2", "warning");
74
+ assert.equal(getUnreadCount(), 2);
75
+ });
76
+
77
+ test("markAllRead sets all entries to read", () => {
78
+ initNotificationStore(tmp);
79
+ appendNotification("msg1", "info");
80
+ appendNotification("msg2", "warning");
81
+
82
+ assert.equal(getUnreadCount(), 2);
83
+
84
+ markAllRead();
85
+
86
+ assert.equal(getUnreadCount(), 0);
87
+
88
+ const entries = readNotifications();
89
+ assert.ok(entries.every((e) => e.read === true));
90
+ });
91
+
92
+ test("clearNotifications empties the file", () => {
93
+ initNotificationStore(tmp);
94
+ appendNotification("msg1", "info");
95
+ appendNotification("msg2", "error");
96
+
97
+ assert.equal(getLineCount(), 2);
98
+
99
+ clearNotifications();
100
+
101
+ assert.equal(getLineCount(), 0);
102
+ assert.equal(getUnreadCount(), 0);
103
+ assert.equal(readNotifications().length, 0);
104
+ });
105
+
106
+ test("rotation keeps only 500 entries", () => {
107
+ initNotificationStore(tmp);
108
+
109
+ for (let i = 0; i < 510; i++) {
110
+ appendNotification(`msg-${i}`, "info");
111
+ }
112
+
113
+ const entries = readNotifications();
114
+ assert.ok(entries.length <= 500, `Expected <= 500 entries, got ${entries.length}`);
115
+ // Most recent should be msg-509
116
+ assert.equal(entries[0].message, "msg-509");
117
+ });
118
+
119
+ test("source field is preserved", () => {
120
+ initNotificationStore(tmp);
121
+ appendNotification("from notify", "info", "notify");
122
+ appendNotification("from logger", "warning", "workflow-logger");
123
+
124
+ const entries = readNotifications();
125
+ assert.equal(entries[0].source, "workflow-logger");
126
+ assert.equal(entries[1].source, "notify");
127
+ });
128
+
129
+ test("messages are truncated at 500 chars", () => {
130
+ initNotificationStore(tmp);
131
+ const longMsg = "x".repeat(600);
132
+ appendNotification(longMsg, "info");
133
+
134
+ const entries = readNotifications();
135
+ assert.ok(entries[0].message.length <= 501); // 500 + "…"
136
+ assert.ok(entries[0].message.endsWith("…"));
137
+ });
138
+
139
+ test("readNotifications with explicit basePath works", () => {
140
+ initNotificationStore(tmp);
141
+ appendNotification("msg1", "info");
142
+
143
+ // Read with explicit basePath
144
+ _resetNotificationStore();
145
+ const entries = readNotifications(tmp);
146
+ assert.equal(entries.length, 1);
147
+ assert.equal(entries[0].message, "msg1");
148
+ });
149
+
150
+ test("init seeds counters from existing file", () => {
151
+ initNotificationStore(tmp);
152
+ appendNotification("msg1", "info");
153
+ appendNotification("msg2", "warning");
154
+
155
+ // Reset and re-init — should seed from disk
156
+ _resetNotificationStore();
157
+ initNotificationStore(tmp);
158
+
159
+ assert.equal(getLineCount(), 2);
160
+ assert.equal(getUnreadCount(), 2);
161
+ });
162
+
163
+ test("no-op when store not initialized", () => {
164
+ // Should not throw
165
+ appendNotification("msg", "info");
166
+ assert.equal(readNotifications().length, 0);
167
+ assert.equal(getUnreadCount(), 0);
168
+ });
169
+
170
+ test("suppressPersistence prevents writes", () => {
171
+ initNotificationStore(tmp);
172
+ appendNotification("before", "info");
173
+ assert.equal(getLineCount(), 1);
174
+
175
+ suppressPersistence();
176
+ appendNotification("suppressed", "info");
177
+ assert.equal(getLineCount(), 1); // still 1
178
+
179
+ unsuppressPersistence();
180
+ appendNotification("after", "info");
181
+ assert.equal(getLineCount(), 2); // now 2
182
+
183
+ const entries = readNotifications();
184
+ assert.equal(entries[0].message, "after");
185
+ assert.equal(entries[1].message, "before");
186
+ // "suppressed" should not appear
187
+ assert.ok(!entries.some((e) => e.message === "suppressed"));
188
+ });
189
+
190
+ test("suppressPersistence is ref-counted", () => {
191
+ initNotificationStore(tmp);
192
+ suppressPersistence();
193
+ suppressPersistence();
194
+ unsuppressPersistence();
195
+ // Still suppressed (one suppress remaining)
196
+ appendNotification("still suppressed", "info");
197
+ assert.equal(getLineCount(), 0);
198
+
199
+ unsuppressPersistence();
200
+ appendNotification("now works", "info");
201
+ assert.equal(getLineCount(), 1);
202
+ });
203
+
204
+ test("reinit switches to new project path", () => {
205
+ const tmp2 = mkdtempSync(join(tmpdir(), "gsd-notif-test2-"));
206
+ mkdirSync(join(tmp2, ".gsd"), { recursive: true });
207
+
208
+ initNotificationStore(tmp);
209
+ appendNotification("project1", "info");
210
+
211
+ // Switch to new project
212
+ initNotificationStore(tmp2);
213
+ appendNotification("project2", "info");
214
+
215
+ // project2 should only have its own entry
216
+ const entries = readNotifications();
217
+ assert.equal(entries.length, 1);
218
+ assert.equal(entries[0].message, "project2");
219
+
220
+ // project1 should still have its entry
221
+ const p1Entries = readNotifications(tmp);
222
+ assert.equal(p1Entries.length, 1);
223
+ assert.equal(p1Entries[0].message, "project1");
224
+
225
+ rmSync(tmp2, { recursive: true, force: true });
226
+ });
227
+
228
+ test("counters resync from disk after markAllRead", () => {
229
+ initNotificationStore(tmp);
230
+ appendNotification("msg1", "info");
231
+ appendNotification("msg2", "info");
232
+ assert.equal(getUnreadCount(), 2);
233
+ assert.equal(getLineCount(), 2);
234
+
235
+ markAllRead();
236
+ assert.equal(getUnreadCount(), 0);
237
+ assert.equal(getLineCount(), 2); // entries still exist, just marked read
238
+ });
239
+
240
+ test("counters resync from disk after clearNotifications", () => {
241
+ initNotificationStore(tmp);
242
+ appendNotification("msg1", "info");
243
+ appendNotification("msg2", "info");
244
+
245
+ clearNotifications();
246
+ assert.equal(getUnreadCount(), 0);
247
+ assert.equal(getLineCount(), 0);
248
+ });
249
+
250
+ test("markAllRead does not delete a foreign lock file", () => {
251
+ initNotificationStore(tmp);
252
+ appendNotification("msg1", "info");
253
+
254
+ // Simulate another process holding the lock
255
+ const lockPath = join(tmp, ".gsd", "notifications.lock");
256
+ writeFileSync(lockPath, String(Date.now()), "utf-8");
257
+
258
+ // markAllRead should still work (best-effort) but not delete the foreign lock
259
+ markAllRead();
260
+
261
+ assert.ok(existsSync(lockPath), "foreign lock file should not be deleted");
262
+
263
+ // Clean up the lock so afterEach doesn't leave artifacts
264
+ rmSync(lockPath, { force: true });
265
+ });
266
+
267
+ test("clearNotifications does not delete a foreign lock file", () => {
268
+ initNotificationStore(tmp);
269
+ appendNotification("msg1", "info");
270
+
271
+ // Simulate another process holding the lock
272
+ const lockPath = join(tmp, ".gsd", "notifications.lock");
273
+ writeFileSync(lockPath, String(Date.now()), "utf-8");
274
+
275
+ // clearNotifications should still work but not delete the foreign lock
276
+ clearNotifications();
277
+
278
+ assert.ok(existsSync(lockPath), "foreign lock file should not be deleted");
279
+
280
+ rmSync(lockPath, { force: true });
281
+ });
282
+ });
@@ -0,0 +1,312 @@
1
+ /**
2
+ * post-exec-retry-bypass.test.ts — Tests for post-execution blocking failure retry bypass.
3
+ *
4
+ * Verifies that when post-execution checks fail (postExecBlockingFailure is true),
5
+ * the retry system is bypassed and auto-mode pauses immediately. Post-execution
6
+ * failures are cross-task consistency issues — retrying the same task won't fix them.
7
+ */
8
+
9
+ import { describe, test, mock, beforeEach, afterEach } from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import { tmpdir } from "node:os";
12
+ import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
13
+ import { join } from "node:path";
14
+
15
+ import { runPostUnitVerification, type VerificationContext } from "../auto-verification.ts";
16
+ import { AutoSession } from "../auto/session.ts";
17
+ import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask } from "../gsd-db.ts";
18
+ import { invalidateAllCaches } from "../cache.ts";
19
+ import { _clearGsdRootCache } from "../paths.ts";
20
+
21
+ // ─── Test Fixtures ───────────────────────────────────────────────────────────
22
+
23
+ let tempDir: string;
24
+ let dbPath: string;
25
+ let originalCwd: string;
26
+
27
+ function makeMockCtx() {
28
+ return {
29
+ ui: {
30
+ notify: mock.fn(),
31
+ setStatus: () => {},
32
+ setWidget: () => {},
33
+ setFooter: () => {},
34
+ },
35
+ model: { id: "test-model" },
36
+ } as any;
37
+ }
38
+
39
+ function makeMockPi() {
40
+ return {
41
+ sendMessage: mock.fn(),
42
+ setModel: mock.fn(async () => true),
43
+ } as any;
44
+ }
45
+
46
+ function makeMockSession(basePath: string, currentUnit?: { type: string; id: string }): AutoSession {
47
+ const s = new AutoSession();
48
+ s.basePath = basePath;
49
+ s.active = true;
50
+ // verificationRetryCount is readonly but initialized as an empty Map in AutoSession
51
+ s.pendingVerificationRetry = null;
52
+ if (currentUnit) {
53
+ s.currentUnit = {
54
+ type: currentUnit.type,
55
+ id: currentUnit.id,
56
+ startedAt: Date.now(),
57
+ };
58
+ }
59
+ return s;
60
+ }
61
+
62
+ function setupTestEnvironment(): void {
63
+ originalCwd = process.cwd();
64
+ tempDir = join(tmpdir(), `post-exec-retry-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
65
+ mkdirSync(tempDir, { recursive: true });
66
+
67
+ const gsdDir = join(tempDir, ".gsd");
68
+ mkdirSync(gsdDir, { recursive: true });
69
+
70
+ const milestonesDir = join(gsdDir, "milestones", "M001", "slices", "S01", "tasks");
71
+ mkdirSync(milestonesDir, { recursive: true });
72
+
73
+ process.chdir(tempDir);
74
+ _clearGsdRootCache();
75
+
76
+ dbPath = join(gsdDir, "gsd.db");
77
+ openDatabase(dbPath);
78
+ }
79
+
80
+ function cleanupTestEnvironment(): void {
81
+ try {
82
+ process.chdir(originalCwd);
83
+ } catch {
84
+ // Ignore
85
+ }
86
+ try {
87
+ closeDatabase();
88
+ } catch {
89
+ // Ignore
90
+ }
91
+ try {
92
+ rmSync(tempDir, { recursive: true, force: true });
93
+ } catch {
94
+ // Ignore
95
+ }
96
+ }
97
+
98
+ function writePreferences(prefs: Record<string, unknown>): void {
99
+ const yamlLines = Object.entries(prefs).map(([k, v]) => `${k}: ${JSON.stringify(v)}`);
100
+ const prefsContent = `---
101
+ ${yamlLines.join("\n")}
102
+ ---
103
+
104
+ # GSD Preferences
105
+ `;
106
+ writeFileSync(join(tempDir, ".gsd", "PREFERENCES.md"), prefsContent);
107
+ invalidateAllCaches();
108
+ _clearGsdRootCache();
109
+ }
110
+
111
+ /**
112
+ * Create a task in DB that will pass basic verification but allows us to test the flow.
113
+ */
114
+ function createBasicTask(): void {
115
+ insertMilestone({ id: "M001" });
116
+ insertSlice({
117
+ id: "S01",
118
+ milestoneId: "M001",
119
+ title: "Test Slice",
120
+ risk: "low",
121
+ });
122
+
123
+ // Create a simple task
124
+ insertTask({
125
+ id: "T01",
126
+ sliceId: "S01",
127
+ milestoneId: "M001",
128
+ title: "Basic task",
129
+ status: "pending",
130
+ planning: {
131
+ description: "A basic task for testing",
132
+ estimate: "1h",
133
+ files: [],
134
+ verify: "echo pass", // Simple verification that always passes
135
+ inputs: [],
136
+ expectedOutput: ["output.ts"],
137
+ observabilityImpact: "",
138
+ },
139
+ sequence: 0,
140
+ });
141
+ }
142
+
143
+ // ─── Tests ───────────────────────────────────────────────────────────────────
144
+
145
+ describe("Post-execution blocking failure retry bypass", () => {
146
+ beforeEach(() => {
147
+ setupTestEnvironment();
148
+ });
149
+
150
+ afterEach(() => {
151
+ cleanupTestEnvironment();
152
+ });
153
+
154
+ test("skips verification when unit type is not execute-task", async () => {
155
+ createBasicTask();
156
+ writePreferences({
157
+ enhanced_verification: true,
158
+ enhanced_verification_post: true,
159
+ verification_auto_fix: true,
160
+ verification_max_retries: 3,
161
+ });
162
+
163
+ const ctx = makeMockCtx();
164
+ const pi = makeMockPi();
165
+ const pauseAutoMock = mock.fn(async () => {});
166
+ const s = makeMockSession(tempDir, { type: "plan-slice", id: "M001/S01" });
167
+
168
+ const vctx: VerificationContext = { s, ctx, pi };
169
+ const result = await runPostUnitVerification(vctx, pauseAutoMock);
170
+
171
+ // Non-execute-task units should return "continue" immediately
172
+ assert.equal(result, "continue");
173
+ assert.equal(pauseAutoMock.mock.callCount(), 0);
174
+ });
175
+
176
+ test("returns continue when verification passes", async () => {
177
+ createBasicTask();
178
+ writePreferences({
179
+ enhanced_verification: true,
180
+ enhanced_verification_post: true,
181
+ verification_auto_fix: true,
182
+ verification_max_retries: 3,
183
+ });
184
+
185
+ const ctx = makeMockCtx();
186
+ const pi = makeMockPi();
187
+ const pauseAutoMock = mock.fn(async () => {});
188
+ const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
189
+
190
+ const vctx: VerificationContext = { s, ctx, pi };
191
+ const result = await runPostUnitVerification(vctx, pauseAutoMock);
192
+
193
+ // When verification passes, should return "continue" and not call pauseAuto
194
+ assert.equal(result, "continue");
195
+ assert.equal(pauseAutoMock.mock.callCount(), 0);
196
+
197
+ // Retry state should be cleared
198
+ assert.equal(s.pendingVerificationRetry, null);
199
+ });
200
+
201
+ test("verification retry count is cleared on success", async () => {
202
+ createBasicTask();
203
+ writePreferences({
204
+ enhanced_verification: true,
205
+ enhanced_verification_post: true,
206
+ verification_auto_fix: true,
207
+ verification_max_retries: 3,
208
+ });
209
+
210
+ const ctx = makeMockCtx();
211
+ const pi = makeMockPi();
212
+ const pauseAutoMock = mock.fn(async () => {});
213
+ const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
214
+
215
+ // Pre-set some retry state
216
+ s.verificationRetryCount.set("M001/S01/T01", 2);
217
+
218
+ const vctx: VerificationContext = { s, ctx, pi };
219
+ const result = await runPostUnitVerification(vctx, pauseAutoMock);
220
+
221
+ // On success, retry count should be cleared
222
+ assert.equal(result, "continue");
223
+ assert.equal(s.verificationRetryCount.has("M001/S01/T01"), false);
224
+ });
225
+
226
+ test("post-exec failure notification mentions cross-task consistency", async () => {
227
+ // This test verifies that the notification for post-exec failures includes
228
+ // the appropriate message about cross-task consistency issues.
229
+ // The actual post-exec failure would require specific file/output state
230
+ // that's harder to set up in a unit test, but we can verify the code path exists.
231
+
232
+ createBasicTask();
233
+ writePreferences({
234
+ enhanced_verification: true,
235
+ enhanced_verification_post: true,
236
+ verification_auto_fix: true,
237
+ verification_max_retries: 3,
238
+ });
239
+
240
+ const ctx = makeMockCtx();
241
+ const pi = makeMockPi();
242
+ const pauseAutoMock = mock.fn(async () => {});
243
+ const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
244
+
245
+ const vctx: VerificationContext = { s, ctx, pi };
246
+ const result = await runPostUnitVerification(vctx, pauseAutoMock);
247
+
248
+ // The verification should pass with our simple "echo pass" task
249
+ // This test mainly confirms the wiring is correct
250
+ assert.equal(result, "continue");
251
+ });
252
+ });
253
+
254
+ describe("Post-execution retry behavior", () => {
255
+ beforeEach(() => {
256
+ setupTestEnvironment();
257
+ });
258
+
259
+ afterEach(() => {
260
+ cleanupTestEnvironment();
261
+ });
262
+
263
+ test("when autofix is disabled, failure pauses immediately without retry", async () => {
264
+ // Create a task with a verify command that will fail
265
+ insertMilestone({ id: "M001" });
266
+ insertSlice({
267
+ id: "S01",
268
+ milestoneId: "M001",
269
+ title: "Test Slice",
270
+ risk: "low",
271
+ });
272
+ insertTask({
273
+ id: "T01",
274
+ sliceId: "S01",
275
+ milestoneId: "M001",
276
+ title: "Failing task",
277
+ status: "pending",
278
+ planning: {
279
+ description: "Task with failing verification",
280
+ estimate: "1h",
281
+ files: [],
282
+ verify: "exit 1", // This will fail
283
+ inputs: [],
284
+ expectedOutput: [],
285
+ observabilityImpact: "",
286
+ },
287
+ sequence: 0,
288
+ });
289
+
290
+ writePreferences({
291
+ enhanced_verification: true,
292
+ enhanced_verification_post: true,
293
+ verification_auto_fix: false, // Autofix disabled
294
+ verification_max_retries: 3,
295
+ });
296
+
297
+ const ctx = makeMockCtx();
298
+ const pi = makeMockPi();
299
+ const pauseAutoMock = mock.fn(async () => {});
300
+ const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
301
+
302
+ const vctx: VerificationContext = { s, ctx, pi };
303
+ const result = await runPostUnitVerification(vctx, pauseAutoMock);
304
+
305
+ // When autofix is disabled and verification fails, should pause
306
+ assert.equal(result, "pause");
307
+ assert.equal(pauseAutoMock.mock.callCount(), 1);
308
+
309
+ // Should NOT set up a retry
310
+ assert.equal(s.pendingVerificationRetry, null);
311
+ });
312
+ });