@songsid/agend 2.3.0-beta.25 → 2.3.0-beta.26

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 (417) hide show
  1. package/dist/adapter-world.js.map +1 -1
  2. package/dist/agend-home.d.ts +5 -0
  3. package/dist/agend-home.js +7 -0
  4. package/dist/agend-home.js.map +1 -0
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +8 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-file-read.js +1 -1
  9. package/dist/agent-file-read.js.map +1 -1
  10. package/dist/agent-whats-new.js.map +1 -1
  11. package/dist/apply-job.js.map +1 -1
  12. package/dist/atomic-write.js.map +1 -1
  13. package/dist/auth-api.d.ts +5 -0
  14. package/dist/auth-api.js +21 -0
  15. package/dist/auth-api.js.map +1 -1
  16. package/dist/authority/authority-state.d.ts +4 -0
  17. package/dist/authority/authority-state.js +17 -8
  18. package/dist/authority/authority-state.js.map +1 -1
  19. package/dist/authority/session-authorizations.js.map +1 -1
  20. package/dist/authority/web-authority.d.ts +14 -6
  21. package/dist/authority/web-authority.js +19 -5
  22. package/dist/authority/web-authority.js.map +1 -1
  23. package/dist/backend/antigravity.d.ts +2 -2
  24. package/dist/backend/antigravity.js +2 -1
  25. package/dist/backend/antigravity.js.map +1 -1
  26. package/dist/backend/claude-code.js.map +1 -1
  27. package/dist/backend/codex-package.js.map +1 -1
  28. package/dist/backend/codex-session-lookup.js.map +1 -1
  29. package/dist/backend/codex.js +1 -1
  30. package/dist/backend/codex.js.map +1 -1
  31. package/dist/backend/effort-metadata.js.map +1 -1
  32. package/dist/backend/grok.js.map +1 -1
  33. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  34. package/dist/backend/kiro-kas-store.js.map +1 -1
  35. package/dist/backend/kiro-v2-store.js.map +1 -1
  36. package/dist/backend/kiro-v3-identity.js.map +1 -1
  37. package/dist/backend/kiro.js.map +1 -1
  38. package/dist/backend/launch-effort.js.map +1 -1
  39. package/dist/backend/mock.js.map +1 -1
  40. package/dist/backend/muse.js.map +1 -1
  41. package/dist/backend/opencode-permission.js.map +1 -1
  42. package/dist/backend/opencode.js.map +1 -1
  43. package/dist/backend/session-signals.js.map +1 -1
  44. package/dist/backend-availability.js.map +1 -1
  45. package/dist/backend-command.js.map +1 -1
  46. package/dist/backend-outage.js.map +1 -1
  47. package/dist/binary-probe.js.map +1 -1
  48. package/dist/bug-report.js.map +1 -1
  49. package/dist/bundled-skills.js.map +1 -1
  50. package/dist/cache-service.js +1 -1
  51. package/dist/cache-service.js.map +1 -1
  52. package/dist/channel/adapters/discord.js +29 -4
  53. package/dist/channel/adapters/discord.js.map +1 -1
  54. package/dist/channel/adapters/telegram.d.ts +6 -0
  55. package/dist/channel/adapters/telegram.js +54 -14
  56. package/dist/channel/adapters/telegram.js.map +1 -1
  57. package/dist/channel/mcp-server.js +10 -1
  58. package/dist/channel/mcp-server.js.map +1 -1
  59. package/dist/channel/mcp-tools.js +2 -2
  60. package/dist/channel/mcp-tools.js.map +1 -1
  61. package/dist/channel/message-queue.js.map +1 -1
  62. package/dist/channel/sticker-download.js.map +1 -1
  63. package/dist/channel/tool-router.js.map +1 -1
  64. package/dist/classic-channel-manager.js +3 -2
  65. package/dist/classic-channel-manager.js.map +1 -1
  66. package/dist/cli-entry.js.map +1 -1
  67. package/dist/cli-flags.js.map +1 -1
  68. package/dist/cli.js +15 -9
  69. package/dist/cli.js.map +1 -1
  70. package/dist/command-table.js.map +1 -1
  71. package/dist/compact-watch.js.map +1 -1
  72. package/dist/completion-install.js.map +1 -1
  73. package/dist/completion.js.map +1 -1
  74. package/dist/config-validator.js +18 -4
  75. package/dist/config-validator.js.map +1 -1
  76. package/dist/config.js +8 -2
  77. package/dist/config.js.map +1 -1
  78. package/dist/connection-secrets.js.map +1 -1
  79. package/dist/context-guardian.js.map +1 -1
  80. package/dist/coredump-filter.js.map +1 -1
  81. package/dist/cpu-profile.js.map +1 -1
  82. package/dist/cross-instance-envelope.js.map +1 -1
  83. package/dist/daemon.d.ts +3 -0
  84. package/dist/daemon.js +73 -3
  85. package/dist/daemon.js.map +1 -1
  86. package/dist/daily-summary.js.map +1 -1
  87. package/dist/darwin-memory.js.map +1 -1
  88. package/dist/dashboard-live.d.ts +8 -0
  89. package/dist/dashboard-live.js +35 -0
  90. package/dist/dashboard-live.js.map +1 -0
  91. package/dist/dashboard-permissions.d.ts +69 -0
  92. package/dist/dashboard-permissions.js +138 -0
  93. package/dist/dashboard-permissions.js.map +1 -0
  94. package/dist/decision-list.js.map +1 -1
  95. package/dist/delivery-consumed-watch.js.map +1 -1
  96. package/dist/delivery-outbox.js.map +1 -1
  97. package/dist/delivery-outbox.test.js.map +1 -1
  98. package/dist/delivery-reconciliation.test.js.map +1 -1
  99. package/dist/diagnostic-redaction.js.map +1 -1
  100. package/dist/doctor.js.map +1 -1
  101. package/dist/e2e-isolation.js.map +1 -1
  102. package/dist/event-log.js.map +1 -1
  103. package/dist/export-import.js +3 -2
  104. package/dist/export-import.js.map +1 -1
  105. package/dist/fleet-control-audit.js.map +1 -1
  106. package/dist/fleet-label.js.map +1 -1
  107. package/dist/fleet-lock.js +3 -2
  108. package/dist/fleet-lock.js.map +1 -1
  109. package/dist/fleet-manager.d.ts +76 -6
  110. package/dist/fleet-manager.js +479 -145
  111. package/dist/fleet-manager.js.map +1 -1
  112. package/dist/fleet-process-handlers.js +3 -2
  113. package/dist/fleet-process-handlers.js.map +1 -1
  114. package/dist/fleet-startup.d.ts +14 -0
  115. package/dist/fleet-startup.js +30 -0
  116. package/dist/fleet-startup.js.map +1 -0
  117. package/dist/fleet-traffic.js.map +1 -1
  118. package/dist/forged-envelope-scan.d.ts +0 -20
  119. package/dist/forged-envelope-scan.js +3 -2
  120. package/dist/forged-envelope-scan.js.map +1 -1
  121. package/dist/full-restart.js.map +1 -1
  122. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +2 -0
  123. package/dist/general-knowledge/skills/web-appearance/SKILL.md +2 -0
  124. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +33 -11
  125. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +42 -13
  126. package/dist/host-memory.js.map +1 -1
  127. package/dist/instance-config-impact.js +6 -0
  128. package/dist/instance-config-impact.js.map +1 -1
  129. package/dist/instance-lifecycle.js +1 -1
  130. package/dist/instance-lifecycle.js.map +1 -1
  131. package/dist/instructions.d.ts +2 -0
  132. package/dist/instructions.js +6 -2
  133. package/dist/instructions.js.map +1 -1
  134. package/dist/kiro-compact-pane.js.map +1 -1
  135. package/dist/kiro-db-reader.js.map +1 -1
  136. package/dist/kiro-engine-status.js.map +1 -1
  137. package/dist/kiro-log-gate.js.map +1 -1
  138. package/dist/kiro-transcript-lane.js.map +1 -1
  139. package/dist/locale.js +22 -0
  140. package/dist/locale.js.map +1 -1
  141. package/dist/log-service.js.map +1 -1
  142. package/dist/log-steps.js.map +1 -1
  143. package/dist/logger.js +3 -2
  144. package/dist/logger.js.map +1 -1
  145. package/dist/login-controller.js.map +1 -1
  146. package/dist/login-manager.js.map +1 -1
  147. package/dist/login-shell.js.map +1 -1
  148. package/dist/memory-pressure.js.map +1 -1
  149. package/dist/muse-usage-relay.js.map +1 -1
  150. package/dist/needs-you-hub.js.map +1 -1
  151. package/dist/needs-you-live.js.map +1 -1
  152. package/dist/network-family.js.map +1 -1
  153. package/dist/operator-control-paths.js +3 -4
  154. package/dist/operator-control-paths.js.map +1 -1
  155. package/dist/outbound-handlers.js.map +1 -1
  156. package/dist/outbound-schemas.d.ts +68 -68
  157. package/dist/outbound-schemas.js +1 -1
  158. package/dist/outbound-schemas.js.map +1 -1
  159. package/dist/outbox-open-error.js.map +1 -1
  160. package/dist/pane-context-cache.js.map +1 -1
  161. package/dist/paths.js.map +1 -1
  162. package/dist/private-dir.js.map +1 -1
  163. package/dist/probe-worker-pool.js.map +1 -1
  164. package/dist/profile-control.js.map +1 -1
  165. package/dist/prompt-file-ref.js.map +1 -1
  166. package/dist/provider-probe.js.map +1 -1
  167. package/dist/provider-secret-registry.js.map +1 -1
  168. package/dist/public-link-auto-open.d.ts +32 -0
  169. package/dist/public-link-auto-open.js +86 -0
  170. package/dist/public-link-auto-open.js.map +1 -0
  171. package/dist/public-link-owner.d.ts +10 -0
  172. package/dist/public-link-owner.js +36 -0
  173. package/dist/public-link-owner.js.map +1 -0
  174. package/dist/public-link-progress.js.map +1 -1
  175. package/dist/public-preview.js.map +1 -1
  176. package/dist/public-web-gateway.js +4 -2
  177. package/dist/public-web-gateway.js.map +1 -1
  178. package/dist/public-web-link.d.ts +35 -1
  179. package/dist/public-web-link.js +142 -43
  180. package/dist/public-web-link.js.map +1 -1
  181. package/dist/quickstart-api.js.map +1 -1
  182. package/dist/quickstart.d.ts +5 -0
  183. package/dist/quickstart.js +25 -16
  184. package/dist/quickstart.js.map +1 -1
  185. package/dist/reply-buttons-controller.js.map +1 -1
  186. package/dist/reply-dedup.js.map +1 -1
  187. package/dist/resource-report.js.map +1 -1
  188. package/dist/restart-guard.js.map +1 -1
  189. package/dist/restart-progress.js.map +1 -1
  190. package/dist/rollout-index.js.map +1 -1
  191. package/dist/runtime-cpu-profile.js.map +1 -1
  192. package/dist/scheduler/db.js.map +1 -1
  193. package/dist/scheduler/scheduler.js.map +1 -1
  194. package/dist/scheduler/task-board-text.js +16 -11
  195. package/dist/scheduler/task-board-text.js.map +1 -1
  196. package/dist/scheduler/task-board.d.ts +39 -11
  197. package/dist/scheduler/task-board.js +123 -45
  198. package/dist/scheduler/task-board.js.map +1 -1
  199. package/dist/scheduler/types.d.ts +1 -1
  200. package/dist/sd-notify.js.map +1 -1
  201. package/dist/search/search-index.js.map +1 -1
  202. package/dist/secret-file.js.map +1 -1
  203. package/dist/secret-store.js.map +1 -1
  204. package/dist/service-installer.d.ts +7 -1
  205. package/dist/service-installer.js +13 -1
  206. package/dist/service-installer.js.map +1 -1
  207. package/dist/service-plan.d.ts +33 -4
  208. package/dist/service-plan.js +177 -8
  209. package/dist/service-plan.js.map +1 -1
  210. package/dist/session-oom.d.ts +21 -0
  211. package/dist/session-oom.js +139 -0
  212. package/dist/session-oom.js.map +1 -0
  213. package/dist/settings-api.js +3 -1
  214. package/dist/settings-api.js.map +1 -1
  215. package/dist/settings-baseline.js.map +1 -1
  216. package/dist/settings-change.d.ts +5 -0
  217. package/dist/settings-change.js +71 -1
  218. package/dist/settings-change.js.map +1 -1
  219. package/dist/settings-confirmation.d.ts +4 -0
  220. package/dist/settings-confirmation.js +2 -0
  221. package/dist/settings-confirmation.js.map +1 -1
  222. package/dist/settings-control.js.map +1 -1
  223. package/dist/settings-effect.js +4 -1
  224. package/dist/settings-effect.js.map +1 -1
  225. package/dist/settings-http-confirmation.js +1 -0
  226. package/dist/settings-http-confirmation.js.map +1 -1
  227. package/dist/settings-instance-patch.js +13 -1
  228. package/dist/settings-instance-patch.js.map +1 -1
  229. package/dist/settings-transaction.js.map +1 -1
  230. package/dist/setup-auth.js.map +1 -1
  231. package/dist/setup-host.d.ts +5 -1
  232. package/dist/setup-host.js +8 -1
  233. package/dist/setup-host.js.map +1 -1
  234. package/dist/setup-wizard.js +5 -2
  235. package/dist/setup-wizard.js.map +1 -1
  236. package/dist/spawn-gate.d.ts +4 -0
  237. package/dist/spawn-gate.js +15 -5
  238. package/dist/spawn-gate.js.map +1 -1
  239. package/dist/startup-memory-deadline.d.ts +23 -0
  240. package/dist/startup-memory-deadline.js +55 -0
  241. package/dist/startup-memory-deadline.js.map +1 -0
  242. package/dist/statusline-watcher.js.map +1 -1
  243. package/dist/storm-window.js.map +1 -1
  244. package/dist/systemd-peer-bus.js.map +1 -1
  245. package/dist/target-queue-worker.js.map +1 -1
  246. package/dist/task-board-summary.js.map +1 -1
  247. package/dist/task-board-tool.js +3 -2
  248. package/dist/task-board-tool.js.map +1 -1
  249. package/dist/task-board-waker.js +8 -4
  250. package/dist/task-board-waker.js.map +1 -1
  251. package/dist/tips.js.map +1 -1
  252. package/dist/tmux-control.js.map +1 -1
  253. package/dist/tmux-exec-helper.js.map +1 -1
  254. package/dist/tmux-manager.d.ts +1 -0
  255. package/dist/tmux-manager.js +2 -1
  256. package/dist/tmux-manager.js.map +1 -1
  257. package/dist/tmux-read.js +1 -1
  258. package/dist/tmux-read.js.map +1 -1
  259. package/dist/tool-permissions.js.map +1 -1
  260. package/dist/tool-progress.js.map +1 -1
  261. package/dist/topic-archiver.js.map +1 -1
  262. package/dist/topic-commands.js.map +1 -1
  263. package/dist/topic-link.js.map +1 -1
  264. package/dist/transcript-delta-reader.js.map +1 -1
  265. package/dist/transcript-jsonl.js.map +1 -1
  266. package/dist/transcript-monitor.js.map +1 -1
  267. package/dist/transcript-sources.js.map +1 -1
  268. package/dist/tunnel/cloudflared-install.js.map +1 -1
  269. package/dist/tunnel/cloudflared.js.map +1 -1
  270. package/dist/tunnel/lease.js +3 -2
  271. package/dist/tunnel/lease.js.map +1 -1
  272. package/dist/tunnel/manager.js.map +1 -1
  273. package/dist/tunnel/public-fetch.js.map +1 -1
  274. package/dist/tunnel/purpose-lane.js.map +1 -1
  275. package/dist/tunnel/types.js.map +1 -1
  276. package/dist/types.d.ts +20 -1
  277. package/dist/ui/chat-render.js +1 -15
  278. package/dist/ui/chat-store.js +171 -61
  279. package/dist/ui/chat-strings.js +7 -2
  280. package/dist/ui/chat-thread.js +48 -1
  281. package/dist/ui/fleet-canvas-layout.js +3 -0
  282. package/dist/ui/fleet-canvas.js +23 -20
  283. package/dist/ui/fleet-office-editor.js +149 -0
  284. package/dist/ui/fleet-office-model.js +358 -0
  285. package/dist/ui/fleet-office-pack.js +199 -0
  286. package/dist/ui/fleet-office-skin.js +474 -0
  287. package/dist/ui/fleet-office-store.js +49 -0
  288. package/dist/ui/fleet-office-zones.js +177 -0
  289. package/dist/ui/fleet-office.js +434 -0
  290. package/dist/ui/fleet-org.js +18 -5
  291. package/dist/ui/guest-refused.html +41 -0
  292. package/dist/ui/image-lightbox.js +9 -3
  293. package/dist/ui/panel-chat.js +14 -13
  294. package/dist/ui/panel-settings.js +24 -7
  295. package/dist/ui/settings-dialogs.js +11 -2
  296. package/dist/ui/settings-model.js +20 -0
  297. package/dist/ui/settings-strings.js +36 -0
  298. package/dist/ui/settings-web-authority.js +7 -2
  299. package/dist/ui/shared/agend-auth.js +2 -0
  300. package/dist/ui/shared/app-i18n.js +2 -2
  301. package/dist/ui/shared/app-prefs.js +2 -0
  302. package/dist/ui/shared/app-shell.js +12 -0
  303. package/dist/ui/shared/app-store.js +1 -0
  304. package/dist/ui/shared/app-stream.js +2 -1
  305. package/dist/ui/shared/app.css +132 -3
  306. package/dist/ui/shared/guest-refused.js +35 -0
  307. package/dist/ui/shared/header-tools.js +2 -2
  308. package/dist/ui/shared/instance-nav.js +25 -8
  309. package/dist/ui/shared/ui-dialog.js +2 -2
  310. package/dist/ui/shared/usage-dialog.js +152 -6
  311. package/dist/ui/shared/view-strings.js +20 -0
  312. package/dist/ui/shared/web-authorize.js +3 -1
  313. package/dist/update-check.js.map +1 -1
  314. package/dist/update-dispatch.js.map +1 -1
  315. package/dist/update-install.js.map +1 -1
  316. package/dist/update-launch.js.map +1 -1
  317. package/dist/update-marker.js.map +1 -1
  318. package/dist/update-preflight.js.map +1 -1
  319. package/dist/usage/format-rich.js.map +1 -1
  320. package/dist/usage/providers.d.ts +8 -0
  321. package/dist/usage/providers.js +6 -2
  322. package/dist/usage/providers.js.map +1 -1
  323. package/dist/usage/statusline-usage.js.map +1 -1
  324. package/dist/usage/usage-api.js +2 -1
  325. package/dist/usage/usage-api.js.map +1 -1
  326. package/dist/usage-report.js.map +1 -1
  327. package/dist/usage-rollup.js +1 -1
  328. package/dist/usage-rollup.js.map +1 -1
  329. package/dist/user-systemd.js.map +1 -1
  330. package/dist/view-api.js +1 -1
  331. package/dist/view-api.js.map +1 -1
  332. package/dist/wake-coordinator.js.map +1 -1
  333. package/dist/web-ambient.js.map +1 -1
  334. package/dist/web-api.d.ts +15 -2
  335. package/dist/web-api.js +174 -25
  336. package/dist/web-api.js.map +1 -1
  337. package/dist/web-app-icon.js.map +1 -1
  338. package/dist/web-appearance-policy.js.map +1 -1
  339. package/dist/web-appearance-tools.js.map +1 -1
  340. package/dist/web-appearance-view.js.map +1 -1
  341. package/dist/web-appearance.js.map +1 -1
  342. package/dist/web-auth.js +5 -4
  343. package/dist/web-auth.js.map +1 -1
  344. package/dist/web-backend-login.js.map +1 -1
  345. package/dist/web-card.js.map +1 -1
  346. package/dist/web-channel-echo.js.map +1 -1
  347. package/dist/web-chat-history.d.ts +41 -11
  348. package/dist/web-chat-history.js +93 -22
  349. package/dist/web-chat-history.js.map +1 -1
  350. package/dist/web-commands.js.map +1 -1
  351. package/dist/web-effect-bundle.d.ts +2 -2
  352. package/dist/web-effect-library.js.map +1 -1
  353. package/dist/web-effect-playback.js.map +1 -1
  354. package/dist/web-effect-profiles.d.ts +16 -16
  355. package/dist/web-effect-profiles.js +3 -2
  356. package/dist/web-effect-profiles.js.map +1 -1
  357. package/dist/web-effect-service.js.map +1 -1
  358. package/dist/web-effect-tools.js.map +1 -1
  359. package/dist/web-effect-trials.js.map +1 -1
  360. package/dist/web-effect-upload.js.map +1 -1
  361. package/dist/web-effect.d.ts +7 -7
  362. package/dist/web-effect.js.map +1 -1
  363. package/dist/web-experimental.js.map +1 -1
  364. package/dist/web-guest-access.js +1 -1
  365. package/dist/web-guest-access.js.map +1 -1
  366. package/dist/web-guest-rooms.js.map +1 -1
  367. package/dist/web-host-guard.js.map +1 -1
  368. package/dist/web-icons.js.map +1 -1
  369. package/dist/web-identity-color.js.map +1 -1
  370. package/dist/web-images.js.map +1 -1
  371. package/dist/web-libavoid.js.map +1 -1
  372. package/dist/web-login.js.map +1 -1
  373. package/dist/web-mentions.js.map +1 -1
  374. package/dist/web-mod-agent.js.map +1 -1
  375. package/dist/web-mod-store.js.map +1 -1
  376. package/dist/web-office-store.d.ts +104 -0
  377. package/dist/web-office-store.js +236 -0
  378. package/dist/web-office-store.js.map +1 -0
  379. package/dist/web-owner-css-store.js.map +1 -1
  380. package/dist/web-owner-css.js.map +1 -1
  381. package/dist/web-png.js.map +1 -1
  382. package/dist/web-prefs.js +2 -0
  383. package/dist/web-prefs.js.map +1 -1
  384. package/dist/web-profile.js.map +1 -1
  385. package/dist/web-reactions.d.ts +2 -1
  386. package/dist/web-request-context.d.ts +5 -0
  387. package/dist/web-request-context.js.map +1 -1
  388. package/dist/web-returning-device.js +1 -1
  389. package/dist/web-returning-device.js.map +1 -1
  390. package/dist/web-schedules.js.map +1 -1
  391. package/dist/web-send-dedupe.d.ts +36 -0
  392. package/dist/web-send-dedupe.js +69 -0
  393. package/dist/web-send-dedupe.js.map +1 -0
  394. package/dist/web-session-authorization.js.map +1 -1
  395. package/dist/web-session.js +1 -1
  396. package/dist/web-session.js.map +1 -1
  397. package/dist/web-settings-patch.d.ts +1 -0
  398. package/dist/web-settings-patch.js +6 -4
  399. package/dist/web-settings-patch.js.map +1 -1
  400. package/dist/web-settings-tools.js.map +1 -1
  401. package/dist/web-terminal-http.js.map +1 -1
  402. package/dist/web-terminal.js.map +1 -1
  403. package/dist/web-theme-store.js.map +1 -1
  404. package/dist/web-upload.d.ts +5 -1
  405. package/dist/web-upload.js +6 -4
  406. package/dist/web-upload.js.map +1 -1
  407. package/dist/web-widget.js.map +1 -1
  408. package/dist/webhook-emitter.js.map +1 -1
  409. package/dist/windows-shortcuts.js +1 -1
  410. package/dist/windows-shortcuts.js.map +1 -1
  411. package/dist/workflow-templates/default.md +1 -1
  412. package/dist/ws-server.js.map +1 -1
  413. package/dist/yaml-shared-nodes.js.map +1 -1
  414. package/launcher/agend-install.cjs +281 -6
  415. package/launcher/private-home.cjs +43 -0
  416. package/package.json +3 -2
  417. package/templates/windows-shortcuts.ps1 +1 -1
