@songsid/agend 2.3.0-beta.19 → 2.3.0-beta.20

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 (81) hide show
  1. package/dist/agent-cli-instructions.md +4 -0
  2. package/dist/agent-cli.js +25 -0
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/channel/mcp-tools.js +2 -1
  5. package/dist/channel/mcp-tools.js.map +1 -1
  6. package/dist/config-validator.js +19 -1
  7. package/dist/config-validator.js.map +1 -1
  8. package/dist/cross-instance-notice.d.ts +7 -0
  9. package/dist/cross-instance-notice.js +10 -0
  10. package/dist/cross-instance-notice.js.map +1 -1
  11. package/dist/daemon.js +4 -0
  12. package/dist/daemon.js.map +1 -1
  13. package/dist/delivery-outbox.js +5 -0
  14. package/dist/delivery-outbox.js.map +1 -1
  15. package/dist/fleet-manager.d.ts +26 -0
  16. package/dist/fleet-manager.js +115 -14
  17. package/dist/fleet-manager.js.map +1 -1
  18. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  19. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +25 -1
  20. package/dist/instance-config-impact.js +4 -0
  21. package/dist/instance-config-impact.js.map +1 -1
  22. package/dist/log-steps.d.ts +1 -0
  23. package/dist/log-steps.js +15 -0
  24. package/dist/log-steps.js.map +1 -1
  25. package/dist/outbound-handlers.d.ts +3 -0
  26. package/dist/outbound-handlers.js +20 -4
  27. package/dist/outbound-handlers.js.map +1 -1
  28. package/dist/outbound-schemas.d.ts +33 -0
  29. package/dist/outbound-schemas.js +25 -8
  30. package/dist/outbound-schemas.js.map +1 -1
  31. package/dist/public-web-gateway.js +5 -2
  32. package/dist/public-web-gateway.js.map +1 -1
  33. package/dist/scheduler/db.d.ts +40 -13
  34. package/dist/scheduler/db.js +123 -89
  35. package/dist/scheduler/db.js.map +1 -1
  36. package/dist/scheduler/task-board-text.d.ts +26 -0
  37. package/dist/scheduler/task-board-text.js +135 -0
  38. package/dist/scheduler/task-board-text.js.map +1 -0
  39. package/dist/scheduler/task-board.d.ts +650 -0
  40. package/dist/scheduler/task-board.js +1938 -0
  41. package/dist/scheduler/task-board.js.map +1 -0
  42. package/dist/scheduler/types.d.ts +6 -1
  43. package/dist/search/search-docs.d.ts +58 -0
  44. package/dist/search/search-docs.js +63 -0
  45. package/dist/search/search-docs.js.map +1 -0
  46. package/dist/search/search-index.d.ts +141 -0
  47. package/dist/search/search-index.js +440 -0
  48. package/dist/search/search-index.js.map +1 -0
  49. package/dist/search/search-store.d.ts +122 -0
  50. package/dist/search/search-store.js +236 -0
  51. package/dist/search/search-store.js.map +1 -0
  52. package/dist/search/search-worker.d.ts +1 -0
  53. package/dist/search/search-worker.js +163 -0
  54. package/dist/search/search-worker.js.map +1 -0
  55. package/dist/task-board-tool.d.ts +25 -0
  56. package/dist/task-board-tool.js +154 -0
  57. package/dist/task-board-tool.js.map +1 -0
  58. package/dist/task-board-waker.d.ts +44 -0
  59. package/dist/task-board-waker.js +128 -0
  60. package/dist/task-board-waker.js.map +1 -0
  61. package/dist/types.d.ts +15 -0
  62. package/dist/ui/fleet-task-card.js +436 -0
  63. package/dist/ui/fleet-task-common.js +448 -0
  64. package/dist/ui/fleet-tasks.js +535 -0
  65. package/dist/ui/panel-chat.js +8 -0
  66. package/dist/ui/panel-fleet.js +21 -65
  67. package/dist/ui/search-dialog.js +349 -0
  68. package/dist/ui/shared/app-i18n.js +3 -0
  69. package/dist/ui/shared/app-shell.js +43 -1
  70. package/dist/ui/shared/app-stream.js +2 -1
  71. package/dist/ui/shared/app.css +270 -2
  72. package/dist/ui/shared/app.js +3 -1
  73. package/dist/ui/shared/ui-toast.js +16 -3
  74. package/dist/web-api.d.ts +14 -4
  75. package/dist/web-api.js +408 -10
  76. package/dist/web-api.js.map +1 -1
  77. package/dist/web-chat-history.d.ts +2 -0
  78. package/dist/web-chat-history.js +2 -0
  79. package/dist/web-chat-history.js.map +1 -1
  80. package/dist/workflow-templates/default.md +3 -0
  81. package/package.json +1 -1
@@ -33,7 +33,8 @@
33
33
  // #1918: traffic — a message between instances moved; delegation — a task opened, or its report closed it (SSE only: the
34
34
  // org canvas reads /ui/traffic when it has no stream).
35
35
  // #2233: backend_login — a dashboard sign-in started, moved or ended (no details: the page reads its own view).
36
- const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "steps_tick", "clock", "traffic", "delegation", "backend_login"];
36
+ // tasks — the task board changed: the card numbers a write touched (SSE only: the board reads again when it is shown).
37
+ const EVENTS = ["status", "message", "agent_reply_live", "delivery", "deliveries", "prompt", "prompts", "prompt_resolved", "activity", "needs", "reply_buttons", "steps_tick", "clock", "traffic", "delegation", "backend_login", "tasks"];
37
38
  const MAX_HELD = 1000; // live events held during one catch-up read; more → read again afterwards
