@songsid/agend 2.3.0-beta.24 → 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 (419) 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 +20 -6
  75. package/dist/config-validator.js.map +1 -1
  76. package/dist/config.js +11 -3
  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 +77 -6
  110. package/dist/fleet-manager.js +488 -147
  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 +36 -12
  125. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +45 -14
  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 +50 -10
  197. package/dist/scheduler/task-board.js +138 -44
  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 +6 -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 +6 -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.d.ts +4 -4
  247. package/dist/task-board-summary.js +4 -4
  248. package/dist/task-board-summary.js.map +1 -1
  249. package/dist/task-board-tool.js +3 -2
  250. package/dist/task-board-tool.js.map +1 -1
  251. package/dist/task-board-waker.js +8 -4
  252. package/dist/task-board-waker.js.map +1 -1
  253. package/dist/tips.js.map +1 -1
  254. package/dist/tmux-control.js.map +1 -1
  255. package/dist/tmux-exec-helper.js.map +1 -1
  256. package/dist/tmux-manager.d.ts +1 -0
  257. package/dist/tmux-manager.js +2 -1
  258. package/dist/tmux-manager.js.map +1 -1
  259. package/dist/tmux-read.js +1 -1
  260. package/dist/tmux-read.js.map +1 -1
  261. package/dist/tool-permissions.js.map +1 -1
  262. package/dist/tool-progress.js.map +1 -1
  263. package/dist/topic-archiver.js.map +1 -1
  264. package/dist/topic-commands.js.map +1 -1
  265. package/dist/topic-link.js.map +1 -1
  266. package/dist/transcript-delta-reader.js.map +1 -1
  267. package/dist/transcript-jsonl.js.map +1 -1
  268. package/dist/transcript-monitor.js.map +1 -1
  269. package/dist/transcript-sources.js.map +1 -1
  270. package/dist/tunnel/cloudflared-install.js.map +1 -1
  271. package/dist/tunnel/cloudflared.js.map +1 -1
  272. package/dist/tunnel/lease.js +3 -2
  273. package/dist/tunnel/lease.js.map +1 -1
  274. package/dist/tunnel/manager.js.map +1 -1
  275. package/dist/tunnel/public-fetch.js.map +1 -1
  276. package/dist/tunnel/purpose-lane.js.map +1 -1
  277. package/dist/tunnel/types.js.map +1 -1
  278. package/dist/types.d.ts +26 -2
  279. package/dist/ui/chat-render.js +1 -15
  280. package/dist/ui/chat-store.js +171 -61
  281. package/dist/ui/chat-strings.js +7 -2
  282. package/dist/ui/chat-thread.js +48 -1
  283. package/dist/ui/fleet-canvas-layout.js +3 -0
  284. package/dist/ui/fleet-canvas.js +23 -20
  285. package/dist/ui/fleet-office-editor.js +149 -0
  286. package/dist/ui/fleet-office-model.js +358 -0
  287. package/dist/ui/fleet-office-pack.js +199 -0
  288. package/dist/ui/fleet-office-skin.js +474 -0
  289. package/dist/ui/fleet-office-store.js +49 -0
  290. package/dist/ui/fleet-office-zones.js +177 -0
  291. package/dist/ui/fleet-office.js +434 -0
  292. package/dist/ui/fleet-org.js +18 -5
  293. package/dist/ui/guest-refused.html +41 -0
  294. package/dist/ui/image-lightbox.js +9 -3
  295. package/dist/ui/panel-chat.js +14 -13
  296. package/dist/ui/panel-settings.js +30 -12
  297. package/dist/ui/settings-dialogs.js +15 -6
  298. package/dist/ui/settings-model.js +40 -3
  299. package/dist/ui/settings-strings.js +38 -0
  300. package/dist/ui/settings-web-authority.js +7 -2
  301. package/dist/ui/shared/agend-auth.js +2 -0
  302. package/dist/ui/shared/app-i18n.js +2 -2
  303. package/dist/ui/shared/app-prefs.js +2 -0
  304. package/dist/ui/shared/app-shell.js +12 -0
  305. package/dist/ui/shared/app-store.js +1 -0
  306. package/dist/ui/shared/app-stream.js +2 -1
  307. package/dist/ui/shared/app.css +132 -3
  308. package/dist/ui/shared/guest-refused.js +35 -0
  309. package/dist/ui/shared/header-tools.js +2 -2
  310. package/dist/ui/shared/instance-nav.js +25 -8
  311. package/dist/ui/shared/ui-dialog.js +2 -2
  312. package/dist/ui/shared/usage-dialog.js +152 -6
  313. package/dist/ui/shared/view-strings.js +20 -0
  314. package/dist/ui/shared/web-authorize.js +3 -1
  315. package/dist/update-check.js.map +1 -1
  316. package/dist/update-dispatch.js.map +1 -1
  317. package/dist/update-install.js.map +1 -1
  318. package/dist/update-launch.js.map +1 -1
  319. package/dist/update-marker.js.map +1 -1
  320. package/dist/update-preflight.js.map +1 -1
  321. package/dist/usage/format-rich.js.map +1 -1
  322. package/dist/usage/providers.d.ts +8 -0
  323. package/dist/usage/providers.js +6 -2
  324. package/dist/usage/providers.js.map +1 -1
  325. package/dist/usage/statusline-usage.js.map +1 -1
  326. package/dist/usage/usage-api.js +2 -1
  327. package/dist/usage/usage-api.js.map +1 -1
  328. package/dist/usage-report.js.map +1 -1
  329. package/dist/usage-rollup.js +1 -1
  330. package/dist/usage-rollup.js.map +1 -1
  331. package/dist/user-systemd.js.map +1 -1
  332. package/dist/view-api.js +1 -1
  333. package/dist/view-api.js.map +1 -1
  334. package/dist/wake-coordinator.js.map +1 -1
  335. package/dist/web-ambient.js.map +1 -1
  336. package/dist/web-api.d.ts +15 -2
  337. package/dist/web-api.js +174 -25
  338. package/dist/web-api.js.map +1 -1
  339. package/dist/web-app-icon.js.map +1 -1
  340. package/dist/web-appearance-policy.js.map +1 -1
  341. package/dist/web-appearance-tools.js.map +1 -1
  342. package/dist/web-appearance-view.js.map +1 -1
  343. package/dist/web-appearance.js.map +1 -1
  344. package/dist/web-auth.js +5 -4
  345. package/dist/web-auth.js.map +1 -1
  346. package/dist/web-backend-login.js.map +1 -1
  347. package/dist/web-card.js.map +1 -1
  348. package/dist/web-channel-echo.js.map +1 -1
  349. package/dist/web-chat-history.d.ts +41 -11
  350. package/dist/web-chat-history.js +93 -22
  351. package/dist/web-chat-history.js.map +1 -1
  352. package/dist/web-commands.js.map +1 -1
  353. package/dist/web-effect-bundle.d.ts +2 -2
  354. package/dist/web-effect-library.js.map +1 -1
  355. package/dist/web-effect-playback.js.map +1 -1
  356. package/dist/web-effect-profiles.d.ts +16 -16
  357. package/dist/web-effect-profiles.js +3 -2
  358. package/dist/web-effect-profiles.js.map +1 -1
  359. package/dist/web-effect-service.js.map +1 -1
  360. package/dist/web-effect-tools.js.map +1 -1
  361. package/dist/web-effect-trials.js.map +1 -1
  362. package/dist/web-effect-upload.js.map +1 -1
  363. package/dist/web-effect.d.ts +7 -7
  364. package/dist/web-effect.js.map +1 -1
  365. package/dist/web-experimental.js.map +1 -1
  366. package/dist/web-guest-access.js +1 -1
  367. package/dist/web-guest-access.js.map +1 -1
  368. package/dist/web-guest-rooms.js.map +1 -1
  369. package/dist/web-host-guard.js.map +1 -1
  370. package/dist/web-icons.js.map +1 -1
  371. package/dist/web-identity-color.js.map +1 -1
  372. package/dist/web-images.js.map +1 -1
  373. package/dist/web-libavoid.js.map +1 -1
  374. package/dist/web-login.js.map +1 -1
  375. package/dist/web-mentions.js.map +1 -1
  376. package/dist/web-mod-agent.js.map +1 -1
  377. package/dist/web-mod-store.js.map +1 -1
  378. package/dist/web-office-store.d.ts +104 -0
  379. package/dist/web-office-store.js +236 -0
  380. package/dist/web-office-store.js.map +1 -0
  381. package/dist/web-owner-css-store.js.map +1 -1
  382. package/dist/web-owner-css.js.map +1 -1
  383. package/dist/web-png.js.map +1 -1
  384. package/dist/web-prefs.js +2 -0
  385. package/dist/web-prefs.js.map +1 -1
  386. package/dist/web-profile.js.map +1 -1
  387. package/dist/web-reactions.d.ts +2 -1
  388. package/dist/web-request-context.d.ts +5 -0
  389. package/dist/web-request-context.js.map +1 -1
  390. package/dist/web-returning-device.js +1 -1
  391. package/dist/web-returning-device.js.map +1 -1
  392. package/dist/web-schedules.js.map +1 -1
  393. package/dist/web-send-dedupe.d.ts +36 -0
  394. package/dist/web-send-dedupe.js +69 -0
  395. package/dist/web-send-dedupe.js.map +1 -0
  396. package/dist/web-session-authorization.js.map +1 -1
  397. package/dist/web-session.js +1 -1
  398. package/dist/web-session.js.map +1 -1
  399. package/dist/web-settings-patch.d.ts +1 -0
  400. package/dist/web-settings-patch.js +6 -4
  401. package/dist/web-settings-patch.js.map +1 -1
  402. package/dist/web-settings-tools.js.map +1 -1
  403. package/dist/web-terminal-http.js.map +1 -1
  404. package/dist/web-terminal.js.map +1 -1
  405. package/dist/web-theme-store.js.map +1 -1
  406. package/dist/web-upload.d.ts +5 -1
  407. package/dist/web-upload.js +6 -4
  408. package/dist/web-upload.js.map +1 -1
  409. package/dist/web-widget.js.map +1 -1
  410. package/dist/webhook-emitter.js.map +1 -1
  411. package/dist/windows-shortcuts.js +1 -1
  412. package/dist/windows-shortcuts.js.map +1 -1
  413. package/dist/workflow-templates/default.md +1 -1
  414. package/dist/ws-server.js.map +1 -1
  415. package/dist/yaml-shared-nodes.js.map +1 -1
  416. package/launcher/agend-install.cjs +281 -6
  417. package/launcher/private-home.cjs +43 -0
  418. package/package.json +3 -2
  419. 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,9 +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;
