@wular/coder 0.4.1 → 0.4.3

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 (378) hide show
  1. package/README.md +69 -70
  2. package/dist/actions-FzDNxMFf.js +20 -0
  3. package/dist/{adapter-BKVzTDnn.js → adapter-7SEiVsmp.js} +1 -1
  4. package/dist/{adapter-B0oYCI0S.js → adapter-BLEfUqoQ.js} +1 -1
  5. package/dist/{adapter-DPInwbJG.js → adapter-H4GlcKQz.js} +1 -1
  6. package/dist/{add-CE-70LFs.js → add-B50dIJGd.js} +1 -1
  7. package/dist/{add-BxpLHnvD.js → add-D7X3gxap.js} +1 -1
  8. package/dist/{add-ej0X9e3a.js → add-DOEUHIe7.js} +1 -1
  9. package/dist/{add-U-pty9qJ.js → add-QExQhTzi.js} +1 -1
  10. package/dist/{add-json-CEa-6luW.js → add-json-Ck4KIZrT.js} +1 -1
  11. package/dist/{agent-JdtZozvP.js → agent-BPtnEf1q.js} +1 -1
  12. package/dist/{agent-BsYx3Odz.js → agent-BlQ_dNNm.js} +2 -2
  13. package/dist/{alias-DX-Bm86x.js → alias-BwQBovke.js} +1 -1
  14. package/dist/{app-BT5f7zL5.js → app-y6BYm2eH.js} +1 -1
  15. package/dist/{approvals-C-AvFt6s.js → approvals-Cc9cq5T1.js} +2 -2
  16. package/dist/{approve-DOmpQRNd.js → approve-oO9k2fnK.js} +2 -2
  17. package/dist/{apps-CjfrZyM5.js → apps-CU2YalX8.js} +1 -1
  18. package/dist/{archive-BziJ1rQ-.js → archive-C5c_HQS1.js} +2 -2
  19. package/dist/{archive-BUB8gFKD.js → archive-CemlWlVY.js} +2 -2
  20. package/dist/archive-sweep-ehg-fWIt.js +1 -0
  21. package/dist/{args-BOEEdpWk.js → args-DcBCtXhz.js} +1 -1
  22. package/dist/{ask-dE3U3ss2.js → ask-Chu7Twu_.js} +2 -2
  23. package/dist/auth-BOyXPOtu.js +1 -0
  24. package/dist/auth-XbqKSbcD.js +1 -0
  25. package/dist/{chat-bridge-BS9WtP3X.js → chat-bridge-DXgZItGP.js} +1 -1
  26. package/dist/{chat-B_yhNTo0.js → chat-cknlIhVh.js} +1 -1
  27. package/dist/{cli-BilSDkvE.js → cli-B1ObVrL4.js} +5 -5
  28. package/dist/cli.d.ts +1 -1
  29. package/dist/cli.js +1 -1
  30. package/dist/{client-D9IoAuDD.js → client-31YzOj-i.js} +1 -1
  31. package/dist/{config-Da_cwoxI.js → config-DAE_qHYA.js} +1 -1
  32. package/dist/{create-C_iMJV4Y.js → create-CM8B8JxD.js} +1 -1
  33. package/dist/credentials-BXD4Qptk.js +1 -0
  34. package/dist/dash/manifest.json +934 -1014
  35. package/dist/dash/public/assets/chunks/{agent-pills-c9c4ac4d4a10df83.js → agent-pills-b6a6da91c9559d9f.js} +1 -1
  36. package/dist/dash/public/assets/chunks/{agent-settings-form-8be2ddab074aba6d.js → agent-settings-form-b615c677046cd6f2.js} +1 -1
  37. package/dist/dash/public/assets/chunks/agents-grid-f2e7b4ff9e11b6e3.js +1 -0
  38. package/dist/dash/public/assets/chunks/{badge-676b01e0c615bf7b.js → badge-215c7b2631dafc69.js} +1 -1
  39. package/dist/dash/public/assets/chunks/{card-e0901d68b79dfa93.js → card-e86dcc81326d2b14.js} +1 -1
  40. package/dist/dash/public/assets/chunks/{combo-41234c24668b1db1.js → combo-09e246dc9a517fa8.js} +1 -1
  41. package/dist/dash/public/assets/chunks/composer-417166b029f48c47.js +1 -0
  42. package/dist/dash/public/assets/chunks/composer-645ae8fa97a35280.js +1 -0
  43. package/dist/dash/public/assets/chunks/{confirm-f2fe4ba9905a89c6.js → confirm-5ae484241ad888fd.js} +1 -1
  44. package/dist/dash/public/assets/chunks/{connect-apps-aa2a3d82e48ed8c3.js → connect-apps-4147c45f45fde2b1.js} +1 -1
  45. package/dist/dash/public/assets/chunks/{connect-repositories-5389ec8701a3deac.js → connect-repositories-77591110836bdf03.js} +1 -1
  46. package/dist/dash/public/assets/chunks/credentials-list-e005a0783ec93465.js +2 -0
  47. package/dist/dash/public/assets/chunks/{custom-range-4761ab14f05149df.js → custom-range-463e119af4681c61.js} +1 -1
  48. package/dist/dash/public/assets/chunks/{editor-bfe8cd854f8001be.js → editor-804b84f4fe6539cd.js} +4 -4
  49. package/dist/dash/public/assets/chunks/{engines-list-f1191390f5715b39.js → engines-list-ceab51f13c690e8c.js} +1 -1
  50. package/dist/dash/public/assets/chunks/{field-901f1f35c18c39c9.js → field-aa0a4b98a976781e.js} +1 -1
  51. package/dist/dash/public/assets/chunks/filter-91144ac2eec6297f.js +1 -0
  52. package/dist/dash/public/assets/chunks/format-105c706345987fb2.js +1 -0
  53. package/dist/dash/public/assets/chunks/heads-a42817b32d2d7f08.js +1 -0
  54. package/dist/dash/public/assets/chunks/{instructions-052fa45a9daf6db6.js → instructions-3a1dcaff2d868aa5.js} +1 -1
  55. package/dist/dash/public/assets/chunks/instructions-6c6f981fffaa31e4.js +1 -0
  56. package/dist/dash/public/assets/chunks/{list-1d259660d7612c5f.js → list-7270361464fc3c04.js} +1 -1
  57. package/dist/dash/public/assets/chunks/{mcp-list-e3bebbbe831393b8.js → mcp-list-2e715d48666b54ad.js} +1 -1
  58. package/dist/dash/public/assets/chunks/{models-list-44f857a469ca04d5.js → models-list-d3c40f94c761a6e5.js} +1 -1
  59. package/dist/dash/public/assets/chunks/paged-3ddb6077bef42752.js +1 -0
  60. package/dist/dash/public/assets/chunks/{platform-actions-d1426602049926d2.js → platform-actions-271ec3b99a64280b.js} +1 -1
  61. package/dist/dash/public/assets/chunks/runners-list-767e49c7d0ec833a.js +1 -0
  62. package/dist/dash/public/assets/chunks/{segmented-358ff2b4992b8384.js → segmented-de4386ccaac7304d.js} +1 -1
  63. package/dist/dash/public/assets/chunks/{select-7ae25fd8a7d6c338.js → select-a90307706cffa6e0.js} +1 -1
  64. package/dist/dash/public/assets/chunks/{sentinel-c972a33dc69a5135.js → sentinel-b5e032b38604dfd3.js} +1 -1
  65. package/dist/dash/public/assets/chunks/shared-init-7f81ae7bc8c82438.js +2 -0
  66. package/dist/dash/public/assets/chunks/shared-init-933c7f9ee85be766.js +5 -0
  67. package/dist/dash/public/assets/chunks/{tail-7c27aab8ba268bd9.js → tail-4891e727ee526792.js} +1 -1
  68. package/dist/dash/public/assets/chunks/task-actions-ce854925a2dc78fd.js +1 -0
  69. package/dist/dash/public/assets/chunks/task-ddfddeaf07fb684c.js +1 -0
  70. package/dist/dash/public/assets/chunks/task-view-c37dcee08f21a279.js +5 -0
  71. package/dist/dash/public/assets/chunks/tasks-list-baef587999f322d8.js +1 -0
  72. package/dist/dash/public/assets/chunks/{toolbar-a75d2570f07cbb70.js → toolbar-8d98313606ad8a2d.js} +1 -1
  73. package/dist/dash/public/assets/chunks/{versions-list-fc2e0d2a7382e049.js → versions-list-532690efd9f1b0ea.js} +1 -1
  74. package/dist/dash/public/assets/dash-49ca85b59ac65c20.css +1 -0
  75. package/dist/dash/public/assets/dash-account-edc56e85dbaaabe0.css +1 -0
  76. package/dist/dash/public/assets/dash-agents-edba57b67e4005d0.css +1 -0
  77. package/dist/dash/public/assets/dash-agents-new-fe22253fbada2fbf.css +1 -0
  78. package/dist/dash/public/assets/dash-agents-param-slug-adf52e9dacb2d825.css +1 -0
  79. package/dist/dash/public/assets/dash-agents-param-slug-edit-a2937a347689ef29.css +1 -0
  80. package/dist/dash/public/assets/dash-agents-param-slug-platforms-265ba13db32a460d.css +1 -0
  81. package/dist/dash/public/assets/dash-agents-param-slug-settings-8afc52eb0372bc4b.css +1 -0
  82. package/dist/dash/public/assets/dash-agents-param-slug-tasks-6193e646be68dbd2.css +1 -0
  83. package/dist/dash/public/assets/dash-agents-param-slug-usage-ceadb3a1c911c402.css +1 -0
  84. package/dist/dash/public/assets/dash-settings-cf1997cd7f72e518.css +1 -0
  85. package/dist/dash/public/assets/dash-settings-param-tab-b44dac8e79a560e6.css +1 -0
  86. package/dist/dash/public/assets/dash-tasks-916c7100a8058aa3.css +1 -0
  87. package/dist/dash/public/assets/dash-tasks-param-id-194a6ffc20e872f2.css +1 -0
  88. package/dist/dash/public/assets/dash-usage-40940b289f85e0b6.css +1 -0
  89. package/dist/dash/public/assets/pnext-client-13aee92bef5c9e3f.js +1 -0
  90. package/dist/dash/public/assets/pnext-client-1f2686b2044f0df6.js +1 -0
  91. package/dist/dash/public/assets/pnext-client-26130234ebad7067.js +1 -0
  92. package/dist/dash/public/assets/pnext-client-311e35b0076bf406.js +1 -0
  93. package/dist/dash/public/assets/pnext-client-4ade1bf75c9c3d87.js +1 -0
  94. package/dist/dash/public/assets/pnext-client-61cd31b687ede1da.js +1 -0
  95. package/dist/dash/public/assets/pnext-client-61db65dc4223217a.js +1 -0
  96. package/dist/dash/public/assets/pnext-client-6ea656e7705e3f2a.js +1 -0
  97. package/dist/dash/public/assets/pnext-client-7fd0f793f0322cb9.js +1 -0
  98. package/dist/dash/public/assets/pnext-client-88159c0840fcfa4a.js +1 -0
  99. package/dist/dash/public/assets/pnext-client-b03f0c47c066b4f3.js +1 -0
  100. package/dist/dash/public/assets/{pnext-client-1b9ab0963a8ff77b.js → pnext-client-d6478835f543f901.js} +1 -1
  101. package/dist/dash/public/assets/pnext-client-dadb8359021ed590.js +1 -0
  102. package/dist/dash/public/assets/pnext-client-de07532d6aaebf2c.js +1 -0
  103. package/dist/dash/public/assets/pnext-client-f87a31890c8ff224.js +1 -0
  104. package/dist/dash/public/assets/pnext-client-ffe1c123a8e2a350.js +1 -0
  105. package/dist/dash/server/bundle-0.1.6-b29ec45cb963d47f/entry.js +2 -0
  106. package/dist/{default-vYPT26DR.js → default-BMvUMTFp.js} +1 -1
  107. package/dist/default-B_nKaX0-.js +1 -0
  108. package/dist/{delete-D8gEwoOj.js → delete-Bjteibld.js} +2 -2
  109. package/dist/{delete-z1_86nf6.js → delete-riCIKZwj.js} +2 -2
  110. package/dist/{disable-DVKd-0Xb.js → disable-DQC56YFy.js} +1 -1
  111. package/dist/{discover-Dvr_3_Xr.js → discover-Qanr5TEO.js} +1 -1
  112. package/dist/{discover-Df-a8K4R.js → discover-lgTYwoQp.js} +2 -2
  113. package/dist/dispatch-BsW_1m7e.js +14 -0
  114. package/dist/{docs-DKJnqZqt.js → docs-DFs7lOJp.js} +1 -1
  115. package/dist/{docs-BX65z2Iu.js → docs-DKROso-4.js} +2 -2
  116. package/dist/{enable-C8Zf_wpN.js → enable-D2DqZvcU.js} +1 -1
  117. package/dist/{env-CRf6wkM2.js → env-YRY3I60r.js} +1 -1
  118. package/dist/exec-DuUkLMqg.js +11 -0
  119. package/dist/executor-vMppVLB6.js +10 -0
  120. package/dist/flow/builtin/review.js +1 -1
  121. package/dist/flow/index.d.ts +1 -1
  122. package/dist/flow/index.js +1 -1
  123. package/dist/{flow-17k25MHt.js → flow-DmERAnR8.js} +1 -1
  124. package/dist/{github-actions-DwUzm4hC.js → github-actions-C_NkHVJq.js} +1 -1
  125. package/dist/http-D01Udu4u.js +1 -0
  126. package/dist/{index-CZjFX5zV.d.ts → index-B1L4zXZH.d.ts} +2 -1
  127. package/dist/{init-CoMfFxbG.js → init-Bd6JHzBc.js} +1 -1
  128. package/dist/integrations-B2CavXR1.js +5 -0
  129. package/dist/integrations-D1Dwy1kt.js +1 -0
  130. package/dist/lib/broker.js +2 -2
  131. package/dist/{list-DX-z3SBp.js → list-4Uo-0v9s.js} +1 -1
  132. package/dist/{list-BAKvxSbh.js → list-B0ujEZhd.js} +3 -3
  133. package/dist/{list-BYiG8Zav.js → list-B5oa1Bhy.js} +2 -2
  134. package/dist/{list-BxEywhHU.js → list-BjE_l6la.js} +1 -1
  135. package/dist/{list-VToARh3C.js → list-Ck9ibCs1.js} +1 -1
  136. package/dist/{list-CIW2joV1.js → list-DhgK4Zc4.js} +1 -1
  137. package/dist/{list-DpCf4G-H.js → list-KvL7AUhD.js} +2 -2
  138. package/dist/{list-C5w01-8q.js → list-byYLBl1U.js} +1 -1
  139. package/dist/{load-BpEsAn51.js → load-SnZd7xG1.js} +1 -1
  140. package/dist/{local-H3uvqEbq.js → local-BRy1EUq2.js} +1 -1
  141. package/dist/log-view-DeJstSkw.js +3 -0
  142. package/dist/{login-DkkslfBx.js → login-C-ahtDKV.js} +1 -1
  143. package/dist/{login-BFxX4jJy.js → login-zpwke8g1.js} +1 -1
  144. package/dist/{logout-BHAtw4Z8.js → logout-CI6Kf-T6.js} +1 -1
  145. package/dist/{maintenance-C5FklUw3.js → maintenance-BAzr9Jlr.js} +1 -1
  146. package/dist/{match-CXAjDhg7.js → match-DxV1d49D.js} +1 -1
  147. package/dist/{mcp-BgKb2Cdk.js → mcp-CmcL1MCG.js} +1 -1
  148. package/dist/{mcp-CCZOAo06.js → mcp-mhXwtGQT.js} +1 -1
  149. package/dist/{migrate-N-85_N7J.js → migrate-Czh3TEK5.js} +1 -1
  150. package/dist/{migrate-C4vM7DQN.js → migrate-w7KfzUAR.js} +1 -1
  151. package/dist/{model-Bs1StDo3.js → model-JRoTH9QK.js} +1 -1
  152. package/dist/models-C-S7HqSb.js +1 -0
  153. package/dist/{prompt-CEuBMNhk.js → prompt-CMXmt2l9.js} +4 -4
  154. package/dist/{push-CUC55Bqd.js → push-B8BhTyZs.js} +1 -1
  155. package/dist/queue-CirjEskI.js +117 -0
  156. package/dist/refresh-Bmy2r1Ee.js +1 -0
  157. package/dist/{remote-miQNTVRa.js → remote-CxFnEjZl.js} +1 -1
  158. package/dist/{remove-B93LNkZI.js → remove-C15WLaXT.js} +1 -1
  159. package/dist/{remove-C5_xggx7.js → remove-CTZYjjSr.js} +1 -1
  160. package/dist/{remove-eQ8y5C5p.js → remove-CTl-H0f0.js} +1 -1
  161. package/dist/{remove-CuH8a3L9.js → remove-DX2FVo5b.js} +1 -1
  162. package/dist/rename-D1g4TnHQ.js +1 -0
  163. package/dist/{result-BEtQZHaY.js → result-D49VN8PM.js} +3 -3
  164. package/dist/result-DGWyUHYU.js +18 -0
  165. package/dist/{resume-DYzKvyEw.js → resume-ywzc7z6A.js} +2 -2
  166. package/dist/{review-oIBeECZE.js → review-ZC0xG8XY.js} +2 -2
  167. package/dist/{rotate-key-IdMYH7gg.js → rotate-key-Bf8_99ck.js} +1 -1
  168. package/dist/{run-CTtd0Znc.js → run-D7SJ-ieT.js} +2 -2
  169. package/dist/run-DoRQSC_K.js +2 -0
  170. package/dist/{run-CawRGnOg.js → run-ukCf2Mdy.js} +1 -1
  171. package/dist/runner-D-REfOUe.js +1 -0
  172. package/dist/{runner-sUpWJ_k0.js → runner-wDYhkVsM.js} +1 -1
  173. package/dist/schema-DoNO0dPh.js +1 -0
  174. package/dist/sdk.d.ts +4 -2
  175. package/dist/sdk.js +1 -1
  176. package/dist/serve-BJ_Mbm2m.js +8 -0
  177. package/dist/{serve-CPnmrKyf.js → serve-C1MMhM1F.js} +2 -2
  178. package/dist/{serve-GmRO89s5.js → serve-DtrOHaoS.js} +2 -2
  179. package/dist/{serve-CE8YV2e9.js → serve-P9pFkEnE.js} +1 -1
  180. package/dist/{serve-BTeomYxW.js → serve-yjSw0B1a.js} +1 -1
  181. package/dist/{server-Kb22cWfF.js → server-DDWTnmbx.js} +1 -1
  182. package/dist/{setup-host-GFTUzzZJ.js → setup-host-L6Rnn5Cz.js} +1 -1
  183. package/dist/{shared-DVtzWhUT.js → shared-XsUX_ZZb.js} +1 -1
  184. package/dist/{show-COTZEYgc.js → show-CU-jqJ90.js} +1 -1
  185. package/dist/{show-CMUg8SJu.js → show-DVJmB2u-.js} +1 -1
  186. package/dist/{status-D26U--p8.js → status-CBydR7LE.js} +1 -1
  187. package/dist/{steer-C6uARLp5.js → steer-Cm_sHZqs.js} +2 -2
  188. package/dist/{stop-BrGI3hSS.js → stop-DJS7DAPJ.js} +1 -1
  189. package/dist/{stop-ByCAlbmN.js → stop-TrebIIuv.js} +2 -2
  190. package/dist/store-BrNMqJgk.js +42 -0
  191. package/dist/store-j8icrG0v.js +19 -0
  192. package/dist/task-C8lmcNsf.js +1 -0
  193. package/dist/{task-DeglY79z.js → task-CpUI19o2.js} +4 -4
  194. package/dist/task-CvSqiwxo.js +1 -0
  195. package/dist/{test-NEbhlxvo.js → test-CDKz4jY_.js} +1 -1
  196. package/dist/{tools-D0Zfs8Hx.js → tools-DBFeoosk.js} +1 -1
  197. package/dist/{types-I5Cwtl_K.d.ts → types-DMGBYB_K.d.ts} +3 -1
  198. package/dist/{unalias-C9NqmSDq.js → unalias-BfQFBnuK.js} +1 -1
  199. package/dist/{update-BTxYQj7Z.js → update-C4FFOie8.js} +1 -1
  200. package/dist/{upgrade-hA0yWBvx.js → upgrade-DLJqMnUQ.js} +2 -2
  201. package/dist/{usage-BhqoCjvE.js → usage-CSh-xwQ2.js} +1 -1
  202. package/dist/{usage-C4d9X2Uq.js → usage-D8mc4qXK.js} +1 -1
  203. package/dist/{watch-DtRuC0Wc.js → watch-CQu-tJKZ.js} +2 -2
  204. package/dist/{watch-CSdj24ct.js → watch-DmwaEvoi.js} +2 -2
  205. package/dist/{wire-BeT1Axeo.js → wire-DzGLZ3-O.js} +1 -1
  206. package/dist/worker-BLKTMLqa.js +1 -0
  207. package/dist/worker-nHemduM_.js +9 -0
  208. package/dist/{workflow-Dcmdooi8.js → workflow-IrouAus_.js} +1 -1
  209. package/docs/cli.md +145 -0
  210. package/docs/mcp.md +1 -1
  211. package/migrations/0000_initial.sql +8 -0
  212. package/migrations/meta/0000_snapshot.json +75 -1
  213. package/migrations/meta/_journal.json +1 -1
  214. package/package.json +3 -2
  215. package/plugins/agents/skills/coder/SKILL.md +1 -0
  216. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  217. package/plugins/claude/skills/dispatch/SKILL.md +1 -1
  218. package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
  219. package/plugins/cursor/skills/coder/SKILL.md +1 -0
  220. package/src/client/index.ts +10 -2
  221. package/src/client/types.ts +18 -0
  222. package/src/core/defaults.ts +36 -0
  223. package/src/core/task/log-view.ts +120 -6
  224. package/src/server/dash/app/dash/agents/(list)/agent-menu.tsx +4 -0
  225. package/src/server/dash/app/dash/agents/(list)/agents-grid.tsx +18 -35
  226. package/src/server/dash/app/dash/agents/(list)/loading.tsx +7 -1
  227. package/src/server/dash/app/dash/agents/[slug]/(tabs)/layout.tsx +2 -1
  228. package/src/server/dash/app/dash/layout.css +58 -1
  229. package/src/server/dash/app/dash/layout.tsx +29 -7
  230. package/src/server/dash/app/dash/settings/[tab]/credentials-list.tsx +20 -13
  231. package/src/server/dash/app/dash/settings/[tab]/page.tsx +1 -1
  232. package/src/server/dash/app/dash/settings/[tab]/runners-list.tsx +13 -12
  233. package/src/server/dash/app/dash/tasks/[id]/activity.tsx +49 -4
  234. package/src/server/dash/app/dash/tasks/[id]/task-actions.tsx +43 -6
  235. package/src/server/dash/app/dash/tasks/[id]/task-view.css +52 -31
  236. package/src/server/dash/app/dash/tasks/[id]/task-view.tsx +143 -51
  237. package/src/server/dash/app/dash/tasks/composer.css +4 -0
  238. package/src/server/dash/app/dash/tasks/composer.tsx +6 -5
  239. package/src/server/dash/app/dash/tasks/list/task.ts +29 -1
  240. package/src/server/dash/app/dash/tasks/list/tasks-list.css +20 -7
  241. package/src/server/dash/app/dash/tasks/list/tasks-list.tsx +256 -109
  242. package/src/server/dash/app/dash/tasks/list/tasks-section.tsx +9 -13
  243. package/src/server/dash/app/dash/tasks/page.tsx +3 -35
  244. package/src/server/dash/comps/frame/loading-card.tsx +4 -3
  245. package/src/server/dash/comps/frame/page-head.css +10 -0
  246. package/src/server/dash/comps/frame/page-head.tsx +3 -2
  247. package/src/server/dash/comps/frame/task-toasts.tsx +196 -50
  248. package/src/server/dash/comps/nav/side-toggle.tsx +16 -5
  249. package/src/server/dash/comps/ui/card.css +1 -0
  250. package/src/server/dash/comps/ui/filter.css +64 -0
  251. package/src/server/dash/comps/ui/filter.tsx +336 -0
  252. package/src/server/dash/comps/ui/icons.ts +2 -0
  253. package/src/server/dash/comps/ui/menu.tsx +9 -1
  254. package/src/server/dash/comps/ui/select.tsx +2 -2
  255. package/src/server/dash/comps/ui/toast.css +3 -2
  256. package/src/server/dash/comps/ui/toolbar.css +14 -0
  257. package/src/server/dash/utils/client.ts +21 -0
  258. package/src/server/dash/utils/format.ts +12 -4
  259. package/src/server/dash/utils/paged.ts +14 -6
  260. package/dist/actions-Mk3FSlpJ.js +0 -20
  261. package/dist/archive-sweep-CtBcARgN.js +0 -1
  262. package/dist/auth-BFny--17.js +0 -1
  263. package/dist/auth-DWHIzqbA.js +0 -1
  264. package/dist/credentials-B1jv6dkg.js +0 -1
  265. package/dist/dash/public/assets/chunks/agents-grid-951584b6f4c4314e.js +0 -1
  266. package/dist/dash/public/assets/chunks/composer-5534be50bc12385e.js +0 -1
  267. package/dist/dash/public/assets/chunks/composer-d10d4b1d54c4f751.js +0 -1
  268. package/dist/dash/public/assets/chunks/credentials-list-bc5bfcab058176c5.js +0 -2
  269. package/dist/dash/public/assets/chunks/format-eb03fc9d9744d960.js +0 -1
  270. package/dist/dash/public/assets/chunks/heads-f3bcd289663d48fa.js +0 -1
  271. package/dist/dash/public/assets/chunks/instructions-d0a4b0d95009c262.js +0 -1
  272. package/dist/dash/public/assets/chunks/paged-bc6fa0c2b9d52ed7.js +0 -1
  273. package/dist/dash/public/assets/chunks/runners-list-1901af8a5dea1f09.js +0 -1
  274. package/dist/dash/public/assets/chunks/shared-init-88cce66bb3cfe1e5.js +0 -5
  275. package/dist/dash/public/assets/chunks/shared-init-ffb3397e494137c9.js +0 -2
  276. package/dist/dash/public/assets/chunks/task-actions-8e1d93c788d001df.js +0 -1
  277. package/dist/dash/public/assets/chunks/task-fa8b82e95e771c4a.js +0 -1
  278. package/dist/dash/public/assets/chunks/task-view-6d7618d2820a5cb4.js +0 -3
  279. package/dist/dash/public/assets/chunks/tasks-list-f4784e5cbd0ed785.js +0 -1
  280. package/dist/dash/public/assets/dash-9e02d5d1a6d42ee3.css +0 -1
  281. package/dist/dash/public/assets/dash-account-04859899c1838ada.css +0 -1
  282. package/dist/dash/public/assets/dash-agents-9e678137267dec07.css +0 -1
  283. package/dist/dash/public/assets/dash-agents-new-26fa27b17ef9b8d8.css +0 -1
  284. package/dist/dash/public/assets/dash-agents-param-slug-b975c48f8abf3444.css +0 -1
  285. package/dist/dash/public/assets/dash-agents-param-slug-edit-e13fda599bb04843.css +0 -1
  286. package/dist/dash/public/assets/dash-agents-param-slug-platforms-70292932cb27bc96.css +0 -1
  287. package/dist/dash/public/assets/dash-agents-param-slug-settings-71f9a86251723fcc.css +0 -1
  288. package/dist/dash/public/assets/dash-agents-param-slug-tasks-fb8eb2ed71006601.css +0 -1
  289. package/dist/dash/public/assets/dash-agents-param-slug-usage-7c8740f1cdd52c71.css +0 -1
  290. package/dist/dash/public/assets/dash-settings-8eb818da462227e8.css +0 -1
  291. package/dist/dash/public/assets/dash-settings-param-tab-849a1a9542e273f5.css +0 -1
  292. package/dist/dash/public/assets/dash-tasks-81e510e0b6931b42.css +0 -1
  293. package/dist/dash/public/assets/dash-tasks-param-id-bbd2a83342f2ec10.css +0 -1
  294. package/dist/dash/public/assets/dash-usage-8e0259e03eaed1bd.css +0 -1
  295. package/dist/dash/public/assets/pnext-client-03b03b254e525e82.js +0 -1
  296. package/dist/dash/public/assets/pnext-client-1c7859bb888f4ef0.js +0 -1
  297. package/dist/dash/public/assets/pnext-client-3c748afe963fb05d.js +0 -1
  298. package/dist/dash/public/assets/pnext-client-4f47c2cae220f107.js +0 -1
  299. package/dist/dash/public/assets/pnext-client-6ab9fd9b53b794d2.js +0 -1
  300. package/dist/dash/public/assets/pnext-client-6c241786342498e4.js +0 -1
  301. package/dist/dash/public/assets/pnext-client-80b56d420f02b808.js +0 -1
  302. package/dist/dash/public/assets/pnext-client-83135c17bae850a3.js +0 -1
  303. package/dist/dash/public/assets/pnext-client-8e6fa4302ae0396f.js +0 -1
  304. package/dist/dash/public/assets/pnext-client-932c9d8cc0717fc8.js +0 -1
  305. package/dist/dash/public/assets/pnext-client-a42ae63d4de113f9.js +0 -1
  306. package/dist/dash/public/assets/pnext-client-d2ee6f8c1e337032.js +0 -1
  307. package/dist/dash/public/assets/pnext-client-d513f65ec23b7f20.js +0 -1
  308. package/dist/dash/public/assets/pnext-client-ecb88e2a8a109954.js +0 -1
  309. package/dist/dash/public/assets/pnext-client-f1545ee6270bbf9a.js +0 -1
  310. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/boot-B2MFTRO2.js +0 -30
  311. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/build-tier-XJJSL5FR.js +0 -2704
  312. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2HFY3DQT.js +0 -208
  313. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2ICEVELW.js +0 -834
  314. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-3UPROT2C.js +0 -35
  315. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4P5CMIYT.js +0 -1582
  316. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4TKZKZ3Q.js +0 -131
  317. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-5RDEILQB.js +0 -437
  318. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-62KEQM43.js +0 -25
  319. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-6KQSWT6D.js +0 -831
  320. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-7DCNCW6O.js +0 -724
  321. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-B4THCTQP.js +0 -22
  322. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-C2LIV2MM.js +0 -172
  323. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-CCATPVTW.js +0 -378
  324. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-COXJAPTU.js +0 -89
  325. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-E6IXL2NJ.js +0 -6270
  326. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FLGRBFAH.js +0 -1198
  327. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FTJVHFWD.js +0 -61
  328. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-H523KSIH.js +0 -5151
  329. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-HT6KKX6A.js +0 -20
  330. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JESFYECC.js +0 -438
  331. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JRPJM2XY.js +0 -17
  332. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L2IWDUXG.js +0 -107
  333. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L5P4Y4UC.js +0 -183
  334. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-NQE46AZN.js +0 -647
  335. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PA4QEARJ.js +0 -129
  336. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PZ5AXOLW.js +0 -7146
  337. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-SNVPTULL.js +0 -2397
  338. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-T3QZT3PG.js +0 -220
  339. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-VC5O2SDP.js +0 -72
  340. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-WZX4V6CM.js +0 -231
  341. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-XUUIDXY5.js +0 -627
  342. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-ZHFDO3PR.js +0 -265
  343. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/compile-CE2DN7ZA.js +0 -18
  344. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-UREY7XJT.js +0 -16
  345. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-loader-UI2XE3PZ.js +0 -17
  346. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/entry.js +0 -219
  347. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/generate-I2PPTDFD.js +0 -16
  348. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/modules-UVOCE4C2.js +0 -48
  349. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/pipeline-UW47K537.js +0 -963
  350. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/proxy-ITGOVJBW.js +0 -26
  351. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/register-4SA55SWT.js +0 -12579
  352. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/router-JNKHLBCZ.js +0 -505
  353. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/routes-PD6J2NXW.js +0 -54
  354. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/tsconfig-defaults-ZXEH7RQK.js +0 -15
  355. package/dist/default-Bobztoxf.js +0 -1
  356. package/dist/dispatch-BKeAch9E.js +0 -14
  357. package/dist/exec-RZFLclKX.js +0 -11
  358. package/dist/executor-BvAA7Cgw.js +0 -10
  359. package/dist/http-Bkg_OQ6r.js +0 -1
  360. package/dist/integrations-5xCaXSd-.js +0 -1
  361. package/dist/integrations-BAgdVDhj.js +0 -5
  362. package/dist/log-view-dg0TlKLA.js +0 -1
  363. package/dist/models-_5LVGC3m.js +0 -1
  364. package/dist/queue-6io_NOP3.js +0 -116
  365. package/dist/refresh-BIrmrWYq.js +0 -1
  366. package/dist/rename-CYfy03FE.js +0 -1
  367. package/dist/result-BzaM3inj.js +0 -15
  368. package/dist/run-BDYl0CKl.js +0 -2
  369. package/dist/runner-D7ScbIHJ.js +0 -1
  370. package/dist/schema-CU2iFkp8.js +0 -1
  371. package/dist/serve-CQKZxzFE.js +0 -4
  372. package/dist/store-B94tSate.js +0 -42
  373. package/dist/store-DzqXS2wT.js +0 -8
  374. package/dist/task-C6VAg5WU.js +0 -1
  375. package/dist/task-CgOKgkEG.js +0 -1
  376. package/dist/worker-CXndxmGF.js +0 -9
  377. package/dist/worker-DX7P9kWl.js +0 -1
  378. package/src/server/dash/app/dash/token-from-hash.tsx +0 -17