38
39
  const RETRY_DELAYS = [1000, 2000, 4000]; // after the first failed catch-up read; then it waits for Retry
39
40
  const POLL_LIMIT_MS = 10000; // a poll that has not answered by then counts as failed
@@ -834,6 +834,9 @@ img.amb-p { object-fit: contain; }
834
834
  .oc-keep { left: var(--s-2); right: var(--s-2); transform: none; max-width: none; flex-wrap: nowrap; }
835
835
  .oc-keep-text { flex: 1; flex-direction: column; align-items: flex-start; gap: 0; }
836
836
  }
837
+ /* #2299: the toasts are a manual popover (ui-toast.js), so they stand in the top layer above an open dialog's backdrop;
838
+ the popover's own box (inset 0, margin, border, background) is undone here, before any placement below. */
839
+ :where(.toasts) { inset: auto; margin: 0; padding: 0; border: 0; background: none; color: inherit; overflow: visible; width: auto; height: auto; }
837
840
  .toasts { position: fixed; bottom: var(--s-6); right: var(--s-6); z-index: 100; display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-2); pointer-events: none; }
838
841
  .toast { padding: 10px var(--s-4); max-width: min(420px, calc(100vw - 32px)); border-radius: var(--r-md); font-size: var(--fs-sm); font-weight: var(--fw-medium); box-shadow: var(--shadow-2); background: var(--accent-fill); color: var(--on-accent); animation: toast-in var(--t-med) var(--ease); }
839
842
  .toast.err { background: var(--danger-fill); color: var(--on-danger); }
@@ -860,7 +863,12 @@ img.amb-p { object-fit: contain; }
860
863
  .panel-head .sb-new { display: none; }
861
864
  .only-narrow { display: inline-flex; }
862
865
  .thread { padding: var(--s-3) var(--s-3) var(--s-2); gap: var(--s-5); }
863
- .col { padding: var(--s-3) var(--s-3) var(--s-8); }
866
+ /* #2299: a phone's side gutter is 16 px — the content column, the header and the toasts alike. */
867
+ .col { padding: var(--s-3) var(--s-4) var(--s-8); }
868
+ .panel-head { padding: max(var(--s-1), env(safe-area-inset-top)) max(var(--s-4), env(safe-area-inset-right)) var(--s-1) max(var(--s-4), env(safe-area-inset-left)); }
869
+ /* #2299: the header's controls are a full touch target (its row is 52 px: 44 px plus its padding). */
870
+ .panel-head .icon-btn { min-width: var(--tap); min-height: var(--tap); }
871
+ .panel-head .btn, .panel-head .seg-item, .panel-head select { min-height: var(--tap); }
864
872
  .dock { padding: 0 max(var(--s-2), env(safe-area-inset-right)) var(--s-2) max(var(--s-2), env(safe-area-inset-left)); }
865
873
  .hint { display: none; }
866
874
  .msg.user { max-width: 88%; }
@@ -876,7 +884,7 @@ img.amb-p { object-fit: contain; }
876
884
  .tab-badge { position: absolute; top: 6px; left: calc(50% + 6px); min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--r-full); background: var(--danger-fill); color: var(--on-danger); font-size: 10px; line-height: 16px; text-align: center; }
877
885
  .main { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }
878
886
  .kb-open .main { padding-bottom: 0; }
879
- .toasts { top: calc(60px + env(safe-area-inset-top)); bottom: auto; left: var(--s-3); right: var(--s-3); align-items: stretch; }
887
+ .toasts { top: calc(60px + env(safe-area-inset-top)); bottom: auto; left: var(--s-4); right: var(--s-4); align-items: stretch; }
880
888
  .tour { left: var(--s-4); right: var(--s-4); width: auto; top: calc(64px + env(safe-area-inset-top)); }
881
889
  .dlg { width: 100vw; max-width: 100vw; margin: auto 0 0; border-radius: var(--r-lg) var(--r-lg) 0 0; max-height: 90vh; }
882
890
  .dlg-box { max-height: 90vh; }
@@ -1737,3 +1745,263 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
1737
1745
  .panel-head:has(> .chat-fx-notice) { flex-wrap: wrap; }
1738
1746
  .chat-fx-notice { order: 10; flex: 1 1 100%; min-width: 0; max-width: 100%; font-size: var(--fs-sm); overflow-wrap: anywhere; }
1739
1747
  .chat-fx-notice > span { display: block; }