381
+ /**
382
+ * #2425: whether a card waits for the person — in review needing their confirmation, or blocked on a question the person
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.
386
+ */
387
+ export declare function waitsForPerson(card: Pick<Card, "status" | "needsReview" | "dispatcher">, gone?: GoneDispatcher): boolean;
364
388
  export declare function requiresReview(card: Pick<Card, "needsReview">): boolean;
365
389
  /**
366
390
  * What a finished card still lacks for the person's confirmation: unchecked acceptance items, no result. A main task
@@ -478,7 +502,10 @@ export declare class TaskBoard {
478
502
  cards: Card[];
479
503
  total: number;
480
504
  };
481
- /** 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
+ */
482
509
  isOnBoard(card: Card): boolean;
483
510
  /**
484
511
  * An instance's work, for `task mine`: what it is doing, what is blocked or waits for the person, what it can start
@@ -488,11 +515,24 @@ export declare class TaskBoard {
488
515
  /** Who holds a live card (open, in progress, blocked, waiting for the person): the instances, and any other name. */
489
516
  liveAssignees(): string[];
490
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[];
491
522
  /**
492
- * What waits for the person (step 3: Needs you): cards in review that need their confirmation, and blocked cards —
493
- * oldest first, each with when it started waiting (its last move to that status).
523
+ * What waits for the person (step 3: Needs you, and the daily summary): cards in review that need their confirmation,
524
+ * and blocked cards whose question is theirs to answer — oldest first, each with when it started waiting (its last
525
+ * move to that status).
526
+ *
527
+ * A blocked card asks the person only when the person handed it out (dispatcher kind "person"). One an instance handed
528
+ * out is that instance's to decide (step 2's wakes: its question goes there, not here). One with no dispatcher is an
529
+ * instance's own "I'm waiting" — every card the `task` tool kept before the board, an agent's card for itself, a
530
+ * reviewer's "waiting on the author" — and is no question to the person: 2.3.0-beta.24 listed 28 such cards, weeks
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.
494
534
  */