@@ -112,6 +112,11 @@ export interface Card {
112
112
  priority: CardPriority;
113
113
  size: CardSize | null;
114
114
  needsReview: boolean;
115
+ /**
116
+ * #2434: opened on the board (createCards), not the `task` tool's own plain task. Before #2434 a main task's default
117
+ * "needs review" marked it; an agent's main task no longer waits for review, so the board says so on its own.
118
+ */
119
+ onBoard: boolean;
115
120
  /** uuids of the cards to finish first (what the fleet's `task` tool has always stored). */
116
121
  deps: string[];
117
122
  goal: string;
@@ -358,14 +363,28 @@ export interface SearchHit {
358
363
  text: string;
359
364
  } | null;
360
365
  }
366
+ /**
367
+ * #2434: who confirms a card in review. The person confirms the cards they handed out (#2426's rule for a blocked card,
368
+ * for review too); an agent's card is the agents' own business: its instance dispatcher confirms it, and one nobody
369
+ * handed out its assignee (or the instance that opened it). A person may still confirm any card.
370
+ */
371
+ export declare const personConfirms: (card: Pick<Card, "dispatcher">) => boolean;
372
+ /** Who that is, in words (an error, the card's text). */
373
+ export declare function confirmerOf(card: Pick<Card, "dispatcher" | "assignee">): string;
361
374
  /** "m3", "M - 3" → "M-3"; null when it is not a card number. */