1748
+
1749
+ /* ── #2293: global search — the sidebar row's shortcut, the header's button (collapsed sidebar, phone), the dialog ── */
1750
+ .kbd-hint { font: inherit; font-size: var(--fs-xs); color: var(--text-3); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 0 var(--s-1); line-height: 18px; white-space: nowrap; }
1751
+ .panel-head .sb-search { display: none; }
1752
+ .sb-collapsed .panel-head .sb-search { display: inline-flex; }
1753
+ .chip-btn.on { color: var(--text); background: var(--surface-2); font-weight: var(--fw-semibold); }
1754
+ .dlg.search-dlg { width: min(680px, calc(100vw - 32px)); margin: 10vh auto auto; max-height: 76vh; }
1755
+ .search-box { display: flex; flex-direction: column; max-height: 76vh; }
1756
+ .search-head { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4); border-bottom: 1px solid var(--border); color: var(--text-2); }
1757
+ .search-input { flex: 1; min-width: 0; border: 0; background: transparent; color: var(--text); font-size: max(16px, var(--fs-lg)); padding: var(--s-2) 0; }
1758
+ .search-input:focus { outline: none; }
1759
+ .search-filters-toggle { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
1760
+ .search-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-4); padding: var(--s-2) var(--s-4); border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
1761
+ .search-field { display: inline-flex; align-items: center; gap: var(--s-2); color: var(--text-2); }
1762
+ .search-field select { max-width: 260px; }
1763
+ /* The filter chips' "on" state in the accent, so it reads apart from the hover and from the folded toggle. */
1764
+ .search-chips .chip-btn.on { background: var(--accent-dim); color: var(--accent); }
1765
+ .search-chips { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 2px; }
1766
+ .search-mine { display: inline-flex; align-items: center; gap: 6px; margin-left: var(--s-2); color: var(--text-2); cursor: pointer; }
1767
+ .search-mine input { margin: 0; accent-color: var(--accent); }
1768
+ .search-body { flex: 1; min-height: 140px; overflow-y: auto; padding: var(--s-2); }
1769
+ .search-foot { padding: var(--s-2) var(--s-4); border-top: 1px solid var(--border); color: var(--text-3); font-size: var(--fs-xs); }
1770
+ .search-hint, .search-status, .search-note { margin: 0; padding: var(--s-2) var(--s-3); color: var(--text-2); font-size: var(--fs-sm); }
1771
+ .search-status.bad { color: var(--danger); }
1772
+ .search-note { color: var(--warn); }
1773
+ .search-recent-head { display: flex; align-items: center; justify-content: space-between; padding: var(--s-2) var(--s-3) 0; color: var(--text-3); font-size: var(--fs-xs); }
1774
+ .search-recent ul { list-style: none; margin: 0; padding: 0; }
1775
+ .search-recent-item { display: flex; align-items: center; gap: var(--s-2); width: 100%; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--text); padding: var(--s-2) var(--s-3); font-size: var(--fs-sm); text-align: left; cursor: pointer; }
1776
+ .search-recent-item:hover { background: var(--surface-2); }
1777
+ .search-recent-item svg { color: var(--text-3); flex: none; }
1778
+ .search-results.stale { opacity: 0.6; }
1779
+ .search-group + .search-group { margin-top: var(--s-2); }
1780
+ .search-group-head { display: flex; justify-content: space-between; gap: var(--s-2); padding: var(--s-2) var(--s-3) var(--s-1); font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-2); }
1781
+ .search-count { color: var(--text-3); font-weight: var(--fw-regular); }
1782
+ .search-row { padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); cursor: pointer; }
1783
+ .search-row.on { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
1784
+ .search-row-top { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--text-2); }
1785
+ .search-who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1786
+ .search-kind { color: var(--text-3); }
1787
+ .search-time { color: var(--text-3); white-space: nowrap; }
1788
+ .search-badge { flex: none; font-size: 10px; line-height: 16px; font-weight: var(--fw-semibold); padding: 0 6px; border-radius: var(--r-full); background: var(--surface-2); color: var(--text-2); }
1789
+ .search-badge.chat { background: var(--accent-dim); color: var(--accent); }
1790
+ .search-badge.xmsg { background: var(--warn-bg); color: var(--warn); }
1791
+ .search-badge.task { background: var(--ok-bg); color: var(--ok); }
1792
+ .search-snippet { margin-top: 2px; font-size: var(--fs-sm); color: var(--text); overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
1793
+ .search-snippet mark, .search-ctx-text mark { background: var(--accent-dim); color: inherit; border-radius: 2px; padding: 0 1px; }
1794
+ .search-detail-head { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) var(--s-1) var(--s-2); font-size: var(--fs-sm); }
1795
+ .search-detail-head .chip-btn { display: inline-flex; align-items: center; gap: 4px; }
1796
+ .search-ctx { list-style: none; margin: 0; padding: 0 var(--s-1); display: flex; flex-direction: column; gap: var(--s-1); }
1797
+ .search-ctx-line { padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); }
1798
+ .search-ctx-line.here { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
1799
+ .search-ctx-text { margin-top: 2px; font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
1800
+ .search-detail-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s-2); padding: var(--s-3) var(--s-1) var(--s-1); }
1801
+ @media (max-width: 899px) {
1802
+ .panel-head .sb-search { display: inline-flex; }
1803
+ .dlg.search-dlg { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; }
1804
+ .search-box { height: 100%; max-height: 100dvh; padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); box-sizing: border-box; }
1805
+ .search-foot { display: none; }
1806
+ .search-row { padding: var(--s-3); }
1807
+ .search-filters-toggle, .search-chips .chip-btn { min-height: var(--tap); }
1808
+ /* The task board (Fleet › Tasks: fleet-tasks.js, fleet-task-card.js). Status colours: to do neutral, in progress the
1809
+ accent, blocked danger, waiting for you warn, done ok. Bars are SVG rects (no style attribute: the CSP). */
1810
+ .tb { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
1811
+ .tb-grow { flex: 1; }
1812
+ .tb-head { flex-wrap: wrap; }
1813
+ .tb-code { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
1814
+ .tb-who { font-size: var(--fs-xs); color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 14em; }
1815
+ .tb-link { color: var(--accent); font-size: var(--fs-xs); background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
1816
+ .tb-link:hover { text-decoration: underline; }
1817
+ .tb-select { min-height: 30px; padding: 0 var(--s-2); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg); color: var(--text); font-size: var(--fs-xs); max-width: 100%; }
1818
+ .tb-select:disabled, .tb-check input:disabled { opacity: 0.6; cursor: default; }
1819
+ .tb-check { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); color: var(--text-2); cursor: pointer; }
1820
+ .tb-check input { width: auto; min-height: 0; margin: 0; }
1821
+ .tb-form-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3); }
1822
+ .tb-chev { transition: transform var(--t-fast) var(--ease); transform: rotate(-90deg); }
1823
+ .tb-chev.open { transform: rotate(0deg); }
1824
+
1825
+ .tb-pill, .tb-tag, .tb-flag { display: inline-flex; align-items: center; gap: 2px; font-size: 11px; font-weight: var(--fw-semibold); padding: 1px var(--s-2); border-radius: var(--r-full); background: var(--surface-2); color: var(--text-2); white-space: nowrap; }
1826
+ .tb-pill.st-claimed, .tb-tag.st-claimed { background: var(--accent-dim); color: var(--accent); }
1827
+ .tb-pill.st-blocked, .tb-tag.st-blocked, .tb-flag.st-blocked, .tb-tag.p-urgent { background: var(--danger-bg); color: var(--danger); }
1828
+ .tb-pill.st-review, .tb-tag.st-review, .tb-flag.st-review, .tb-tag.p-high { background: var(--warn-bg); color: var(--warn); }
1829
+ .tb-pill.st-done, .tb-tag.st-done { background: var(--ok-bg); color: var(--ok); }
1830
+ .tb-pill.st-cancelled { color: var(--text-3); text-decoration: line-through; }
1831
+ .tb-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--text-3); flex-shrink: 0; }
1832
+ .tb-col.st-claimed > .tb-col-head > .tb-dot, .tb-dot.st-claimed { background: var(--accent); }
1833
+ .tb-col.st-blocked > .tb-col-head > .tb-dot, .tb-dot.st-blocked { background: var(--danger); }
1834
+ .tb-col.st-review > .tb-col-head > .tb-dot, .tb-dot.st-review { background: var(--warn); }
1835
+ .tb-col.st-done > .tb-col-head > .tb-dot, .tb-dot.st-done { background: var(--ok); }
1836
+
1837
+ .tb-bar { width: 96px; height: 6px; border-radius: var(--r-full); overflow: hidden; flex-shrink: 0; }
1838
+ .tb-bar.wide { width: 100%; flex-shrink: 1; }
1839
+ .tb-bar-track { fill: var(--surface-2); }
1840
+ .tb-seg.st-done { fill: var(--ok); }
1841
+ .tb-seg.st-review { fill: var(--warn); }
1842
+ .tb-seg.st-claimed { fill: var(--accent); }
1843
+ .tb-seg.st-blocked { fill: var(--danger); }
1844
+ .tb-pct { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
1845
+
1846
+ /* Home: what waits for the person, then the projects. */
1847
+ .tb-home { display: flex; flex-direction: column; gap: var(--s-4); }
1848
+ /* The public link: the board is shown, not changed. */
1849
+ .tb-readonly { display: flex; align-items: center; gap: var(--s-2); margin: 0 0 var(--s-3); padding: var(--s-2) var(--s-3); border: 1px solid var(--border); border-radius: var(--r-md); background: var(--surface); color: var(--text-2); font-size: var(--fs-sm); }
1850
+ .tb-home > .list-head { margin: 0; }
1851
+ .tb-section-title { display: flex; align-items: center; gap: var(--s-2); margin: 0 0 var(--s-2); font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-2); }
1852
+ .tb-waiting { padding: var(--s-3); border: 1px solid var(--warn-line); border-radius: var(--r-lg); }
1853
+ .tb-wait-row { flex-wrap: wrap; align-items: flex-start; }
1854
+ /* #2299: the pill sits on the title's first line (one baseline); the reason, under the title; the actions after. */
1855
+ .tb-wait-main { flex: 1 1 320px; min-width: 0; display: flex; align-items: baseline; gap: var(--s-2) var(--s-3); }
1856
+ .tb-wait-main > .tb-pill { flex-shrink: 0; min-width: 112px; justify-content: center; }
1857
+ .tb-wait-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-left: auto; }
1858
+ .tb-wait-actions .btn { min-width: 88px; padding: 0 var(--s-4); }
1859
+ .tb-wait-title { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); background: none; border: 0; padding: 0; color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-sm); }
1860
+ .tb-wait-title:hover { text-decoration: underline; }
1861
+ .tb-wait-reason { flex-basis: 100%; margin-top: 2px; color: var(--danger); font-size: var(--fs-xs); line-height: var(--lh-xs); overflow-wrap: anywhere; }
1862
+ .tb-projects { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--s-3); }
1863
+ .tb-project { position: relative; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface); min-width: 0; }
1864
+ .tb-project:hover { border-color: var(--border-strong); }
1865
+ .tb-project.is-archived { opacity: 0.7; }
1866
+ .tb-project-open { display: flex; flex-direction: column; gap: var(--s-2); width: 100%; padding: var(--s-4); padding-right: calc(var(--s-4) + 32px); background: none; border: 0; color: inherit; text-align: left; cursor: pointer; border-radius: inherit; }
1867
+ .tb-project-name { font-weight: var(--fw-bold); font-size: var(--fs-md); overflow-wrap: anywhere; }
1868
+ .tb-project-desc { color: var(--text-2); font-size: var(--fs-xs); overflow-wrap: anywhere; }
1869
+ .tb-project-pct { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); }
1870
+ .tb-project-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-3); font-size: var(--fs-xs); color: var(--text-2); }
1871
+ .tb-project-menu { position: absolute; top: var(--s-2); right: var(--s-2); }
1872
+ .tb-archived { display: flex; flex-direction: column; gap: var(--s-2); align-items: flex-start; }
1873
+ .tb-archived > .tb-projects { align-self: stretch; }
1874
+
1875
+ .tb-search { position: relative; min-width: 180px; flex: 0 1 280px; }
1876
+ .tb-search input { width: 100%; min-height: 34px; padding: 0 var(--s-3) 0 30px; border: 1px solid var(--border); border-radius: var(--r-full); background: var(--bg); color: var(--text); font-size: var(--fs-sm); }
1877
+ .tb-search input:focus { border-color: var(--focus); outline: none; box-shadow: 0 0 0 3px var(--accent-dim); }
1878
+ .tb-search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-3); pointer-events: none; }
1879
+ .tb-search-results { position: absolute; z-index: 5; top: calc(100% + 4px); right: 0; width: min(440px, calc(100vw - 32px)); max-height: 60vh; overflow-y: auto; margin: 0; padding: var(--s-1); list-style: none; background: var(--bg); border: 1px solid var(--border-strong); border-radius: var(--r-md); box-shadow: var(--shadow-2); }
1880
+ .tb-search-results button { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); width: 100%; padding: var(--s-2); background: none; border: 0; border-radius: var(--r-sm); color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-sm); }
1881
+ .tb-search-results li.active button, .tb-search-results button:hover { background: var(--surface-2); }
1882
+ .tb-search-title { flex: 1; min-width: 0; overflow-wrap: anywhere; }
1883
+ .tb-search-where { flex-basis: 100%; font-size: var(--fs-xs); overflow-wrap: anywhere; }
1884
+ .tb-search-none { padding: var(--s-2); font-size: var(--fs-sm); }
1885
+
1886
+ /* A project: its header, then one column per status. */
1887
+ .tb-bar-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
1888
+ .tb-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); min-width: 0; }
1889
+ .tb-title h2 { margin: 0; font-size: var(--fs-lg); overflow-wrap: anywhere; }
1890
+ .tb-stats { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); }
1891
+ .tb-scroll { overflow-x: auto; margin: 0 calc(-1 * var(--s-2)); padding: 0 var(--s-2) var(--s-2); }
1892
+ /* #2299: 176 px a column fits all five beside the sidebar from 1280 px wide (220 px cut Done off at 1366). */
1893
+ .tb-columns { display: grid; grid-template-columns: repeat(5, minmax(176px, 1fr)); gap: var(--s-3); align-items: start; min-width: min-content; }
1894
+ .tb-col { display: flex; flex-direction: column; gap: var(--s-2); min-height: 120px; padding: var(--s-2); border-radius: var(--r-lg); background: var(--surface); border: 1px dashed transparent; }
1895
+ .tb-col.can-drop { border-color: var(--border); }
1896
+ .tb-col.over { border-color: var(--accent); background: var(--accent-dim); }
1897
+ .tb-col-head { display: flex; align-items: center; gap: var(--s-2); margin: 0; padding: var(--s-1) var(--s-1) 0; font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-2); text-transform: uppercase; letter-spacing: 0.04em; }
1898
+ .tb-col-head .tb-link { text-transform: none; letter-spacing: 0; font-weight: var(--fw-regular); }
1899
+ .tb-count { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--text-3); }
1900
+ .tb-drop-line { height: 3px; border-radius: 2px; background: var(--accent); }
1901
+
1902
+ .tb-tile { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-3); border-radius: var(--r-md); background: var(--bg); border: 1px solid var(--border); border-left: 3px solid var(--text-3); cursor: pointer; font-size: var(--fs-sm); min-width: 0; }
1903
+ .tb-tile:hover { border-color: var(--border-strong); }
1904
+ .tb-tile.st-claimed { border-left-color: var(--accent); }
1905
+ .tb-tile.st-blocked { border-left-color: var(--danger); }
1906
+ .tb-tile.st-review { border-left-color: var(--warn); }
1907
+ .tb-tile.st-done { border-left-color: var(--ok); }
1908
+ .tb-tile.dragging { opacity: 0.4; }
1909
+ .tb-tile.flash { animation: tb-flash 1.6s var(--ease); }
1910
+ @keyframes tb-flash { from { box-shadow: 0 0 0 3px var(--accent-dim); background: var(--accent-dim); } to { box-shadow: none; } }
1911
+ .tb-tile-top { display: flex; align-items: center; gap: var(--s-2); }
1912
+ .tb-size { font-size: var(--fs-xs); color: var(--text-3); }
1913
+ /* The tile's control: its title, a plain-looking button (Enter opens the card, Alt+arrows move it). */
1914
+ .tb-tile-title { display: block; width: 100%; padding: 0; background: none; border: 0; border-radius: var(--r-sm); color: var(--text); font: inherit; font-weight: var(--fw-medium); text-align: left; cursor: pointer; overflow-wrap: anywhere; }
1915
+ .tb-tile-title:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
1916
+ .tb-tile:has(.tb-tile-title:focus-visible) { border-color: var(--focus); }
1917
+ .tb-tile-note { display: flex; gap: var(--s-1); align-items: flex-start; font-size: var(--fs-xs); color: var(--text-2); overflow-wrap: anywhere; }
1918
+ /* A main task's sub task at work now (its worker in the foot), and what a card waiting for you still lacks. */
1919
+ .tb-tile-now { display: flex; align-items: baseline; gap: var(--s-1); min-width: 0; font-size: var(--fs-xs); color: var(--text-2); }
1920
+ .tb-now-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1921
+ .tb-now-label { flex-shrink: 0; font-size: 11px; font-weight: var(--fw-semibold); color: var(--text-2); }
1922
+ .tb-now-label.st-claimed { color: var(--accent); }
1923
+ .tb-now-label.st-blocked { color: var(--danger); }
1924
+ .tb-now-label.st-review { color: var(--warn); }
1925
+ .tb-tag.wrapup { background: var(--warn-bg); color: var(--warn); }
1926
+ .tb-wait-reason.wrapup, .tc-hint.wrapup { color: var(--warn); }
1927
+ .tb-wait-asked { flex-basis: 100%; font-size: var(--fs-xs); }
1928
+ .tc-now { display: flex; align-items: baseline; gap: var(--s-2); width: 100%; padding: 0; background: none; border: 0; color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-sm); }
1929
+ .tc-now:hover .tc-child-title { text-decoration: underline; }
1930
+ .tb-tile-note.st-blocked { color: var(--danger); }
1931
+ .tb-tile-progress { display: flex; align-items: center; gap: var(--s-2); width: 100%; padding: 0; background: none; border: 0; color: var(--text-2); font-size: var(--fs-xs); cursor: pointer; }
1932
+ .tb-tile-progress > span { white-space: nowrap; }
1933
+ .tb-tile-progress > .tb-bar { flex: 1; min-width: 40px; }
1934
+ .tb-subs { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); padding-top: var(--s-2); }
1935
+ .tb-subs button { display: flex; align-items: center; gap: var(--s-2); width: 100%; padding: 2px var(--s-1); background: none; border: 0; border-radius: var(--r-sm); color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-xs); }
1936
+ .tb-subs button:hover { background: var(--surface-2); }
1937
+ .tb-sub-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1938
+ .tb-sub-blocked { padding-left: 22px; font-size: var(--fs-xs); color: var(--danger); overflow-wrap: anywhere; }
1939
+ .tb-sub-more { font-size: var(--fs-xs); padding-left: var(--s-1); }
1940
+ .tb-tile-foot { display: flex; align-items: center; gap: var(--s-2); }
1941
+ .tb-tile-foot > .tb-who { flex: 1; min-width: 0; max-width: none; }
1942
+ .tb-tile-stats { display: inline-flex; gap: var(--s-2); font-size: var(--fs-xs); color: var(--text-3); }
1943
+ .tb-tile-stats > span { display: inline-flex; align-items: center; gap: 2px; }
1944
+ .tb-tile-stats > .all { color: var(--ok); }
1945
+ .tb-tile-stats > .reply { color: var(--accent); }
1946
+
1947
+ /* One card, in the app's dialog. */
1948
+ .tc { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
1949
+ .tc-head { display: flex; align-items: center; gap: var(--s-2); }
1950
+ .tc-title { flex: 1; min-width: 0; padding: var(--s-1) var(--s-2); margin-left: calc(-1 * var(--s-2)); background: none; border: 1px solid transparent; border-radius: var(--r-sm); color: var(--text); font: inherit; font-size: var(--fs-lg); font-weight: var(--fw-semibold); text-align: left; cursor: text; overflow-wrap: anywhere; }
1951
+ .tc-title:hover { border-color: var(--border); }
1952
+ .tc-title-input { flex: 1; min-width: 0; padding: var(--s-1) var(--s-2); border: 1px solid var(--focus); border-radius: var(--r-sm); background: var(--bg); color: var(--text); font-size: max(16px, var(--fs-lg)); font-weight: var(--fw-semibold); }
1953
+ .tc-ver { font-size: var(--fs-xs); }
1954
+ .tc-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); padding-bottom: var(--s-3); border-bottom: 1px solid var(--border); }
1955
+ .tc-hint { font-size: var(--fs-xs); }
1956
+ .tc-meta { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--s-2) var(--s-4); margin: 0; font-size: var(--fs-sm); align-items: center; }
1957
+ .tc-meta dt { color: var(--text-2); }
1958
+ .tc-meta dd { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin: 0; min-width: 0; }
1959
+ .tc-value { color: var(--text); overflow-wrap: anywhere; }
1960
+ .tc-dep { padding: 1px var(--s-2); border: 1px solid var(--ok); border-radius: var(--r-full); background: none; color: var(--ok); font-family: var(--font-mono); font-size: var(--fs-xs); cursor: pointer; }
1961
+ .tc-dep.waiting { border-color: var(--warn); color: var(--warn); }
1962
+ .tc-field h3 { margin: 0 0 var(--s-1); font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-2); text-transform: uppercase; letter-spacing: 0.04em; }
1963
+ .tc-field.tone-blocked h3 { color: var(--danger); }
1964
+ .tc-text { display: block; width: 100%; padding: var(--s-2); background: none; border: 1px solid transparent; border-radius: var(--r-sm); color: var(--text); font: inherit; font-size: var(--fs-sm); text-align: left; white-space: pre-wrap; overflow-wrap: anywhere; cursor: text; }
1965
+ .tc-text:hover { border-color: var(--border); background: var(--surface); }
1966
+ .tc-text.empty { color: var(--text-3); }
1967
+ .tc-edit textarea, .tc-composer textarea, .tc-quick-add { width: 100%; padding: var(--s-2); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg); color: var(--text); font-size: var(--fs-sm); resize: vertical; }
1968
+ .tc-edit textarea:focus, .tc-composer textarea:focus, .tc-quick-add:focus { border-color: var(--focus); outline: none; box-shadow: 0 0 0 3px var(--accent-dim); }
1969
+ .tc-edit-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-top: var(--s-1); font-size: var(--fs-xs); }
1970
+ .tc-counter { font-variant-numeric: tabular-nums; color: var(--text-3); }
1971
+ .tc-counter.over { color: var(--danger); font-weight: var(--fw-semibold); }
1972
+ .tc-checklist { display: flex; flex-direction: column; gap: var(--s-1); margin: 0 0 var(--s-1); padding: 0; list-style: none; }
1973
+ .tc-checklist .done { color: var(--text-3); text-decoration: line-through; }
1974
+ .tc-children { display: flex; flex-direction: column; gap: 2px; margin: 0 0 var(--s-2); padding: 0; list-style: none; }
1975
+ .tc-children button { display: flex; align-items: center; gap: var(--s-2); width: 100%; padding: var(--s-1) var(--s-2); background: none; border: 0; border-radius: var(--r-sm); color: var(--text); text-align: left; cursor: pointer; font-size: var(--fs-sm); }
1976
+ .tc-children button:hover { background: var(--surface); }
1977
+ .tc-child-title { flex: 1; min-width: 0; overflow-wrap: anywhere; }
1978
+ .tc-tabs { padding: 0; margin: var(--s-2) 0 0; max-width: none; }
1979
+ .tc-feed { display: flex; flex-direction: column; gap: var(--s-1); margin: 0; padding: 0; list-style: none; font-size: var(--fs-sm); overflow-wrap: anywhere; }
1980
+ .tc-comments { display: flex; flex-direction: column; gap: var(--s-2); }
1981
+ .tc-comment { padding: var(--s-2) var(--s-3); border-radius: var(--r-md); background: var(--surface); font-size: var(--fs-sm); }
1982
+ .tc-comment.person { background: var(--accent-dim); }
1983
+ .tc-comment-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-bottom: 2px; font-size: var(--fs-xs); }
1984
+ .tc-comment-body { white-space: pre-wrap; overflow-wrap: anywhere; }
1985
+ .tc-composer { display: flex; gap: var(--s-2); align-items: flex-end; }
1986
+
1987
+ @media (max-width: 899px) {
1988
+ /* A phone stacks the columns: no sideways scrolling, and a card moves through its dialog's "Move to…". */
1989
+ .tb-scroll { overflow-x: visible; margin: 0; padding: 0; }
1990
+ .tb-columns { grid-template-columns: minmax(0, 1fr); min-width: 0; }
1991
+ .tb-col { min-height: 0; }
1992
+ .tb-form-row { grid-template-columns: minmax(0, 1fr); }
1993
+ .tb-search { flex: 1 1 100%; }
1994
+ /* #2299: a phone's waiting row: pill and title on one line, the actions under them (assignee left, buttons right). */
1995
+ .tb-wait-main > .tb-pill { min-width: 0; }
1996
+ .tb-wait-actions { flex-basis: 100%; margin-left: 0; }
1997
+ .tb-wait-actions > .muted { margin-right: auto; }
1998
+ /* #2299: on the board its toasts come in above the tabs, not over its title, search and progress — unless a card's
1999
+ sheet is open (its buttons are at the bottom). */
2000
+ .shell:has(.tb-home, .tb-board-page):not(:has(dialog[open])) .toasts { top: auto; bottom: calc(56px + env(safe-area-inset-bottom) + var(--s-3)); }
2001
+ /* …and they do not pile down the board: an older confirmation gives way to two newer toasts; an error never does. */
2002
+ .shell:has(.tb-home, .tb-board-page) .toasts > .toast.ok:nth-last-child(n+3) { display: none; }
2003
+ }
2004
+ @media (prefers-reduced-motion: reduce) {
2005
+ .tb-tile.flash { animation: none; }
2006
+ .tb-chev { transition: none; }
2007
+ }
@@ -8,7 +8,7 @@
8
8
  // #1665 first: the host's preferences go into this browser's cache before any module below reads its key.