495
- waitingForPerson(): Array<{
535
+ waitingForPerson(gone?: GoneDispatcher): Array<{
496
536
  card: Card;
497
537
  since: string;
498
538
  }>;
@@ -509,7 +549,7 @@ export declare class TaskBoard {
509
549
  * Step 3, the daily summary (task-board-summary.ts): what waits for the person now, what got done and what was
510
550
  * opened in [since, until) (not cancelled), and every live main task's project and assignee.
511
551
  */
512
- summaryRead(since: string, until: string): {
552
+ summaryRead(since: string, until: string, gone?: GoneDispatcher): {
513
553
  waiting: Card[];
514
554
  done: Card[];
515
555
  created: Card[];
@@ -559,8 +599,8 @@ export declare class TaskBoard {
559
599
  */
560
600
  legacyClaim(actor: Actor, uuid: string, assignee: string): Card;
561
601
  /**
562
- * `task done`: from open or claimed. A card that needs the person's confirmation goes to review instead, so no
563
- * 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
564
604
  * (an open one is claimed first, under the same rules).
565
605
  */
566
606
  legacyComplete(actor: Actor, uuid: string, result?: string): Card;
@@ -615,8 +655,8 @@ export declare class TaskBoard {
615
655
  private applyMove;
616
656
  /**
617
657
  * A main task with sub tasks follows them: open until something starts, in progress while they run, and when they
618
- * are all done, review (or done, for one that skips the person's confirmation). Only a person moves it from review
619
- * 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).
620
660
  */
621
661
  /** What a main task's sub tasks make its status (its own, when it is blocked, cancelled or has none). */
622
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,7 +133,16 @@ 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);
137
+ /**
138
+ * #2425: whether a card waits for the person — in review needing their confirmation, or blocked on a question the person
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.
142
+ */
143
+ export function waitsForPerson(card, gone) {
144
+ return (personConfirms(card) || orphaned(card, gone)) && ((card.status === "review" && card.needsReview) || card.status === "blocked");
145
+ }
114
146
  export function requiresReview(card) {
115
147
  return card.needsReview;
116
148
  }
@@ -148,6 +180,7 @@ function toCard(r) {
148
180
  priority: r.priority ?? "normal",
149
181
  size: r.size ?? null,
150
182
  needsReview: Number(r.needs_review) === 1,
183
+ onBoard: Number(r.on_board) === 1,
151
184
  deps: parseJsonArray(r.depends_on, []).filter((d) => typeof d === "string"),
152
185
  goal: r.description ?? "",
153
186
  scope: r.scope ?? "",
@@ -250,6 +283,7 @@ export class TaskBoard {
250
283
  add("seen_entry_id", "INTEGER NOT NULL DEFAULT 0");
251
284
  add("dispatcher", "TEXT");
252
285
  add("dispatcher_kind", "TEXT");
286
+ add("on_board", "INTEGER NOT NULL DEFAULT 0"); // #2434; a row before it: its needs_review says so
253
287
  this.db.exec(`
254
288
  CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code ON tasks(code);
255
289
  CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
@@ -753,9 +787,12 @@ export class TaskBoard {
753
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);
754
788
  return { cards: rows.map(toCard), total: n };
755
789
  }
756
- /** 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
+ */
757
794
  isOnBoard(card) {
758
- 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;
759
796
  }
760
797
  /**
761
798
  * An instance's work, for `task mine`: what it is doing, what is blocked or waits for the person, what it can start
@@ -800,16 +837,39 @@ export class TaskBoard {
800
837
  mains: coordinating.map((card) => ({ card, progress: this.progress(card) })),
801
838
  };
802
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
+ }
803
847
  /**
804
- * What waits for the person (step 3: Needs you): cards in review that need their confirmation, and blocked cards —
805
- * oldest first, each with when it started waiting (its last move to that status).
848
+ * What waits for the person (step 3: Needs you, and the daily summary): cards in review that need their confirmation,
849
+ * and blocked cards whose question is theirs to answer — oldest first, each with when it started waiting (its last
850
+ * move to that status).
851
+ *
852
+ * A blocked card asks the person only when the person handed it out (dispatcher kind "person"). One an instance handed
853
+ * out is that instance's to decide (step 2's wakes: its question goes there, not here). One with no dispatcher is an
854
+ * instance's own "I'm waiting" — every card the `task` tool kept before the board, an agent's card for itself, a
855
+ * reviewer's "waiting on the author" — and is no question to the person: 2.3.0-beta.24 listed 28 such cards, weeks
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.
806
859
  */
807
- waitingForPerson() {
808
- const rows = this.db.prepare("SELECT * FROM tasks WHERE (status = 'review' AND needs_review = 1) OR status = 'blocked' 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
+ ];
809
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");
810
868
  return rows.map((card) => {
811
869
  const at = moved.get(card.uuid, card.status)?.at;
812
- return { card, since: at ?? card.submittedAt ?? card.updatedAt };
870
+ // Never updatedAt (#2425): a card edited every minute (a note, a heartbeat) would get a new `since` — and so a new
871
+ // Needs-you item id, posted again at the bottom as if it were new, once a minute.
872
+ return { card, since: at ?? card.submittedAt ?? card.createdAt };
813
873
  }).sort((a, b) => a.since.localeCompare(b.since));
814
874
  }
815
875
  /** Cards by their numbers, in one read (the ones that exist; #2341 review: the cards a summary line named). */
@@ -897,10 +957,10 @@ export class TaskBoard {
897
957
  * Step 3, the daily summary (task-board-summary.ts): what waits for the person now, what got done and what was
898
958
  * opened in [since, until) (not cancelled), and every live main task's project and assignee.
899
959
  */
900
- summaryRead(since, until) {
960
+ summaryRead(since, until, gone) {
901
961
  const between = (sql) => this.db.prepare(sql).all(since, until).map(toCard);
902
962
  return {
903
- waiting: this.waitingForPerson().map((w) => w.card),
963
+ waiting: this.waitingForPerson(gone).map((w) => w.card),
904
964
  done: between("SELECT * FROM tasks WHERE status = 'done' AND done_at >= ? AND done_at < ? ORDER BY sort"),
905
965
  created: between("SELECT * FROM tasks WHERE status != 'cancelled' AND created_at >= ? AND created_at < ? ORDER BY sort"),
906
966
  mains: this.db.prepare("SELECT project_id, assignee FROM tasks WHERE kind = 'main' AND status != 'cancelled'").all()
@@ -942,13 +1002,14 @@ export class TaskBoard {
942
1002
  const acceptance = checkAcceptance(spec.acceptance ?? []);
943
1003
  if (acceptance.length > CARD_LIMITS.acceptanceItems)
944
1004
  throw new TaskBoardError("too_long", `at most ${CARD_LIMITS.acceptanceItems} acceptance items`, "acceptance_max", [CARD_LIMITS.acceptanceItems]);
945
- const needsReview = spec.needsReview ?? kind === "main";
946
- if (!needsReview && kind === "main" && !isPerson(actor))
947
- throw new TaskBoardError("forbidden", "only a person can open a main task that skips their confirmation");
948
1005
  const now = this.nowIso();
949
1006
  const assignee = spec.assignee !== undefined ? normalizeAssignee(spec.assignee) : isPerson(actor) ? null : actor.name;
950
1007
  const opener = isPerson(actor) ? { kind: "person", name: actor.name }
951
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");
952
1013
  const card = {
953
1014
  uuid: randomUUID(),
954
1015
  code: this.nextCode(kind === "main" ? "M" : "S"),
@@ -958,10 +1019,11 @@ export class TaskBoard {
958
1019
  title,
959
1020
  status: "open",
960
1021
  assignee,
961
- dispatcher: spec.dispatcher !== undefined ? toDispatcher(spec.dispatcher, actor) : (parent?.dispatcher ?? opener),
1022
+ dispatcher,
962
1023
  priority: spec.priority ?? "normal",
963
1024
  size: spec.size ?? null,
964
1025
  needsReview,
1026
+ onBoard: true,
965
1027
  deps: this.checkDeps({ uuid: null, kind, parent: parent?.uuid ?? null }, (spec.dependsOn ?? []).map((d) => this.resolveRef(d, created))),
966
1028
  goal: checkText("the goal", spec.goal ?? "", CARD_LIMITS.goal),
967
1029
  scope: checkText("the scope", spec.scope ?? "", CARD_LIMITS.scope),
@@ -1048,11 +1110,24 @@ export class TaskBoard {
1048
1110
  set("size", patch.size);
1049
1111
  if (patch.priority !== undefined)
1050
1112
  set("priority", patch.priority);
1051
- if (patch.dispatcher !== undefined)
1052
- 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
+ }
1053
1127
  if (patch.needsReview !== undefined) {
1054
- if (ai && card.needsReview && !patch.needsReview)
1055
- 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
+ }
1056
1131
  set("needsReview", patch.needsReview);
1057
1132
  }
1058
1133
  if (patch.dependsOn !== undefined) {
@@ -1220,7 +1295,7 @@ export class TaskBoard {
1220
1295
  const now = this.nowIso();
1221
1296
  const card = {
1222
1297
  uuid: randomUUID(), code: this.nextCode("M"), kind: "main", projectId: null, parent: null, title: params.title, status: "open",
1223
- 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,
1224
1299
  // Opened for another instance: the opener handed it out (its blocked question goes to it).
1225
1300
  dispatcher: actor.kind === "instance" && params.assignee && params.assignee !== actor.name ? { kind: "instance", name: actor.name } : null,
1226
1301
  deps: this.checkDeps({ uuid: null, kind: "main", parent: null }, params.depends_on ?? []),
@@ -1273,8 +1348,8 @@ export class TaskBoard {
1273
1348
  });
1274
1349
  }
1275
1350
  /**
1276
- * `task done`: from open or claimed. A card that needs the person's confirmation goes to review instead, so no
1277
- * 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
1278
1353
  * (an open one is claimed first, under the same rules).
1279
1354
  */
1280
1355
  legacyComplete(actor, uuid, result) {
@@ -1282,8 +1357,10 @@ export class TaskBoard {
1282
1357
  const card = this.byUuid(uuid);
1283
1358
  if (!card)
1284
1359
  throw new Error(`Task "${uuid}" not found`);
1285
- if (card.status !== "claimed" && card.status !== "open") {
1286
- 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` : ""})`);
1287
1364
  }
1288
1365
  const open = this.children(card.uuid).filter((k) => !isFinished(k));
1289
1366
  if (open.length)
@@ -1292,7 +1369,7 @@ export class TaskBoard {
1292
1369
  this.event(actor, card, "updated", { changes: { result: { from: card.result, to: result } } });
1293
1370
  card.result = result;
1294
1371
  }
1295
- const to = requiresReview(card) && !isPerson(actor) ? "review" : "done";
1372
+ const to = requiresReview(card) && !confirms(card, actor) ? "review" : "done";
1296
1373
  if (this.heldToBoardRules(card, actor)) {
1297
1374
  if (card.status === "open") {
1298
1375
  this.checkManualMove(card, actor, "claimed", "", { finishing: true });
@@ -1345,9 +1422,9 @@ export class TaskBoard {
1345
1422
  if (derived)
1346
1423
  this.checkDerivedMove(card, actor, to, "");
1347
1424
  else {
1348
- if (card.status === "review" && to === "done")
1349
- throw new Error(`Task "${card.code}" is waiting for the person's confirmation`);
1350
- 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))
1351
1428
  to = "review";
1352
1429
  if (held && to !== card.status)
1353
1430
  this.checkManualMove(card, actor, to, "");
@@ -1414,7 +1491,13 @@ export class TaskBoard {
1414
1491
  this.checkOneAtATime(card, actor);
1415
1492
  }
1416
1493
  else if (from === "review") {
1417
- 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) {
1418
1501
  if (!own)
1419
1502
  throw notOwner();
1420
1503
  if (startsWork)
@@ -1458,14 +1541,24 @@ export class TaskBoard {
1458
1541
  }
1459
1542
  case "done": {
1460
1543
  if (ai) {
1461
- if (from === "review")
1462
- throw new TaskBoardError("forbidden", `${card.code} is waiting for the person's confirmation: only they finish it`);
1463
- if (from !== "claimed")
1464
- throw new TaskBoardError("invalid", `only a card in progress can be done (${card.code} is ${from})`);
1465
- if (!own)
1466
- throw notOwner();
1467
- if (requiresReview(card))
1468
- 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
+ }
1469
1562
  }
1470
1563
  else if (from === "review" && reason) {
1471
1564
  this.entry(card, "approve", actor, reason);
@@ -1671,8 +1764,8 @@ export class TaskBoard {
1671
1764
  }
1672
1765
  /**
1673
1766
  * A main task with sub tasks follows them: open until something starts, in progress while they run, and when they
1674
- * are all done, review (or done, for one that skips the person's confirmation). Only a person moves it from review
1675
- * 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).
1676
1769
  */
1677
1770
  /** What a main task's sub tasks make its status (its own, when it is blocked, cancelled or has none). */
1678
1771
  derivedStatus(main) {
@@ -1705,7 +1798,7 @@ export class TaskBoard {
1705
1798
  this.save(main);
1706
1799
  this.event(actor, main, "moved", { from, to: next, auto: true });
1707
1800
  if (next === "review")
1708
- 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`);
1709
1802
  else if (from === "done" || from === "review")
1710
1803
  this.say("back_to", [main.code, next], `${main.code} has unfinished sub tasks again; it is back to ${next}`);
1711
1804
  if (next === "done")
@@ -1824,7 +1917,8 @@ export class TaskBoard {
1824
1917
  add(card, card.assignee, "cancelled", e.actor, why);
1825
1918
  continue;
1826
1919
  }
1827
- // 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.
1828
1922
  if (d.to === "review" && !isPerson(e.actor) && requiresReview(card))
1829
1923
  add(card, dispatcherOf(), "to_confirm", e.actor);
1830
1924
  if (d.auto) {
@@ -1836,9 +1930,9 @@ export class TaskBoard {
1836
1930
  add(card, card.assignee, "start", e.actor);
1837
1931
  else if (d.from === "blocked" && d.to === "claimed")
1838
1932
  add(card, card.assignee, "answered", e.actor, comments.get(card.uuid));
1839
- 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"))
1840
1934
  add(card, card.assignee, "sent_back", e.actor, d.reason);
1841
- else if (d.from === "review" && d.to === "done" && isPerson(e.actor)) {
1935
+ else if (d.from === "review" && d.to === "done") {
1842
1936
  add(card, card.assignee, "approved", e.actor, d.reason);
1843
1937
  add(card, dispatcherOf(), "approved", e.actor, d.reason);
1844
1938
  }
@@ -2071,8 +2165,8 @@ export class TaskBoard {
2071
2165
  }
2072
2166
  insert(card) {
2073
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,
2074
- scope, acceptance, context, state, blocked_reason, size, needs_review, version, sort, created_at, updated_at, started_at, submitted_at, done_at, seen_entry_id)
2075
- 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);
2076
2170
  }
2077
2171
  /** Empty text is stored as NULL in the columns the `task` tool has always had (description, result, depends_on). */
2078
2172
  save(card) {