362
375
  export declare function cardCode(raw: string): string | null;
363
- /** A card waits for the person's confirmation when it is finished. */
376
+ /**
377
+ * An instance dispatcher that is no longer in the fleet (deleted): `gone(name)`, asked of the fleet. Its cards have no one
378
+ * left to answer or confirm them but the person. Unknown (no `gone`): every dispatcher is there.
379
+ */
380
+ export type GoneDispatcher = (name: string) => boolean;
364
381
  /**
365
382
  * #2425: whether a card waits for the person — in review needing their confirmation, or blocked on a question the person
366
- * owns (they handed it out). The one rule for waitingForPerson's query, Needs you and a button's route.
383
+ * owns; both only when they handed it out (#2434: an agent's card in review is its dispatcher's to confirm), or when the
384
+ * instance that handed it out was deleted since (`gone`: nobody else is left to answer it). The one rule for
385
+ * waitingForPerson's query, Needs you and a button's route.
367
386
  */
368
- export declare function waitsForPerson(card: Pick<Card, "status" | "needsReview" | "dispatcher">): boolean;
387
+ export declare function waitsForPerson(card: Pick<Card, "status" | "needsReview" | "dispatcher">, gone?: GoneDispatcher): boolean;
369
388
  export declare function requiresReview(card: Pick<Card, "needsReview">): boolean;