9
9
  import { bootPrefs } from "./app-prefs.js";
10
10
  import { html, render } from "./app-html.js";
11
- import { Shell, handleKey, showDialog, closeDrawer, openDrawer, shellStore, retryable, setNewInstanceOpener } from "./app-shell.js";
11
+ import { Shell, handleKey, handleSearchKey, showDialog, closeDrawer, openDrawer, shellStore, retryable, setNewInstanceOpener } from "./app-shell.js";
12
12
  import { installSwipe } from "./app-swipe.js";
13
13
  import { startRouter, navStore, navigate } from "./app-nav.js";
14
14
  import { createStream } from "./app-stream.js";
@@ -123,6 +123,8 @@ if (!guest) installOwnerCss();
123
123
  }
124
124
 
125
125
  document.addEventListener("keydown", handleKey);
126
+ // #2293: Ctrl+K / ⌘K (and Ctrl+F in a chat) — an admin's full app only; the server refuses a guest's search anyway.
127
+ if (mode === "full" && !guest) document.addEventListener("keydown", handleSearchKey);
126
128
  // #1605: on a phone (the sidebar is a drawer), swipe right in the main area to open it, left to close it — the same
127
129
  // open/close as ☰ and the scrim. Nothing that owns a sideways drag (code, tables, the terminal, inputs, dialogs) and