@@ -40,7 +40,7 @@ const ICONS: Record<TaskDisplayRow['kind'], string> = {
40
40
 
41
41
  /** Rows long enough to fold to one line. */
42
42
  const foldable = (row: TaskDisplayRow) =>
43
- Boolean(row.detail) || row.title.length > 120 || row.title.includes('\n');
43
+ Boolean(row.detail || row.result?.detail) || row.title.length > 120 || row.title.includes('\n');
44
44
 
45
45
  const atEnd = () => innerHeight + scrollY >= document.documentElement.scrollHeight - 48;
46
46
 
@@ -65,15 +65,43 @@ function useWindowVirtualizer(count: number, list: { current: HTMLElement | null
65
65
  return virtual;
66
66
  }
67
67
 
68
+ /** A call's or approval's outcome on the right: a spinner and the time so far, then ✓ or ✗ and how long it took. */
69
+ function CallState({ row, running }: { row: TaskDisplayRow; running: boolean }) {
70
+ const [, tick] = useReducer((n: number) => n + 1, 0);
71
+ useEffect(() => {
72
+ if (!running) return;
73
+ const timer = setInterval(() => tick(0), 1000);
74
+ return () => clearInterval(timer);
75
+ }, [running]);
76
+ const result = row.result;
77
+ const took = running ? Math.max(0, Date.now() - row.at) : result?.durationMs;
78
+ const text = [
79
+ row.state,
80
+ result && !result.ok && result.exitCode !== undefined ? `exit ${result.exitCode}` : '',
81
+ took ? formatDuration(took) : '',
82
+ ]
83
+ .filter(Boolean)
84
+ .join(' · ');
85
+
86
+ return (
87
+ <span class={`act-state${result && !result.ok ? ' bad' : ''}`}>
88
+ {running ? <i class="spin" /> : result ? <Icon d={result.ok ? iCheck : iX} /> : null}
89
+ {text}
90
+ </span>
91
+ );
92
+ }
93
+
68
94
  function Row({
69
95
  row,
70
96
  tz,
71
97
  open,
98
+ running,
72
99
  onToggle,
73
100
  }: {
74
101
  row: TaskDisplayRow;
75
102
  tz: string;
76
103
  open: boolean;
104
+ running: boolean;
77
105
  onToggle: () => void;
78
106
  }) {
79
107
  const title = useRef<HTMLSpanElement>(null);
@@ -83,6 +111,7 @@ function Row({
83
111
  }, [row.title]);
84
112
  const fold = open || clipped || foldable(row);
85
113
  const failed = row.tone === 'error';
114
+ const timed = row.kind === 'tool' || row.state !== undefined;
86
115
  const extra =
87
116
  row.kind === 'tool-result'
88
117
  ? [
@@ -100,7 +129,7 @@ function Row({
100
129
  return (
101
130
  <Tag
102
131
  type={fold ? 'button' : undefined}
103
- class={`act ${row.kind}${row.tone ? ` ${row.tone}` : ''}${row.separated ? ' sep' : ''}${open ? ' open' : ''}`}
132
+ class={`act ${row.kind}${row.tone ? ` ${row.tone}` : ''}${row.separated ? ' sep' : ''}${timed ? ' timed' : ''}${open ? ' open' : ''}`}
104
133
  aria-expanded={fold ? open : undefined}
105
134
  onClick={fold ? onToggle : undefined}
106
135
  >
@@ -108,11 +137,20 @@ function Row({
108
137
  <Icon d={row.kind === 'tool-result' && failed ? iX : ICONS[row.kind]} />
109
138
  <span class="act-text">
110
139
  <span class="act-title" ref={title}>
111
- {row.title}
140
+ {row.kind === 'tool' && row.tool && row.title.startsWith(`${row.tool} `) ? (
141
+ <>
142
+ <span class="act-tool">{row.tool}</span>
143
+ {row.title.slice(row.tool.length)}
144
+ </>
145
+ ) : (
146
+ row.title
147
+ )}
112
148
  </span>
113
149
  {extra ? <span class="act-detail">{extra}</span> : null}
114
- {row.detail ? <span class="act-detail">{row.detail}</span> : null}
150
+ {row.detail && (open || !row.state) ? <span class="act-detail">{row.detail}</span> : null}
151
+ {open && row.result?.detail ? <span class="act-detail">{row.result.detail}</span> : null}
115
152
  </span>
153
+ {timed ? <CallState row={row} running={running} /> : null}
116
154
  {fold ? <Icon d={iDown} class="act-chev" /> : null}
117
155
  </Tag>
118
156
  );
@@ -132,6 +170,8 @@ export function Activity({
132
170
  const [open, setOpen] = useState<Set<number>>(new Set());
133
171
  const [follow, setFollow] = useState(live);
134
172
  const virtual = useWindowVirtualizer(rows.length, list);
173
+ // Calls the agent has moved on from without a result are not still running.
174
+ const settled = rows.findLastIndex(row => row.kind === 'assistant' || row.kind === 'reasoning');
135
175
 
136
176
  useEffect(() => {
137
177
  const onScroll = () => setFollow(atEnd());
@@ -173,6 +213,11 @@ export function Activity({
173
213
  row={row}
174
214
  tz={tz}
175
215
  open={open.has(row.seq)}
216
+ running={
217
+ live &&
218
+ !row.result &&
219
+ (row.state !== undefined || (row.kind === 'tool' && item.index > settled))
220
+ }
176
221
  onToggle={() =>
177
222
  setOpen(value => {
178
223
  const next = new Set(value);
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState } from 'preact/hooks';
3
+ import { useEffect, useState } from 'preact/hooks';
4
4
 
5
5
  import { client } from '@/utils/client';
6
6
  import { useRouter } from '@wular/pnext/navigation/client';
@@ -12,6 +12,7 @@ import { ConfirmDialog } from '@/comps/ui/confirm';
12
12
  import { Menu, MenuItem } from '@/comps/ui/menu';
13
13
  import { Icon } from '@/comps/ui/icon';
14
14
  import { iDots } from '@/comps/ui/icons';
15
+ import { showToast, toastError } from '@/comps/frame/task-toasts';
15
16
 
16
17
  export function approvalParts(approval: unknown): {
17
18
  id?: string;
@@ -114,6 +115,14 @@ export function TaskActions({ task, live }: { task: TaskRow; live: boolean }) {
114
115
  )}
115
116
  <Menu summary={<Icon d={iDots} />} summaryClass="icon-btn" label="More actions">
116
117
  {live && !archived ? <MenuItem title="Archive" onClick={archive} /> : null}
118
+ <MenuItem
119
+ title="Copy task id"
120
+ onClick={() =>
121
+ void navigator.clipboard
122
+ .writeText(task.task.id)
123
+ .then(() => showToast('Copied', task.task.id))
124
+ }
125
+ />
117
126
  <MenuItem title="Delete" danger onClick={() => setDeleting('ask')} />
118
127
  </Menu>
119
128
  <ConfirmDialog
@@ -131,9 +140,26 @@ export function TaskActions({ task, live }: { task: TaskRow; live: boolean }) {
131
140
 
132
141
  export function Approval({ task }: { task: TaskRow }) {
133
142
  const action = useAction(task.task.id);
143
+ const [busy, setBusy] = useState<'accept' | 'decline'>();
144
+ // A sent decision hides its card at once; the events stream catches up after.
145
+ const [decided, setDecided] = useState<string>();
134
146
  const { id, action: tool, reason } = approvalParts(task.approval);
135
- const decide = (decision: 'accept' | 'decline') =>
136
- void action.run('approve', { approvalId: id, decision });
147
+
148
+ const decide = async (decision: 'accept' | 'decline') => {
149
+ setBusy(decision);
150
+ const ok = await action.run('approve', { approvalId: id, decision });
151
+ setBusy(undefined);
152
+ if (!ok) return;
153
+ setDecided(id);
154
+ showToast(decision === 'accept' ? 'Allowed' : 'Denied', 'The task continues.');
155
+ };
156
+
157
+ useEffect(() => {
158
+ if (action.error) toastError('Not sent', action.error);
159
+ }, [action.error]);
160
+
161
+ if (id && decided === id) return null;
162
+
137
163
  return (
138
164
  <Card
139
165
  tone="accent"
@@ -141,11 +167,22 @@ export function Approval({ task }: { task: TaskRow }) {
141
167
  actions={
142
168
  task.task.flow === 'default' ? (
143
169
  <>
144
- <Flash {...action} />
145
- <button type="button" class="btn outline sm" onClick={() => decide('decline')}>
170
+ <button
171
+ type="button"
172
+ class="btn outline sm"
173
+ disabled={Boolean(busy)}
174
+ aria-busy={busy === 'decline'}
175
+ onClick={() => void decide('decline')}
176
+ >
146
177
  Deny
147
178
  </button>
148
- <button type="button" class="btn sm" onClick={() => decide('accept')}>
179
+ <button
180
+ type="button"
181
+ class="btn sm"
182
+ disabled={Boolean(busy)}
183
+ aria-busy={busy === 'accept'}
184
+ onClick={() => void decide('accept')}
185
+ >
149
186
  Allow
150
187
  </button>
151
188
  </>
@@ -1,60 +1,52 @@
1
- /* task head: the id line under the title */
2
- .head .lead:has(.task-id) {
1
+ /* task head: the facts line under the title */
2
+ .head .lead:has(.fact) {
3
3
  display: flex;
4
4
  align-items: center;
5
- gap: 12px;
6
- }
7
- .head.stuck .lead:has(.task-id) {
8
- display: none;
9
- }
10
- .task-id {
5
+ gap: 16px;
11
6
  font-size: 12px;
12
- overflow: hidden;
13
- text-overflow: ellipsis;
14
7
  }
15
- .head .copy {
16
- display: inline-grid;
17
- place-items: center;
18
- flex: none;
19
- margin-left: -8px;
20
- padding: 2px;
21
- border: 0;
22
- background: none;
8
+ .fact {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: 5px;
12
+ white-space: nowrap;
13
+ }
14
+ .fact .ic {
15
+ width: 12px;
16
+ height: 12px;
23
17
  color: var(--dim);
24
- cursor: pointer;
25
18
  }
26
- .head .copy:hover {
27
- color: var(--fg);
19
+ .fact-dim {
20
+ color: var(--dim);
28
21
  }
29
- .head .copy .ic {
30
- width: 13px;
31
- height: 13px;
22
+ .head.stuck .lead:has(.fact) {
23
+ display: none;
32
24
  }
33
25
  /* phones: the actions sit on the back link's row, the title gets the full width under it */
34
26
  @media (max-width: 760px) {
35
- .head:has(.task-id) .back {
27
+ .head:has(.fact) .back {
36
28
  height: 34px;
37
29
  }
38
- .head:has(.task-id) .title-row > h1 {
30
+ .head:has(.fact) .title-row > h1 {
39
31
  flex-basis: 100%;
40
32
  }
41
- .head:has(.task-id) .actions {
33
+ .head:has(.fact) .actions {
42
34
  position: absolute;
43
35
  top: 0;
44
36
  right: 16px;
45
37
  width: auto;
46
38
  flex-wrap: nowrap;
47
39
  }
48
- .head.stuck:has(.task-id) .actions {
40
+ .head.stuck:has(.fact) .actions {
49
41
  position: static;
50
42
  }
51
- .head.stuck:has(.task-id) .actions > .menu {
43
+ .head.stuck:has(.fact) .actions > .menu {
52
44
  display: inline;
53
45
  }
54
- .head.stuck:has(.task-id) .title-row > h1 {
46
+ .head.stuck:has(.fact) .title-row > h1 {
55
47
  flex-basis: auto;
56
48
  }
57
- .head.stuck:has(.task-id) .meta {
49
+ .head.stuck:has(.fact) .meta {
58
50
  display: none;
59
51
  }
60
52
  }
@@ -196,6 +188,9 @@ button.act:hover {
196
188
  flex: 1 1 0;
197
189
  color: var(--dim);
198
190
  }
191
+ .act-tool {
192
+ color: var(--fg);
193
+ }
199
194
  .act.open .act-text {
200
195
  display: grid;
201
196
  gap: 2px;
@@ -228,6 +223,32 @@ button.act:hover {
228
223
  .act.error :is(.act-title, .ic) {
229
224
  color: var(--bad);
230
225
  }
226
+ .act.timed {
227
+ grid-template-columns: 76px 16px minmax(0, 1fr) auto 14px;
228
+ }
229
+ .act-state {
230
+ color: var(--dim);
231
+ font-size: 11px;
232
+ font-variant-numeric: tabular-nums;
233
+ white-space: nowrap;
234
+ }
235
+ .act-state.bad,
236
+ .act-state.bad .ic {
237
+ color: var(--bad);
238
+ }
239
+ .act-state :is(.ic, .spin) {
240
+ display: inline-block;
241
+ width: 11px;
242
+ height: 11px;
243
+ margin: 0 6px 0 0;
244
+ vertical-align: -1px;
245
+ }
246
+ .act-state .spin {
247
+ border: 1.5px solid currentColor;
248
+ border-top-color: transparent;
249
+ border-radius: 50%;
250
+ animation: spin 0.8s linear infinite;
251
+ }
231
252
  .act-empty {
232
253
  padding: 24px 0;
233
254
  text-align: center;
@@ -12,7 +12,7 @@ import { formatDate, formatDuration, reasonText } from '@/utils/format';
12
12
  import { Link } from '@wular/pnext/link';
13
13
  import { useSearchParams } from '@wular/pnext/navigation/client';
14
14
  import type { TaskLog, TaskRow } from '@coder/client/types';
15
- import { iAgent, iCheck, iCopy, iDown } from '@/comps/ui/icons';
15
+ import { iAgent, iCheck, iClock, iDown, iFolder, iPlug, iTerminal } from '@/comps/ui/icons';
16
16
  import { Badge } from '@/comps/ui/badge';
17
17
  import { Card, Loading } from '@/comps/ui/card';
18
18
  import { Collapse } from '@/comps/ui/collapse';
@@ -25,7 +25,7 @@ import { Status } from '@/app/dash/tasks/list/status';
25
25
  import { folderName, sourceLabel, taskActive, taskTitle } from '@/app/dash/tasks/list/task';
26
26
  import { Approval, TaskActions, useAction } from './task-actions';
27
27
  import { Activity } from './activity';
28
- import { toastError, watchTask } from '@/comps/frame/task-toasts';
28
+ import { toastError, useTaskEvents, watchTask } from '@/comps/frame/task-toasts';
29
29
 
30
30
  const TABS = ['Conversation', 'Activity'];
31
31
 
@@ -104,30 +104,10 @@ function TaskBack() {
104
104
  );
105
105
  }
106
106
 
107
- function CopyId({ id }: { id: string }) {
108
- const [copied, setCopied] = useState(false);
109
- const label = copied ? 'Copied' : 'Copy task id';
110
- useEffect(() => {
111
- if (!copied) return;
112
- const timer = setTimeout(() => setCopied(false), 1500);
113
- return () => clearTimeout(timer);
114
- }, [copied]);
115
- return (
116
- <button
117
- type="button"
118
- class={copied ? 'copy copied' : 'copy'}
119
- aria-label={label}
120
- title={label}
121
- onClick={() => void navigator.clipboard.writeText(id).then(() => setCopied(true))}
122
- >
123
- <Icon d={copied ? iCheck : iCopy} />
124
- </button>
125
- );
126
- }
127
-
128
- /** The task's one-row head: back, title, status, source and agent, its actions, and a muted line with id, created time and duration. */
107
+ /** The task's one-row head: back, title, status, source and agent, its actions, and a muted line with created time, source, folder, and the agent with its model. */
129
108
  function TaskHead({ task, tz }: { task: TaskRow; tz: string }) {
130
109
  const live = taskActive(task);
110
+ const { engine, model, effort } = { ...task.task.definition, ...task.task.usage };
131
111
  const source = task.task.event?.integration ?? task.task.source;
132
112
  const kind = task.task.event?.type ?? (task.task.flow !== 'default' ? task.task.flow : undefined);
133
113
 
@@ -143,23 +123,39 @@ function TaskHead({ task, tz }: { task: TaskRow; tz: string }) {
143
123
  }
144
124
  lead={
145
125
  <>
146
- <span class="task-id">{task.task.id}</span>
147
- <CopyId id={task.task.id} />
148
- <span>{formatDate(task.createdAt, tz)}</span>
149
- {task.startedAt && task.finishedAt ? (
150
- <span>{formatDuration(task.finishedAt - task.startedAt)}</span>
126
+ <span class="fact" title={`Created ${formatDate(task.createdAt, tz)}`}>
127
+ <Icon d={iClock} />
128
+ {formatDate(task.createdAt, tz, true)}
129
+ {task.startedAt && task.finishedAt
130
+ ? `, ran ${formatDuration(task.finishedAt - task.startedAt)}`
131
+ : null}
132
+ </span>
133
+ <span class="fact" title="Source">
134
+ <Icon d={task.task.event ? iPlug : iTerminal} />
135
+ {kind ? `${sourceLabel(source)} ${kind}` : sourceLabel(source)}
136
+ </span>
137
+ {task.task.cwd ? (
138
+ <span class="fact" title={task.task.cwd}>
139
+ <Icon d={iFolder} />
140
+ {folderName(task.task.cwd)}
141
+ </span>
151
142
  ) : null}
152
- <span>{kind ? `${sourceLabel(source)} ${kind}` : sourceLabel(source)}</span>
153
- {task.task.cwd ? <span title={task.task.cwd}>{folderName(task.task.cwd)}</span> : null}
154
- <Link
155
- class="agent-link"
156
- href="/dash/agents/[slug]"
157
- params={{ slug: task.task.agent }}
158
- search={{ from: `/dash/tasks/${task.task.id}` }}
159
- >
160
- <Icon d={iAgent} />
161
- {task.task.agent}
162
- </Link>
143
+ <span class="fact">
144
+ <Link
145
+ class="agent-link"
146
+ href="/dash/agents/[slug]"
147
+ params={{ slug: task.task.agent }}
148
+ search={{ from: `/dash/tasks/${task.task.id}` }}
149
+ >
150
+ <Icon d={iAgent} />
151
+ {task.task.agent}
152
+ </Link>
153
+ {engine ? (
154
+ <span class="fact-dim" title="Engine, model and effort">
155
+ {[engine, model, effort].filter(Boolean).join('/')}
156
+ </span>
157
+ ) : null}
158
+ </span>
163
159
  </>
164
160
  }
165
161
  actions={<TaskActions task={task} live={live} />}
@@ -167,12 +163,16 @@ function TaskHead({ task, tz }: { task: TaskRow; tz: string }) {
167
163
  );
168
164
  }
169
165
 
170
- /** Display rows for the lines so far; new lines only extend them. */
166
+ /** Display rows for the lines so far; new lines extend them or complete an earlier call. */
171
167
  function useDisplayRows(lines: TaskLog[]) {
172
168
  const view = useRef<{ log: LogView; rows: TaskDisplayRow[]; seen: number }>();
173
169
  return useMemo(() => {
174
170
  const state = (view.current ??= { log: new LogView(), rows: [], seen: 0 });
175
- for (const line of lines.slice(state.seen)) state.rows.push(...state.log.reduce(line));
171
+ for (const line of lines.slice(state.seen))
172
+ for (const row of state.log.reduce(line)) {
173
+ if (row.seq > (state.rows.at(-1)?.seq ?? -1)) state.rows.push(row);
174
+ else state.rows[state.rows.findLastIndex(({ seq }) => seq === row.seq)] = row;
175
+ }
176
176
  state.seen = lines.length;
177
177
  return [...state.rows];
178
178
  }, [lines]);
@@ -214,17 +214,107 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
214
214
  if (action.error) toastError('Not sent', action.error);
215
215
  }, [action.error]);
216
216
 
217
+ const pending = useRef<TaskLog[]>([]);
217
218
  const append = (more: TaskLog[]) => {
218
219
  const fresh = more.filter(line => line.seq > after.current);
219
220
  if (!fresh.length) return;
220
221
  after.current = fresh.at(-1)!.seq;
221
- setLines(value => [...value, ...fresh]);
222
+ // Lines that land together render once.
223
+ if (pending.current.push(...fresh) === fresh.length)
224
+ setTimeout(() => {
225
+ const batch = pending.current.splice(0);
226
+ setLines(value => [...value, ...batch]);
227
+ });
222
228
  };
223
229
 
224
230
  useEffect(() => {
225
231
  if (live && replies) input.current?.focus();
226
232
  }, []);
227
233
 
234
+ const [visible, setVisible] = useState(true);
235
+ useEffect(() => {
236
+ const sync = () => setVisible(document.visibilityState === 'visible');
237
+ sync();
238
+ addEventListener('visibilitychange', sync);
239
+ return () => removeEventListener('visibilitychange', sync);
240
+ }, []);
241
+
242
+ // Logs stream from when the task runs until the stream ends them, which waits for logs still arriving.
243
+ const [streaming, setStreaming] = useState(live);
244
+ useEffect(() => {
245
+ if (live) setStreaming(true);
246
+ }, [live]);
247
+
248
+ // Each turn's reads apply to it alone: a continue, here or from elsewhere, starts the next.
249
+ const [round, setRound] = useState(0);
250
+ const roundNow = useRef(0);
251
+ roundNow.current = round;
252
+
253
+ const reload = () => {
254
+ const started = roundNow.current;
255
+ void client.tasks
256
+ .get(task.task.id)
257
+ .then(({ logs: _logs, ...full }) => {
258
+ if (roundNow.current === started) setTask(full);
259
+ })
260
+ .catch(reason => setStatus(reasonText(reason)));
261
+ };
262
+
263
+ // A running stream follows its own cursor; a stopped page fetches the lines it missed.
264
+ const catchUp = () => {
265
+ reload();
266
+ if (!streaming) setComplete(false);
267
+ };
268
+
269
+ // A new turn's prompt, from this page or elsewhere; its state comes from the events stream.
270
+ useEffect(() => {
271
+ if (!round) return;
272
+ void client.tasks
273
+ .get(task.task.id)
274
+ .then(({ turns }) => {
275
+ if (roundNow.current === round)
276
+ setTask(current => (taskActive(current) ? { ...current, turns } : current));
277
+ })
278
+ .catch(() => {});
279
+ }, [round]);
280
+
281
+ // The head follows the browser's events stream; a finish brings the full task.
282
+ useTaskEvents(
283
+ event => {
284
+ if (event.id !== task.task.id || event.deleted) return;
285
+ if (live && !taskActive(event)) reload();
286
+ // A new run, even one that finished and was continued between two events.
287
+ const rerun = live && task.startedAt !== undefined && event.startedAt !== task.startedAt;
288
+ if (taskActive(event) && (!live || rerun)) {
289
+ setRound(value => value + 1);
290
+ setStreaming(true);
291
+ }
292
+ // A turn continued and finished elsewhere while this page heard nothing of it.
293
+ if (!live && !taskActive(event) && event.finishedAt !== task.finishedAt) {
294
+ reload();
295
+ setComplete(false);
296
+ }
297
+ setTask(current => ({
298
+ ...current,
299
+ status: event.status,
300
+ approval: event.approval ?? undefined,
301
+ archivedAt: event.archivedAt,
302
+ startedAt: event.startedAt,
303
+ finishedAt: event.finishedAt,
304
+ ...(event.result === undefined
305
+ ? {}
306
+ : {
307
+ result: event.result ?? undefined,
308
+ error: event.error ?? undefined,
309
+ answer: event.answer ?? undefined,
310
+ }),
311
+ }));
312
+ },
313
+ // Missed events: refetch the task and catch up its logs from the cursor.
314
+ catchUp,
315
+ () => document.visibilityState === 'visible' && catchUp(),
316
+ );
317
+
228
318
  // Older lines a chunk at a time, then the stream follows the run.
229
319
  useEffect(() => {
230
320
  if (complete) return;
@@ -237,8 +327,9 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
237
327
  .catch(reason => setStatus(reasonText(reason)));
238
328
  }, [complete, lines.length]);
239
329
 
330
+ // A hidden tab holds no log stream and resumes from its cursor.
240
331
  useEffect(() => {
241
- if (!complete || !live) return;
332
+ if (!complete || !streaming || !visible) return;
242
333
  const abort = new AbortController();
243
334
  const follow = async () => {
244
335
  while (!abort.signal.aborted) {
@@ -250,10 +341,9 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
250
341
  }
251
342
  setStatus('');
252
343
  for await (const event of serverEvents(response.body)) {
344
+ if (abort.signal.aborted) return;
253
345
  if (event.event === 'log') append([JSON.parse(event.data)]);
254
- else if (event.event === 'status')
255
- setTask(current => ({ ...current, ...JSON.parse(event.data) }));
256
- else if (event.event === 'end') return;
346
+ else if (event.event === 'end') return setStreaming(false);
257
347
  }
258
348
  } catch {
259
349
  if (abort.signal.aborted) return;
@@ -264,7 +354,7 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
264
354
  };
265
355
  void follow();
266
356
  return () => abort.abort();
267
- }, [complete, live]);
357
+ }, [complete, streaming, visible, round]);
268
358
 
269
359
  const turns = task.turns ?? [
270
360
  {
@@ -295,12 +385,14 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
295
385
  const ok = await action.run(kind, { text }).finally(() => setSending(false));
296
386
  if (!ok) return;
297
387
  input.current!.value = '';
298
- if (kind === 'continue')
388
+ if (kind === 'continue') {
389
+ setRound(value => value + 1);
299
390
  setTask(current => ({
300
391
  ...current,
301
392
  status: 'queued',
302
393
  result: undefined,
303
394
  error: undefined,
395
+ startedAt: undefined,
304
396
  finishedAt: undefined,
305
397
  turns: [
306
398
  ...turns.map((turn, index) =>
@@ -311,7 +403,7 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
311
403
  { prompt: text },
312
404
  ],
313
405
  }));
314
- else {
406
+ } else {
315
407
  setSent(value => [...value, { kind, text, turn: turns.length - 1 }]);
316
408
  setTab('activity');
317
409
  }
@@ -332,6 +424,7 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
332
424
  {fallback.engine} was unavailable. {fallback.detail} This task is using {fallback.next}.
333
425
  </p>
334
426
  ))}
427
+ {task.approval ? <Approval task={task} /> : null}
335
428
  <nav class="tabs" role="tablist" aria-label="Task">
336
429
  {TABS.map(label => {
337
430
  const key = label.toLowerCase() as Tab;
@@ -366,7 +459,6 @@ export function TaskView({ initial, tz }: { initial: TaskRow; tz: string }) {
366
459
  <p class="who">Prompt</p>
367
460
  <Collapse class="prose">{turn.prompt}</Collapse>
368
461
  </div>
369
- {last && task.approval ? <Approval task={task} /> : null}
370
462
  {last
371
463
  ? (task.answer ?? []).slice(0, knownAnswers.current).map((answer, index) => (
372
464
  <div key={`a${index}`} class="turn answer">
@@ -216,6 +216,10 @@ dialog.compose-dialog::backdrop {
216
216
  height: 30px;
217
217
  margin-left: auto;
218
218
  }
219
+ /* the dialog's prompt grows to 20 lines, short of the dialog's own scroll */
220
+ .compose-dialog .composer textarea {
221
+ max-height: min(20lh, max(4lh, calc(86dvh - 180px)));
222
+ }
219
223
  .compose-dialog .composer,
220
224
  .compose-dialog > .loading,
221
225
  .compose-dialog > .flash,
@@ -823,8 +823,8 @@ export function LoadingComposer() {
823
823
  );
824
824
  }
825
825
 
826
- /** Opens the composer in a dialog with `agent` picked; starting a task opens it. */
827
- export function NewTaskButton({ agent }: { agent: string }) {
826
+ /** Opens the composer in a dialog, with `agent` picked when given; starting a task opens it. */
827
+ export function NewTaskButton({ agent }: { agent?: string }) {
828
828
  const dialog = useRef<HTMLDialogElement>(null);
829
829
  const [setup, setSetup] = useState<Awaited<ReturnType<typeof loadComposer>>>();
830
830
  const [error, setError] = useState('');
@@ -852,18 +852,19 @@ export function NewTaskButton({ agent }: { agent: string }) {
852
852
  onPointerEnter={prepare}
853
853
  onFocus={prepare}
854
854
  >
855
+ <Icon d={iPlus} />
855
856
  New task
856
857
  </button>
857
858
  <dialog
858
859
  ref={dialog}
859
860
  class="compose-dialog"
860
- aria-label={`New task for ${agent}`}
861
+ aria-label={agent ? `New task for ${agent}` : 'New task'}
861
862
  onClose={() => setShown(false)}
862
863
  onClick={event => event.target === event.currentTarget && dialog.current?.close()}
863
864
  >
864
865
  <header>
865
866
  <h2>New task</h2>
866
- <span class="sub">{agent}</span>
867
+ {agent ? <span class="sub">{agent}</span> : null}
867
868
  <button
868
869
  type="button"
869
870
  class="icon-btn"
@@ -876,7 +877,7 @@ export function NewTaskButton({ agent }: { agent: string }) {
876
877
  {!shown ? null : setup ? (
877
878
  <Composer
878
879
  {...setup}
879
- initial={{ ...savedChoice(document.cookie), agent }}
880
+ initial={{ ...savedChoice(document.cookie), ...(agent ? { agent } : {}) }}
880
881
  tz={Intl.DateTimeFormat().resolvedOptions().timeZone}
881
882
  />
882
883
  ) : error ? (