370
389
  /**
371
390
  * What a finished card still lacks for the person's confirmation: unchecked acceptance items, no result. A main task
@@ -483,7 +502,10 @@ export declare class TaskBoard {
483
502
  cards: Card[];
484
503
  total: number;
485
504
  };
486
- /** On the board's terms: in a project, a sub task, one with sub tasks, or one that waits for the person. */
505
+ /**
506
+ * On the board's terms: opened on it (#2434), in a project, a sub task, one with sub tasks, or one that waits for
507
+ * review (every main task opened on the board before #2434 did).
508
+ */
487
509
  isOnBoard(card: Card): boolean;
488
510
  /**
489
511
  * An instance's work, for `task mine`: what it is doing, what is blocked or waits for the person, what it can start
@@ -493,6 +515,10 @@ export declare class TaskBoard {
493
515
  /** Who holds a live card (open, in progress, blocked, waiting for the person): the instances, and any other name. */
494
516
  liveAssignees(): string[];
495
517
  myWork(name: string): MyWork;
518
+ /** #2434: whether `actor` confirms `card` (finishes it from review): the person their cards, an agent's its dispatcher. */
519
+ confirms(card: Card, actor: Actor): boolean;
520
+ /** #2434: the cards an instance handed out that wait in review for its confirmation (its `task mine`). */
521
+ toConfirm(name: string): Card[];
496
522
  /**
497
523
  * What waits for the person (step 3: Needs you, and the daily summary): cards in review that need their confirmation,
498
524
  * and blocked cards whose question is theirs to answer — oldest first, each with when it started waiting (its last
@@ -502,9 +528,11 @@ export declare class TaskBoard {
502
528
  * out is that instance's to decide (step 2's wakes: its question goes there, not here). One with no dispatcher is an
503
529
  * instance's own "I'm waiting" — every card the `task` tool kept before the board, an agent's card for itself, a
504
530
  * reviewer's "waiting on the author" — and is no question to the person: 2.3.0-beta.24 listed 28 such cards, weeks
505
- * old, as "stuck, asking you" (#2425). Nothing is changed on the cards; they stay on the board as they are.
531
+ * old, as "stuck, asking you" (#2425). Nothing is changed on the cards; they stay on the board as they are. One an
532
+ * instance handed out that was deleted since (`gone`) has nobody else to answer or confirm it: it falls back to the
533
+ * person.
506
534
  */