128
130
  // never the left edge (iOS: "back").
@@ -1,5 +1,6 @@
1
- // Toasts: one stack in a corner (under the header on a phone), each gone after a few seconds. Text only.
2
- import { html, useEffect, useState } from "./app-html.js";
1
+ // Toasts: one stack in a corner (under the header on a phone; above the tabs on the task board), each gone after a few
2
+ // seconds. Text only. A manual popover, so it stands in the top layer above an open dialog (#2299).
3
+ import { html, useEffect, useRef, useState } from "./app-html.js";
3
4
  import { createStore } from "./app-store.js";
4
5
 
5
6
  const toasts = createStore([]);
@@ -14,5 +15,17 @@ export function toast(text, ok = true) {
14
15
  export function Toasts() {
15
16
  const [list, setList] = useState(toasts.get());
16
17
  useEffect(() => toasts.subscribe(setList), []);
17
- return html`<div data-trust="toast" class="toasts" role="status" aria-live="polite">${list.map(x => html`<div key=${x.id} class=${`toast ${x.ok ? "ok" : "err"}`}>${x.text}</div>`)}</div>`;
18
+ // #2299: a manual popover — in the top layer, so an open modal dialog's backdrop does not dim a toast (it did: grey on
19
+ // grey in the dark theme). Shown again on every new toast, so it is above a dialog opened since. Without popover
20
+ // support it stays an ordinary fixed box, as before.
21
+ const box = useRef(null);
22
+ useEffect(() => {
23
+ const el = box.current;
24
+ if (!el || typeof el.showPopover !== "function") return;
25
+ try {
26
+ if (el.matches(":popover-open")) el.hidePopover();
27
+ if (list.length) el.showPopover();
28
+ } catch { /* a popover that cannot be shown stays where it is */ }
29
+ }, [list]);
30
+ return html`<div ref=${box} popover="manual" data-trust="toast" class="toasts" role="status" aria-live="polite">${list.map(x => html`<div key=${x.id} class=${`toast ${x.ok ? "ok" : "err"}`}>${x.text}</div>`)}</div>`;
18
31
  }
package/dist/web-api.d.ts CHANGED
@@ -11,6 +11,7 @@ import type { WebAppearance } from "./web-appearance.js";
11
11
  import type { SendOpts } from "./channel/types.js";
12
12
  import type { IncomingMessage, ServerResponse } from "node:http";
13
13
  import type { LifecycleCreateArgs } from "./instance-lifecycle.js";
14
+ import { type Actor as BoardActor, type TaskBoard } from "./scheduler/task-board.js";
14
15
  import { type PreviewAvailability } from "./web-preview.js";
15
16
  import { type WebChatHistory, type WebChatMessage } from "./web-chat-history.js";
16
17
  import { type WebFileLedger } from "./web-upload.js";
@@ -60,6 +61,9 @@ export interface WebApiContext {
60
61
  usage_report_on_public_link?: boolean;
61
62
  upload_max_file_mb?: number;
62
63
  upload_max_total_mb?: number;
64
+ search?: {
65
+ enabled?: boolean;
66
+ };
63
67
  };
64
68
  defaults?: {
65
69
  backend?: string;
@@ -149,6 +153,10 @@ export interface WebApiContext {
149
153
  emitSseEvent(event: string, data: unknown): void;
150
154
  /** The web chat's recent messages (history + SSE replay); absent in contexts that have no chat. */
151
155
  readonly webChatHistory?: WebChatHistory;
156
+ /** #2293: the global search index (null or absent: off — web.search.enabled false — or not started). */
157
+ readonly searchIndex?: Pick<import("./search/search-index.js").SearchIndex, "query" | "context" | "status"> | null;
158
+ /** #2293: whose cross-instance messages search leaves out now (cross_instance_visibility "hidden"; see searchVisibility). */
159
+ searchVisibility?(): import("./search/search-docs.js").XmsgVisibility;
152
160
  /** Uploads and the files the dashboard may fetch back (web track C2); absent: no file routes. */
153
161
  readonly webFiles?: WebFileLedger;
154
162
  /** #1630: a cached avatar (an own bot's, or the picture uploaded in View) — never a fetch; null when there is none. */
@@ -332,10 +340,12 @@ export interface WebApiContext {
332
340
  priority?: string;
333
341
  assignee?: string;
334
342
  created_by: string;
335
- }): unknown;
336
- updateTask(id: string, params: Record<string, unknown>): unknown;
337
- claimTask(id: string, assignee: string): unknown;
338
- completeTask(id: string, result?: string): unknown;
343
+ }, by?: BoardActor): unknown;
344
+ updateTask(id: string, params: Record<string, unknown>, by?: BoardActor): unknown;
345
+ claimTask(id: string, assignee: string, by?: BoardActor): unknown;
346
+ completeTask(id: string, result?: string, by?: BoardActor): unknown;
347
+ /** The task board on the same table (scheduler/task-board.ts); absent in a hand-built context. */
348
+ board?: TaskBoard;
339
349
  };
340
350
  list(target?: string): unknown[];
341
351
  create(params: unknown): unknown;