507
- waitingForPerson(): Array<{
535
+ waitingForPerson(gone?: GoneDispatcher): Array<{
508
536
  card: Card;
509
537
  since: string;
510
538
  }>;
@@ -521,7 +549,7 @@ export declare class TaskBoard {
521
549
  * Step 3, the daily summary (task-board-summary.ts): what waits for the person now, what got done and what was
522
550
  * opened in [since, until) (not cancelled), and every live main task's project and assignee.
523
551
  */
524
- summaryRead(since: string, until: string): {
552
+ summaryRead(since: string, until: string, gone?: GoneDispatcher): {
525
553
  waiting: Card[];
526
554
  done: Card[];
527
555
  created: Card[];
@@ -571,8 +599,8 @@ export declare class TaskBoard {
571
599
  */
572
600
  legacyClaim(actor: Actor, uuid: string, assignee: string): Card;
573
601
  /**
574
- * `task done`: from open or claimed. A card that needs the person's confirmation goes to review instead, so no
575
- * instance can finish one alone. On the board, an instance finishes as it would there: its own card, with a result
602
+ * `task done`: from open or claimed. A card that needs confirmation goes to review instead, so no instance finishes
603
+ * one it does not confirm alone; the one who confirms it finishes it, from review too (#2434). On the board, an instance finishes as it would there: its own card, with a result
576
604
  * (an open one is claimed first, under the same rules).
577
605
  */
578
606
  legacyComplete(actor: Actor, uuid: string, result?: string): Card;
@@ -627,8 +655,8 @@ export declare class TaskBoard {
627
655
  private applyMove;
628
656
  /**
629
657
  * A main task with sub tasks follows them: open until something starts, in progress while they run, and when they
630
- * are all done, review (or done, for one that skips the person's confirmation). Only a person moves it from review
631
- * to done. A blocked or cancelled main task is left alone, and so is one without sub tasks (it is moved by hand).
658
+ * are all done, review (or done, for one that needs no confirmation). Only whoever confirms it (#2434) moves it
659
+ * from review to done. A blocked or cancelled main task is left alone, and so is one without sub tasks (it is moved by hand).
632
660
  */
633
661
  /** What a main task's sub tasks make its status (its own, when it is blocked, cancelled or has none). */
634
662
  private derivedStatus;
@@ -97,6 +97,29 @@ function toDispatcher(input, actor) {
97
97
  */
98
98
  const openedForItself = (c) => c.assignee !== null && c.createdBy === c.assignee && c.dispatcher === null;
99
99
  const isDispatcher = (card, actor) => card.dispatcher?.kind === "instance" && !isPerson(actor) && card.dispatcher.name === actor.name;
100
+ /**
101
+ * #2434: who confirms a card in review. The person confirms the cards they handed out (#2426's rule for a blocked card,
102
+ * for review too); an agent's card is the agents' own business: its instance dispatcher confirms it, and one nobody
103
+ * handed out its assignee (or the instance that opened it). A person may still confirm any card.
104
+ */
105
+ export const personConfirms = (card) => card.dispatcher?.kind === "person";
106
+ function confirms(card, actor) {
107
+ if (isPerson(actor))
108
+ return true;
109
+ if (personConfirms(card))
110
+ return false;
111
+ if (card.dispatcher?.kind === "instance")
112
+ return card.dispatcher.name === actor.name;
113
+ return card.assignee === actor.name || (card.assignee === null && card.createdBy === actor.name);
114
+ }
115
+ /** Who that is, in words (an error, the card's text). */
116
+ export function confirmerOf(card) {
117
+ if (card.dispatcher?.kind === "person")
118
+ return "the person";
119
+ if (card.dispatcher?.kind === "instance")
120
+ return card.dispatcher.name;
121
+ return card.assignee ?? "its assignee";
122
+ }
100
123
  function normalizeAssignee(raw) {
101
124
  const v = raw?.trim() ?? "";
102
125
  if (v === "" || v.toLowerCase() === "none")
@@ -110,13 +133,15 @@ export function cardCode(raw) {
110
133
  const m = CODE_RE.exec(raw);
111
134
  return m ? `${m[1].toUpperCase()}-${Number(m[2])}` : null;
112
135
  }
113
- /** A card waits for the person's confirmation when it is finished. */
136
+ const orphaned = (card, gone) => !!gone && card.dispatcher?.kind === "instance" && gone(card.dispatcher.name);
114
137
  /**
115
138
  * #2425: whether a card waits for the person — in review needing their confirmation, or blocked on a question the person
116
- * owns (they handed it out). The one rule for waitingForPerson's query, Needs you and a button's route.
139
+ * owns; both only when they handed it out (#2434: an agent's card in review is its dispatcher's to confirm), or when the
140
+ * instance that handed it out was deleted since (`gone`: nobody else is left to answer it). The one rule for
141
+ * waitingForPerson's query, Needs you and a button's route.
117
142
  */
118
- export function waitsForPerson(card) {
119
- return (card.status === "review" && card.needsReview) || (card.status === "blocked" && card.dispatcher?.kind === "person");
143
+ export function waitsForPerson(card, gone) {
144
+ return (personConfirms(card) || orphaned(card, gone)) && ((card.status === "review" && card.needsReview) || card.status === "blocked");
120
145
  }
121
146
  export function requiresReview(card) {
122
147
  return card.needsReview;
@@ -155,6 +180,7 @@ function toCard(r) {
155
180
  priority: r.priority ?? "normal",
156
181
  size: r.size ?? null,
157
182
  needsReview: Number(r.needs_review) === 1,
183
+ onBoard: Number(r.on_board) === 1,
158
184
  deps: parseJsonArray(r.depends_on, []).filter((d) => typeof d === "string"),
159
185
  goal: r.description ?? "",
160
186
  scope: r.scope ?? "",
@@ -257,6 +283,7 @@ export class TaskBoard {
257
283
  add("seen_entry_id", "INTEGER NOT NULL DEFAULT 0");
258
284
  add("dispatcher", "TEXT");
259
285
  add("dispatcher_kind", "TEXT");
286
+ add("on_board", "INTEGER NOT NULL DEFAULT 0"); // #2434; a row before it: its needs_review says so
260
287
  this.db.exec(`
261
288
  CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code ON tasks(code);
262
289
  CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
@@ -760,9 +787,12 @@ export class TaskBoard {
760
787
  const rows = this.db.prepare(`SELECT * ${from} ORDER BY CASE kind WHEN 'main' THEN 0 ELSE 1 END, ${STATUS_ORDER}, ${PRIORITY_ORDER}, sort LIMIT ?`).all(...params, limit);
761
788
  return { cards: rows.map(toCard), total: n };
762
789
  }
763
- /** On the board's terms: in a project, a sub task, one with sub tasks, or one that waits for the person. */
790
+ /**
791
+ * On the board's terms: opened on it (#2434), in a project, a sub task, one with sub tasks, or one that waits for
792
+ * review (every main task opened on the board before #2434 did).
793
+ */
764
794
  isOnBoard(card) {
765
- return card.projectId !== null || card.kind === "sub" || card.needsReview || this.activeSubs(card.uuid).length > 0;
795
+ return card.onBoard || card.projectId !== null || card.kind === "sub" || card.needsReview || this.activeSubs(card.uuid).length > 0;
766
796
  }
767
797
  /**
768
798
  * An instance's work, for `task mine`: what it is doing, what is blocked or waits for the person, what it can start
@@ -807,6 +837,13 @@ export class TaskBoard {
807
837
  mains: coordinating.map((card) => ({ card, progress: this.progress(card) })),
808
838
  };
809
839
  }
840
+ /** #2434: whether `actor` confirms `card` (finishes it from review): the person their cards, an agent's its dispatcher. */
841
+ confirms(card, actor) { return confirms(card, actor); }
842
+ /** #2434: the cards an instance handed out that wait in review for its confirmation (its `task mine`). */
843
+ toConfirm(name) {
844
+ return this.db.prepare("SELECT * FROM tasks WHERE status = 'review' AND needs_review = 1 AND (dispatcher_kind IS NULL OR dispatcher_kind != 'person') AND dispatcher = ? AND (assignee IS NULL OR assignee != ?) ORDER BY updated_at")
845
+ .all(name, name).map(toCard);
846
+ }
810
847
  /**
811
848
  * What waits for the person (step 3: Needs you, and the daily summary): cards in review that need their confirmation,
812
849
  * and blocked cards whose question is theirs to answer — oldest first, each with when it started waiting (its last
@@ -816,10 +853,17 @@ export class TaskBoard {
816
853
  * out is that instance's to decide (step 2's wakes: its question goes there, not here). One with no dispatcher is an
817
854
  * instance's own "I'm waiting" — every card the `task` tool kept before the board, an agent's card for itself, a
818
855
  * reviewer's "waiting on the author" — and is no question to the person: 2.3.0-beta.24 listed 28 such cards, weeks
819
- * old, as "stuck, asking you" (#2425). Nothing is changed on the cards; they stay on the board as they are.
856
+ * old, as "stuck, asking you" (#2425). Nothing is changed on the cards; they stay on the board as they are. One an
857
+ * instance handed out that was deleted since (`gone`) has nobody else to answer or confirm it: it falls back to the
858
+ * person.
820
859
  */
821
- waitingForPerson() {
822
- const rows = this.db.prepare("SELECT * FROM tasks WHERE (status = 'review' AND needs_review = 1) OR (status = 'blocked' AND dispatcher_kind = 'person') ORDER BY updated_at").all().map(toCard);
860
+ waitingForPerson(gone) {
861
+ const waiting = "((status = 'review' AND needs_review = 1) OR status = 'blocked')";
862
+ const rows = [
863
+ ...this.db.prepare(`SELECT * FROM tasks WHERE dispatcher_kind = 'person' AND ${waiting}`).all().map(toCard),
864
+ ...(gone ? this.db.prepare(`SELECT * FROM tasks WHERE dispatcher IS NOT NULL AND (dispatcher_kind IS NULL OR dispatcher_kind != 'person') AND ${waiting}`).all()
865
+ .map(toCard).filter((card) => orphaned(card, gone)) : []),
866
+ ];
823
867
  const moved = this.db.prepare("SELECT at FROM task_events WHERE task_id = ? AND type = 'moved' AND json_extract(data, '$.to') = ? ORDER BY id DESC LIMIT 1");
824
868
  return rows.map((card) => {
825
869
  const at = moved.get(card.uuid, card.status)?.at;
@@ -913,10 +957,10 @@ export class TaskBoard {
913
957
  * Step 3, the daily summary (task-board-summary.ts): what waits for the person now, what got done and what was
914
958
  * opened in [since, until) (not cancelled), and every live main task's project and assignee.
915
959
  */
916
- summaryRead(since, until) {
960
+ summaryRead(since, until, gone) {
917
961
  const between = (sql) => this.db.prepare(sql).all(since, until).map(toCard);
918
962
  return {
919
- waiting: this.waitingForPerson().map((w) => w.card),
963
+ waiting: this.waitingForPerson(gone).map((w) => w.card),
920
964
  done: between("SELECT * FROM tasks WHERE status = 'done' AND done_at >= ? AND done_at < ? ORDER BY sort"),
921
965
  created: between("SELECT * FROM tasks WHERE status != 'cancelled' AND created_at >= ? AND created_at < ? ORDER BY sort"),
922
966
  mains: this.db.prepare("SELECT project_id, assignee FROM tasks WHERE kind = 'main' AND status != 'cancelled'").all()
@@ -958,13 +1002,14 @@ export class TaskBoard {
958
1002
  const acceptance = checkAcceptance(spec.acceptance ?? []);
959
1003
  if (acceptance.length > CARD_LIMITS.acceptanceItems)
960
1004
  throw new TaskBoardError("too_long", `at most ${CARD_LIMITS.acceptanceItems} acceptance items`, "acceptance_max", [CARD_LIMITS.acceptanceItems]);
961
- const needsReview = spec.needsReview ?? kind === "main";
962
- if (!needsReview && kind === "main" && !isPerson(actor))
963
- throw new TaskBoardError("forbidden", "only a person can open a main task that skips their confirmation");
964
1005
  const now = this.nowIso();
965
1006
  const assignee = spec.assignee !== undefined ? normalizeAssignee(spec.assignee) : isPerson(actor) ? null : actor.name;
966
1007
  const opener = isPerson(actor) ? { kind: "person", name: actor.name }
967
1008
  : actor.kind === "instance" && assignee && assignee !== actor.name ? { kind: "instance", name: actor.name } : null;
1009
+ const dispatcher = spec.dispatcher !== undefined ? toDispatcher(spec.dispatcher, actor) : (parent?.dispatcher ?? opener);
1010
+ // #2434: a main task waits for the person's confirmation when the person handed it out; an agent's needs none
1011
+ // unless it asks for one (its dispatcher then confirms it).
1012
+ const needsReview = spec.needsReview ?? (kind === "main" && dispatcher?.kind === "person");
968
1013
  const card = {
969
1014
  uuid: randomUUID(),
970
1015
  code: this.nextCode(kind === "main" ? "M" : "S"),
@@ -974,10 +1019,11 @@ export class TaskBoard {
974
1019
  title,
975
1020
  status: "open",
976
1021
  assignee,
977
- dispatcher: spec.dispatcher !== undefined ? toDispatcher(spec.dispatcher, actor) : (parent?.dispatcher ?? opener),
1022
+ dispatcher,
978
1023
  priority: spec.priority ?? "normal",
979
1024
  size: spec.size ?? null,
980
1025
  needsReview,
1026
+ onBoard: true,
981
1027
  deps: this.checkDeps({ uuid: null, kind, parent: parent?.uuid ?? null }, (spec.dependsOn ?? []).map((d) => this.resolveRef(d, created))),
982
1028
  goal: checkText("the goal", spec.goal ?? "", CARD_LIMITS.goal),
983
1029
  scope: checkText("the scope", spec.scope ?? "", CARD_LIMITS.scope),
@@ -1064,11 +1110,24 @@ export class TaskBoard {
1064
1110
  set("size", patch.size);
1065
1111
  if (patch.priority !== undefined)
1066
1112
  set("priority", patch.priority);
1067
- if (patch.dispatcher !== undefined)
1068
- set("dispatcher", toDispatcher(patch.dispatcher, actor), "dispatcher", (d) => d?.name ?? null);
1113
+ if (patch.dispatcher !== undefined) {
1114
+ const next = toDispatcher(patch.dispatcher, actor);
1115
+ // #2434: the person's card stays theirs — an instance re-dispatching it would take it out of their confirmation.
1116
+ const moved = JSON.stringify(next) !== JSON.stringify(card.dispatcher);
1117
+ if (ai && personConfirms(card) && moved) {
1118
+ throw new TaskBoardError("forbidden", `${card.code} was handed out by the person; only they hand it to someone else`);
1119
+ }
1120
+ // #2436 review: nor an agent's card that waits for its dispatcher's confirmation — dropping or replacing the
1121
+ // dispatcher would let its assignee confirm it itself. Only that dispatcher (or a person) hands it on.
1122
+ if (ai && moved && card.needsReview && card.dispatcher?.kind === "instance" && card.dispatcher.name !== actor.name) {
1123
+ throw new TaskBoardError("forbidden", `${card.code} waits for ${card.dispatcher.name}'s confirmation; only they hand it to someone else`);
1124
+ }
1125
+ set("dispatcher", next, "dispatcher", (d) => d?.name ?? null);
1126
+ }
1069
1127
  if (patch.needsReview !== undefined) {
1070
- if (ai && card.needsReview && !patch.needsReview)
1071
- throw new TaskBoardError("forbidden", "only a person can clear \"needs review\"");
1128
+ if (ai && card.needsReview && !patch.needsReview && !confirms(card, actor)) {
1129
+ throw new TaskBoardError("forbidden", personConfirms(card) ? "only a person can clear \"needs review\"" : `only ${confirmerOf(card)} can clear "needs review" on ${card.code}`);
1130
+ }
1072
1131
  set("needsReview", patch.needsReview);
1073
1132
  }
1074
1133
  if (patch.dependsOn !== undefined) {
@@ -1236,7 +1295,7 @@ export class TaskBoard {
1236
1295
  const now = this.nowIso();
1237
1296
  const card = {
1238
1297
  uuid: randomUUID(), code: this.nextCode("M"), kind: "main", projectId: null, parent: null, title: params.title, status: "open",
1239
- assignee: params.assignee ?? null, priority: params.priority ?? "normal", size: null, needsReview: false,
1298
+ assignee: params.assignee ?? null, priority: params.priority ?? "normal", size: null, needsReview: false, onBoard: false,
1240
1299
  // Opened for another instance: the opener handed it out (its blocked question goes to it).
1241
1300
  dispatcher: actor.kind === "instance" && params.assignee && params.assignee !== actor.name ? { kind: "instance", name: actor.name } : null,
1242
1301
  deps: this.checkDeps({ uuid: null, kind: "main", parent: null }, params.depends_on ?? []),
@@ -1289,8 +1348,8 @@ export class TaskBoard {
1289
1348
  });
1290
1349
  }
1291
1350
  /**
1292
- * `task done`: from open or claimed. A card that needs the person's confirmation goes to review instead, so no
1293
- * instance can finish one alone. On the board, an instance finishes as it would there: its own card, with a result
1351
+ * `task done`: from open or claimed. A card that needs confirmation goes to review instead, so no instance finishes
1352
+ * one it does not confirm alone; the one who confirms it finishes it, from review too (#2434). On the board, an instance finishes as it would there: its own card, with a result
1294
1353
  * (an open one is claimed first, under the same rules).
1295
1354
  */
1296
1355
  legacyComplete(actor, uuid, result) {
@@ -1298,8 +1357,10 @@ export class TaskBoard {
1298
1357
  const card = this.byUuid(uuid);
1299
1358
  if (!card)
1300
1359
  throw new Error(`Task "${uuid}" not found`);
1301
- if (card.status !== "claimed" && card.status !== "open") {
1302
- throw new Error(`Task "${uuid}" is ${card.status}, cannot complete (must be open or claimed)`);
1360
+ // #2434: a card in review is finished by whoever confirms it (an agent's card: its dispatcher).
1361
+ const confirming = card.status === "review" && confirms(card, actor);
1362
+ if (card.status !== "claimed" && card.status !== "open" && !confirming) {
1363
+ throw new Error(`Task "${uuid}" is ${card.status}, cannot complete (must be open or claimed${card.status === "review" ? `; it waits for ${confirmerOf(card)}'s confirmation` : ""})`);
1303
1364
  }
1304
1365
  const open = this.children(card.uuid).filter((k) => !isFinished(k));
1305
1366
  if (open.length)
@@ -1308,7 +1369,7 @@ export class TaskBoard {
1308
1369
  this.event(actor, card, "updated", { changes: { result: { from: card.result, to: result } } });
1309
1370
  card.result = result;
1310
1371
  }
1311
- const to = requiresReview(card) && !isPerson(actor) ? "review" : "done";
1372
+ const to = requiresReview(card) && !confirms(card, actor) ? "review" : "done";
1312
1373
  if (this.heldToBoardRules(card, actor)) {
1313
1374
  if (card.status === "open") {
1314
1375
  this.checkManualMove(card, actor, "claimed", "", { finishing: true });
@@ -1361,9 +1422,9 @@ export class TaskBoard {
1361
1422
  if (derived)
1362
1423
  this.checkDerivedMove(card, actor, to, "");
1363
1424
  else {
1364
- if (card.status === "review" && to === "done")
1365
- throw new Error(`Task "${card.code}" is waiting for the person's confirmation`);
1366
- if (to === "done" && requiresReview(card))
1425
+ if (card.status === "review" && to === "done" && !confirms(card, actor))
1426
+ throw new Error(`Task "${card.code}" is waiting for ${confirmerOf(card)}'s confirmation`);
1427
+ if (to === "done" && card.status !== "review" && requiresReview(card) && !confirms(card, actor))
1367
1428
  to = "review";
1368
1429
  if (held && to !== card.status)
1369
1430
  this.checkManualMove(card, actor, to, "");
@@ -1430,7 +1491,13 @@ export class TaskBoard {
1430
1491
  this.checkOneAtATime(card, actor);
1431
1492
  }
1432
1493
  else if (from === "review") {
1433
- if (ai) {
1494
+ if (ai && !own && confirms(card, actor)) {
1495
+ // #2434: an agent's card sent back by the instance that confirms it, as the person sends theirs back.
1496
+ if (!reason)
1497
+ throw new TaskBoardError("invalid", "sending work back needs a reason", "sendback_reason");
1498
+ this.entry(card, "reject", actor, reason);
1499
+ }
1500
+ else if (ai) {
1434
1501
  if (!own)
1435
1502
  throw notOwner();
1436
1503
  if (startsWork)
@@ -1474,14 +1541,24 @@ export class TaskBoard {
1474
1541
  }
1475
1542
  case "done": {
1476
1543
  if (ai) {
1477
- if (from === "review")
1478
- throw new TaskBoardError("forbidden", `${card.code} is waiting for the person's confirmation: only they finish it`);
1479
- if (from !== "claimed")
1480
- throw new TaskBoardError("invalid", `only a card in progress can be done (${card.code} is ${from})`);
1481
- if (!own)
1482
- throw notOwner();
1483
- if (requiresReview(card))
1484
- throw new TaskBoardError("needs_review", `${card.code} needs the person's confirmation; move it to review`);
1544
+ if (from === "review") {
1545
+ if (!confirms(card, actor)) {
1546
+ throw new TaskBoardError("forbidden", personConfirms(card) ? `${card.code} is waiting for the person's confirmation: only they finish it`
1547
+ : `${card.code} is waiting for ${confirmerOf(card)}'s confirmation: only they finish it`);
1548
+ }
1549
+ if (reason)
1550
+ this.entry(card, "approve", actor, reason);
1551
+ }
1552
+ else {
1553
+ if (from !== "claimed")
1554
+ throw new TaskBoardError("invalid", `only a card in progress can be done (${card.code} is ${from})`);
1555
+ if (!own)
1556
+ throw notOwner();
1557
+ if (requiresReview(card) && !confirms(card, actor)) {
1558
+ throw new TaskBoardError("needs_review", personConfirms(card) ? `${card.code} needs the person's confirmation; move it to review`
1559
+ : `${card.code} needs ${confirmerOf(card)}'s confirmation; move it to review`);
1560
+ }
1561
+ }
1485
1562
  }
1486
1563
  else if (from === "review" && reason) {
1487
1564
  this.entry(card, "approve", actor, reason);
@@ -1687,8 +1764,8 @@ export class TaskBoard {
1687
1764
  }
1688
1765
  /**
1689
1766
  * A main task with sub tasks follows them: open until something starts, in progress while they run, and when they
1690
- * are all done, review (or done, for one that skips the person's confirmation). Only a person moves it from review
1691
- * to done. A blocked or cancelled main task is left alone, and so is one without sub tasks (it is moved by hand).
1767
+ * are all done, review (or done, for one that needs no confirmation). Only whoever confirms it (#2434) moves it
1768
+ * from review to done. A blocked or cancelled main task is left alone, and so is one without sub tasks (it is moved by hand).
1692
1769
  */
1693
1770
  /** What a main task's sub tasks make its status (its own, when it is blocked, cancelled or has none). */
1694
1771
  derivedStatus(main) {
@@ -1721,7 +1798,7 @@ export class TaskBoard {
1721
1798
  this.save(main);
1722
1799
  this.event(actor, main, "moved", { from, to: next, auto: true });
1723
1800
  if (next === "review")
1724
- this.say("review_waiting", [main.code, main.title], `${main.code} "${main.title}": every sub task is done; it is waiting for the person's confirmation`);
1801
+ this.say("review_waiting", [main.code, main.title], `${main.code} "${main.title}": every sub task is done; it is waiting for ${confirmerOf(main)}'s confirmation`);
1725
1802
  else if (from === "done" || from === "review")
1726
1803
  this.say("back_to", [main.code, next], `${main.code} has unfinished sub tasks again; it is back to ${next}`);
1727
1804
  if (next === "done")
@@ -1840,7 +1917,8 @@ export class TaskBoard {
1840
1917
  add(card, card.assignee, "cancelled", e.actor, why);
1841
1918
  continue;
1842
1919
  }
1843
- // Its dispatcher tells the person it waits for them (not the board).
1920
+ // An agent's card waits for its instance dispatcher to confirm it (#2434: its own to confirm, nobody to ask). A
1921
+ // person's card has no instance dispatcher, so no notice: the person has it in Needs you.
1844
1922
  if (d.to === "review" && !isPerson(e.actor) && requiresReview(card))
1845
1923
  add(card, dispatcherOf(), "to_confirm", e.actor);
1846
1924
  if (d.auto) {
@@ -1852,9 +1930,9 @@ export class TaskBoard {
1852
1930
  add(card, card.assignee, "start", e.actor);
1853
1931
  else if (d.from === "blocked" && d.to === "claimed")
1854
1932
  add(card, card.assignee, "answered", e.actor, comments.get(card.uuid));
1855
- else if (d.from === "review" && (d.to === "claimed" || d.to === "open") && isPerson(e.actor))
1933
+ else if (d.from === "review" && (d.to === "claimed" || d.to === "open"))
1856
1934
  add(card, card.assignee, "sent_back", e.actor, d.reason);
1857
- else if (d.from === "review" && d.to === "done" && isPerson(e.actor)) {
1935
+ else if (d.from === "review" && d.to === "done") {
1858
1936
  add(card, card.assignee, "approved", e.actor, d.reason);
1859
1937
  add(card, dispatcherOf(), "approved", e.actor, d.reason);
1860
1938
  }
@@ -2087,8 +2165,8 @@ export class TaskBoard {
2087
2165
  }
2088
2166
  insert(card) {
2089
2167
  this.db.prepare(`INSERT INTO tasks (id, code, kind, project_id, parent_id, title, description, status, priority, assignee, dispatcher, dispatcher_kind, created_by, depends_on, result,
2090
- scope, acceptance, context, state, blocked_reason, size, needs_review, version, sort, created_at, updated_at, started_at, submitted_at, done_at, seen_entry_id)
2091
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(card.uuid, card.code, card.kind, card.projectId, card.parent, card.title, card.goal === "" ? null : card.goal, card.status, card.priority, card.assignee, card.dispatcher?.name ?? null, card.dispatcher?.kind ?? null, card.createdBy, card.deps.length ? JSON.stringify(card.deps) : null, card.result === "" ? null : card.result, card.scope, JSON.stringify(card.acceptance), card.context, card.state, card.blockedReason, card.size, card.needsReview ? 1 : 0, card.version, card.sort, card.createdAt, card.updatedAt, card.startedAt, card.submittedAt, card.doneAt, card.seenEntryId);
2168
+ scope, acceptance, context, state, blocked_reason, size, needs_review, version, sort, created_at, updated_at, started_at, submitted_at, done_at, seen_entry_id, on_board)
2169
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(card.uuid, card.code, card.kind, card.projectId, card.parent, card.title, card.goal === "" ? null : card.goal, card.status, card.priority, card.assignee, card.dispatcher?.name ?? null, card.dispatcher?.kind ?? null, card.createdBy, card.deps.length ? JSON.stringify(card.deps) : null, card.result === "" ? null : card.result, card.scope, JSON.stringify(card.acceptance), card.context, card.state, card.blockedReason, card.size, card.needsReview ? 1 : 0, card.version, card.sort, card.createdAt, card.updatedAt, card.startedAt, card.submittedAt, card.doneAt, card.seenEntryId, card.onBoard ? 1 : 0);
2092
2170
  }
2093
2171
  /** Empty text is stored as NULL in the columns the `task` tool has always had (description, result, depends_on). */
2094
2172
  save(card) {