@songsid/agend 2.3.2-beta.9 → 2.3.2

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 (243) hide show
  1. package/README.md +6 -0
  2. package/README.zh-TW.md +6 -0
  3. package/dist/agent-cli.js +48 -0
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.js +6 -0
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-whats-new.js +3 -0
  8. package/dist/agent-whats-new.js.map +1 -1
  9. package/dist/auth-api.js +2 -0
  10. package/dist/auth-api.js.map +1 -1
  11. package/dist/backend/claude-code.js +23 -1
  12. package/dist/backend/claude-code.js.map +1 -1
  13. package/dist/backend/codex-session-lookup.d.ts +15 -0
  14. package/dist/backend/codex-session-lookup.js +58 -0
  15. package/dist/backend/codex-session-lookup.js.map +1 -1
  16. package/dist/backend/codex.js +15 -4
  17. package/dist/backend/codex.js.map +1 -1
  18. package/dist/backend/kiro-classic-archive.d.ts +57 -0
  19. package/dist/backend/kiro-classic-archive.js +622 -0
  20. package/dist/backend/kiro-classic-archive.js.map +1 -0
  21. package/dist/backend/kiro-classic-links.d.ts +24 -0
  22. package/dist/backend/kiro-classic-links.js +135 -0
  23. package/dist/backend/kiro-classic-links.js.map +1 -0
  24. package/dist/backend/kiro-identity.d.ts +100 -1
  25. package/dist/backend/kiro-identity.js +262 -31
  26. package/dist/backend/kiro-identity.js.map +1 -1
  27. package/dist/backend/kiro-migration-verify.d.ts +10 -0
  28. package/dist/backend/kiro-migration-verify.js +52 -0
  29. package/dist/backend/kiro-migration-verify.js.map +1 -0
  30. package/dist/backend/kiro-reconcile-store.d.ts +27 -0
  31. package/dist/backend/kiro-reconcile-store.js +344 -0
  32. package/dist/backend/kiro-reconcile-store.js.map +1 -0
  33. package/dist/backend/kiro-v2-store-worker.js +35 -1
  34. package/dist/backend/kiro-v2-store-worker.js.map +1 -1
  35. package/dist/backend/kiro-v2-store.d.ts +31 -1
  36. package/dist/backend/kiro-v2-store.js +22 -12
  37. package/dist/backend/kiro-v2-store.js.map +1 -1
  38. package/dist/backend/kiro.d.ts +124 -1
  39. package/dist/backend/kiro.js +553 -29
  40. package/dist/backend/kiro.js.map +1 -1
  41. package/dist/backend/types.d.ts +102 -0
  42. package/dist/backend/types.js +9 -0
  43. package/dist/backend/types.js.map +1 -1
  44. package/dist/cache-service.d.ts +13 -1
  45. package/dist/cache-service.js +45 -2
  46. package/dist/cache-service.js.map +1 -1
  47. package/dist/channel/ipc-timeouts.js +3 -0
  48. package/dist/channel/ipc-timeouts.js.map +1 -1
  49. package/dist/channel/mcp-tools.js +40 -5
  50. package/dist/channel/mcp-tools.js.map +1 -1
  51. package/dist/channel/tool-router.js +2 -1
  52. package/dist/channel/tool-router.js.map +1 -1
  53. package/dist/classic-channel-manager.d.ts +6 -0
  54. package/dist/classic-channel-manager.js +25 -0
  55. package/dist/classic-channel-manager.js.map +1 -1
  56. package/dist/cli.js +50 -4
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config-validator.d.ts +7 -1
  59. package/dist/config-validator.js +17 -8
  60. package/dist/config-validator.js.map +1 -1
  61. package/dist/config.js +2 -0
  62. package/dist/config.js.map +1 -1
  63. package/dist/daemon.d.ts +66 -0
  64. package/dist/daemon.js +421 -19
  65. package/dist/daemon.js.map +1 -1
  66. package/dist/dashboard-permissions.d.ts +15 -4
  67. package/dist/dashboard-permissions.js +49 -23
  68. package/dist/dashboard-permissions.js.map +1 -1
  69. package/dist/doctor.js +2 -1
  70. package/dist/doctor.js.map +1 -1
  71. package/dist/fleet-control-audit.d.ts +1 -1
  72. package/dist/fleet-control-audit.js +3 -0
  73. package/dist/fleet-control-audit.js.map +1 -1
  74. package/dist/fleet-manager.d.ts +90 -3
  75. package/dist/fleet-manager.js +569 -41
  76. package/dist/fleet-manager.js.map +1 -1
  77. package/dist/forged-envelope-scan.d.ts +9 -0
  78. package/dist/forged-envelope-scan.js +30 -5
  79. package/dist/forged-envelope-scan.js.map +1 -1
  80. package/dist/general-instance.d.ts +24 -0
  81. package/dist/general-instance.js +53 -0
  82. package/dist/general-instance.js.map +1 -1
  83. package/dist/general-knowledge/skills/docker-install/SKILL.md +25 -0
  84. package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -3
  85. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -1
  86. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +34 -0
  87. package/dist/general-knowledge/skills/session-management/SKILL.md +1 -1
  88. package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -1
  89. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +32 -0
  90. package/dist/general-knowledge/skills/web-appearance/SKILL.md +32 -0
  91. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +57 -17
  92. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +57 -17
  93. package/dist/instance-config-impact.js +3 -2
  94. package/dist/instance-config-impact.js.map +1 -1
  95. package/dist/instance-lifecycle.d.ts +4 -0
  96. package/dist/instance-lifecycle.js +7 -0
  97. package/dist/instance-lifecycle.js.map +1 -1
  98. package/dist/kiro-migrate.d.ts +19 -2
  99. package/dist/kiro-migrate.js +133 -35
  100. package/dist/kiro-migrate.js.map +1 -1
  101. package/dist/kiro-reconcile.d.ts +21 -0
  102. package/dist/kiro-reconcile.js +88 -0
  103. package/dist/kiro-reconcile.js.map +1 -0
  104. package/dist/kiro-session-proof.d.ts +41 -0
  105. package/dist/kiro-session-proof.js +89 -0
  106. package/dist/kiro-session-proof.js.map +1 -0
  107. package/dist/kiro-tui-forensic.d.ts +19 -0
  108. package/dist/kiro-tui-forensic.js +124 -0
  109. package/dist/kiro-tui-forensic.js.map +1 -0
  110. package/dist/locale.js +198 -0
  111. package/dist/locale.js.map +1 -1
  112. package/dist/log-service.js +2 -2
  113. package/dist/log-service.js.map +1 -1
  114. package/dist/needs-you.d.ts +3 -1
  115. package/dist/needs-you.js +4 -2
  116. package/dist/needs-you.js.map +1 -1
  117. package/dist/office-levels.d.ts +64 -0
  118. package/dist/office-levels.js +66 -0
  119. package/dist/office-levels.js.map +1 -0
  120. package/dist/office-share-page.d.ts +3 -1
  121. package/dist/office-share-page.js +8 -3
  122. package/dist/office-share-page.js.map +1 -1
  123. package/dist/office-share-service.d.ts +6 -3
  124. package/dist/office-share-service.js +12 -8
  125. package/dist/office-share-service.js.map +1 -1
  126. package/dist/office-share.d.ts +45 -8
  127. package/dist/office-share.js +75 -15
  128. package/dist/office-share.js.map +1 -1
  129. package/dist/outbound-handlers.js +20 -1
  130. package/dist/outbound-handlers.js.map +1 -1
  131. package/dist/outbound-schemas.d.ts +47 -2
  132. package/dist/outbound-schemas.js +41 -2
  133. package/dist/outbound-schemas.js.map +1 -1
  134. package/dist/public-web-gateway.js +1 -1
  135. package/dist/public-web-gateway.js.map +1 -1
  136. package/dist/quickstart-api.d.ts +39 -1
  137. package/dist/quickstart-api.js +115 -17
  138. package/dist/quickstart-api.js.map +1 -1
  139. package/dist/remote-card-answer.d.ts +83 -0
  140. package/dist/remote-card-answer.js +208 -0
  141. package/dist/remote-card-answer.js.map +1 -0
  142. package/dist/remote-card-prompt.d.ts +38 -0
  143. package/dist/remote-card-prompt.js +86 -0
  144. package/dist/remote-card-prompt.js.map +1 -0
  145. package/dist/scheduler/task-board.d.ts +28 -6
  146. package/dist/scheduler/task-board.js +66 -11
  147. package/dist/scheduler/task-board.js.map +1 -1
  148. package/dist/service-installer.d.ts +13 -0
  149. package/dist/service-installer.js +15 -0
  150. package/dist/service-installer.js.map +1 -1
  151. package/dist/service-owner.d.ts +25 -0
  152. package/dist/service-owner.js +43 -2
  153. package/dist/service-owner.js.map +1 -1
  154. package/dist/settings-api.d.ts +2 -0
  155. package/dist/settings-api.js +18 -12
  156. package/dist/settings-api.js.map +1 -1
  157. package/dist/settings-change.js +10 -5
  158. package/dist/settings-change.js.map +1 -1
  159. package/dist/settings-effect.js +7 -4
  160. package/dist/settings-effect.js.map +1 -1
  161. package/dist/settings-instance-patch.js +1 -1
  162. package/dist/settings-instance-patch.js.map +1 -1
  163. package/dist/setup-form.d.ts +2 -2
  164. package/dist/setup-form.js +139 -93
  165. package/dist/setup-form.js.map +1 -1
  166. package/dist/setup-i18n.d.ts +25 -0
  167. package/dist/setup-i18n.js +226 -0
  168. package/dist/setup-i18n.js.map +1 -0
  169. package/dist/task-board-waker.js +7 -2
  170. package/dist/task-board-waker.js.map +1 -1
  171. package/dist/tool-permissions.js +18 -0
  172. package/dist/tool-permissions.js.map +1 -1
  173. package/dist/transcript-sources.d.ts +1 -0
  174. package/dist/transcript-sources.js +20 -3
  175. package/dist/transcript-sources.js.map +1 -1
  176. package/dist/types.d.ts +10 -0
  177. package/dist/ui/backend-signin.js +4 -3
  178. package/dist/ui/chat-store.js +3 -2
  179. package/dist/ui/chat-strings.js +2 -2
  180. package/dist/ui/fleet-office-card.js +114 -0
  181. package/dist/ui/fleet-office-editor.js +81 -9
  182. package/dist/ui/fleet-office-layout.js +56 -11
  183. package/dist/ui/fleet-office-levels.js +105 -0
  184. package/dist/ui/fleet-office-model.js +9 -1
  185. package/dist/ui/fleet-office-pack.d.ts +15 -3
  186. package/dist/ui/fleet-office-pack.js +177 -23
  187. package/dist/ui/fleet-office-skin.js +39 -3
  188. package/dist/ui/fleet-office-store.js +74 -3
  189. package/dist/ui/fleet-office-view.js +8 -0
  190. package/dist/ui/fleet-office-zones.js +102 -19
  191. package/dist/ui/fleet-office.js +308 -44
  192. package/dist/ui/fleet-org.js +2 -0
  193. package/dist/ui/fleet-task-card.js +2 -2
  194. package/dist/ui/fleet-task-common.js +30 -7
  195. package/dist/ui/fleet-task-work.js +2 -2
  196. package/dist/ui/fleet-tasks.js +10 -11
  197. package/dist/ui/office-share-dialog.js +31 -7
  198. package/dist/ui/office-share-page.js +1 -0
  199. package/dist/ui/panel-chat.js +14 -2
  200. package/dist/ui/panel-log.js +4 -4
  201. package/dist/ui/panel-needs.js +1 -1
  202. package/dist/ui/panel-settings.js +16 -8
  203. package/dist/ui/settings-discord.js +3 -2
  204. package/dist/ui/settings-guests.js +17 -7
  205. package/dist/ui/settings-model.js +17 -9
  206. package/dist/ui/settings-registry.js +1 -0
  207. package/dist/ui/settings-strings.js +16 -4
  208. package/dist/ui/settings-token.js +3 -2
  209. package/dist/ui/settings-wizard.js +25 -9
  210. package/dist/ui/shared/app-i18n.js +2 -2
  211. package/dist/ui/shared/app-stream.js +1 -1
  212. package/dist/ui/shared/app.css +70 -2
  213. package/dist/ui/shared/setup-host-text.js +107 -0
  214. package/dist/ui/structured-signin.js +89 -38
  215. package/dist/usage-rollup.d.ts +34 -3
  216. package/dist/usage-rollup.js +56 -3
  217. package/dist/usage-rollup.js.map +1 -1
  218. package/dist/web-api.d.ts +10 -1
  219. package/dist/web-api.js +207 -17
  220. package/dist/web-api.js.map +1 -1
  221. package/dist/web-auth.js +1 -1
  222. package/dist/web-auth.js.map +1 -1
  223. package/dist/web-office-acts.d.ts +5 -2
  224. package/dist/web-office-acts.js +9 -4
  225. package/dist/web-office-acts.js.map +1 -1
  226. package/dist/web-office-pack-read.d.ts +35 -0
  227. package/dist/web-office-pack-read.js +252 -0
  228. package/dist/web-office-pack-read.js.map +1 -0
  229. package/dist/web-office-pack-tools.d.ts +241 -0
  230. package/dist/web-office-pack-tools.js +785 -0
  231. package/dist/web-office-pack-tools.js.map +1 -0
  232. package/dist/web-office-skins.d.ts +68 -4
  233. package/dist/web-office-skins.js +324 -33
  234. package/dist/web-office-skins.js.map +1 -1
  235. package/dist/web-office-store.d.ts +47 -4
  236. package/dist/web-office-store.js +124 -23
  237. package/dist/web-office-store.js.map +1 -1
  238. package/dist/web-settings-patch.d.ts +3 -1
  239. package/dist/web-settings-patch.js +4 -1
  240. package/dist/web-settings-patch.js.map +1 -1
  241. package/dist/workflow-templates/default.md +1 -1
  242. package/package.json +2 -2
  243. package/templates/windows-shortcuts.ps1 +18 -17
@@ -42,33 +42,62 @@ export function overflowSpots(area, need = 0) {
42
42
  * slot }). Someone still in the same area keeps their slot; the others take the first free seat, in the area's order,
43
43
  * then an overflow spot (slot -1, -2, …). No two share one. Whoever finds neither is counted in `crowd` (area → n).
44
44
  * Returns { seats: Map name → { area, slot, x, y }, crowd: Map }.
45
+ *
46
+ * #2691: a seat may be reserved (its `for`). Whoever it is for, wanting its area, takes a free seat reserved for them
47
+ * before anything else (the one they sat on last time, else their first in the area's order) — owners are seated
48
+ * before anyone keeps a slot, so a borrower on an owner's seat moves off when the owner comes. Everyone else keeps
49
+ * their slot or takes the first free seat not reserved for someone else: a plain one, else a reserved one that says
50
+ * `lend` (one no owner of it took). A reserved seat not lent, none of whose owners wants the area, is unavailable:
51
+ * the area's overflow spots and `over` count without it. Seats without `for` work as before.
45
52
  */
46
53
  export function assignSeats(pack, wants, prev = new Map()) {
47
- const seats = new Map(), crowd = new Map(), taken = new Map(), spill = new Map(), demand = new Map();
54
+ const seats = new Map(), crowd = new Map(), taken = new Map(), spill = new Map(), demand = new Map(), wanting = new Map();
48
55
  const areaOf = (a) => own(pack.areas, a);
49
- for (const w of wants) demand.set(w.area, (demand.get(w.area) || 0) + 1);
56
+ for (const w of wants) {
57
+ demand.set(w.area, (demand.get(w.area) || 0) + 1);
58
+ if (!wanting.has(w.area)) wanting.set(w.area, new Set());
59
+ wanting.get(w.area).add(w.name);
60
+ }
61
+ const mine = (s, name) => !!s.for && s.for.includes(name);
62
+ const lent = (s) => !!s.for && s.lend === true;
63
+ // The seats someone wanting the area may end up on: plain, lent, or reserved for someone who wants it.
64
+ const usable = (area) => areaOf(area).seats.filter((s) => !s.for || s.lend === true || s.for.some((n) => wanting.get(area).has(n))).length;
50
65
  const spotsOf = (area) => {
51
- if (!spill.has(area)) spill.set(area, overflowSpots(areaOf(area), Math.max(0, (demand.get(area) || 0) - areaOf(area).seats.length)));
66
+ if (!spill.has(area)) spill.set(area, overflowSpots(areaOf(area), Math.max(0, (demand.get(area) || 0) - usable(area))));
52
67
  return spill.get(area);
53
68
  };
54
69
  const pos = (area, slot) => (slot >= 0 ? areaOf(area).seats[slot] : spotsOf(area)[-slot - 1]);
55
70
  const usedIn = (area) => { if (!taken.has(area)) taken.set(area, new Set()); return taken.get(area); };
56
71
  const give = (name, area, slot) => { const p = pos(area, slot); usedIn(area).add(slot); seats.set(name, { area, slot, x: p.x, y: p.y }); };
72
+ const free = (w) => !seats.has(w.name) && !!areaOf(w.area);
73
+ // Owners first: their own seat from last time, then their first free one.
74
+ for (const w of wants) {
75
+ const p = prev.get(w.name), s = p && p.area === w.area && p.slot >= 0 && areaOf(w.area) ? areaOf(w.area).seats[p.slot] : null;
76
+ if (s && mine(s, w.name) && !usedIn(w.area).has(p.slot)) give(w.name, w.area, p.slot);
77
+ }
78
+ for (const w of wants) {
79
+ if (!free(w)) continue;
80
+ const used = usedIn(w.area), slot = areaOf(w.area).seats.findIndex((s, i) => mine(s, w.name) && !used.has(i));
81
+ if (slot >= 0) give(w.name, w.area, slot);
82
+ }
57
83
  for (const w of wants) {
58
84
  const p = prev.get(w.name);
59
- if (!p || p.area !== w.area || !areaOf(w.area) || !pos(w.area, p.slot) || usedIn(w.area).has(p.slot)) continue;
85
+ if (!free(w) || !p || p.area !== w.area || !pos(w.area, p.slot) || usedIn(w.area).has(p.slot)) continue;
86
+ const s = p.slot >= 0 ? areaOf(w.area).seats[p.slot] : null;
87
+ if (s && s.for && !lent(s)) continue; // reserved for someone else (theirs would be taken above)
60
88
  give(w.name, w.area, p.slot);
61
89
  }
62
90
  for (const w of wants) {
63
- if (seats.has(w.name) || !areaOf(w.area)) continue;
91
+ if (!free(w)) continue;
64
92
  const used = usedIn(w.area), area = areaOf(w.area);
65
- let slot = area.seats.findIndex((_, i) => !used.has(i));
93
+ let slot = area.seats.findIndex((s, i) => !s.for && !used.has(i));
94
+ if (slot < 0) slot = area.seats.findIndex((s, i) => lent(s) && !used.has(i));
66
95
  if (slot < 0) { const j = spotsOf(w.area).findIndex((_, i) => !used.has(-i - 1)); slot = j < 0 ? null : -j - 1; }
67
96
  if (slot === null) crowd.set(w.area, (crowd.get(w.area) || 0) + 1);
68
97
  else give(w.name, w.area, slot);
69
98
  }
70
- // Over capacity: more people want the area than it has seats (their bubbles and tags are thinned, fleet-office.js).
71
- const over = new Set([...demand].filter(([a, n]) => areaOf(a) && n > areaOf(a).seats.length).map(([a]) => a));
99
+ // Over capacity: more people want the area than it has seats for them (their bubbles and tags are thinned, fleet-office.js).
100
+ const over = new Set([...demand].filter(([a, n]) => areaOf(a) && n > usable(a)).map(([a]) => a));
72
101
  return { seats, crowd, over };
73
102
  }
74
103
 
@@ -79,17 +108,39 @@ export function assignSeats(pack, wants, prev = new Map()) {
79
108
  */
80
109
  export function zonesLayout(pack, nodes, prev = new Map(), { night = false } = {}) {
81
110
  // At night (#2568) the idle go to bed: the area asleep people go to while it has a free seat; the rest doze where they are.
82
- const bed = zoneOf(pack, "paused"), beds = (own(pack.areas, bed)?.seats || []).length, sleepers = new Set(), inBed = new Set();
83
- let used = nodes.filter((n) => n.state !== "idle" && zoneOf(pack, n.state) === bed).length;
84
- if (night) for (const n of nodes) if (n.state === "idle") { sleepers.add(n.name); if (used < beds) { used++; inBed.add(n.name); } }
85
- const wants = nodes.filter((n) => n.state !== "missing").map((n) => ({ name: n.name, area: inBed.has(n.name) ? bed : zoneOf(pack, n.state) }));
86
- const { seats, crowd, over } = assignSeats(pack, wants, prev);
111
+ const bed = zoneOf(pack, "paused"), bedSeats = own(pack.areas, bed)?.seats || [], sleepers = new Set(), inBed = new Set();
112
+ const wantsOf = (beds) => nodes.filter((n) => n.state !== "missing").map((n) => ({ name: n.name, area: beds.has(n.name) ? bed : zoneOf(pack, n.state) }));
113
+ let placed = assignSeats(pack, wantsOf(inBed), prev);
114
+ if (night) {
115
+ // #2691 review: who goes to bed is decided by the seat assignment itself (assignSeats' reservation rules), one
116
+ // sleeper at a time — whoever has a bed of their own there first, then the rest in the fleet's order. A sleeper
117
+ // goes to bed only if it gives them a bed (never an overflow spot) without taking one from anyone already in one
118
+ // (the awake first, then the sleepers admitted before), unless it is a bed reserved for them: an owner comes first,
119
+ // as by day. Everyone else dozes where they are.
120
+ const inABed = (r) => new Set([...r.seats].filter(([, s]) => s.area === bed && s.slot >= 0).map(([name]) => name));
121
+ const idle = nodes.filter((n) => n.state === "idle");
122
+ const hasBed = (n) => bedSeats.some((s) => s.for && s.for.includes(n.name));
123
+ for (const n of idle) sleepers.add(n.name);
124
+ for (const n of [...idle.filter(hasBed), ...idle.filter((n) => !hasBed(n))]) {
125
+ const trial = assignSeats(pack, wantsOf(new Set([...inBed, n.name])), prev), s = trial.seats.get(n.name);
126
+ if (!s || s.area !== bed || s.slot < 0) {
127
+ if (!hasBed(n)) break; // no bed free for anyone without one: none for the rest either
128
+ continue;
129
+ }
130
+ const before = inABed(placed), after = inABed(trial);
131
+ const theirs = !!bedSeats[s.slot].for && bedSeats[s.slot].for.includes(n.name);
132
+ if (!theirs && [...before].some((m) => !after.has(m))) continue;
133
+ inBed.add(n.name);
134
+ placed = trial;
135
+ }
136
+ }
137
+ const { seats, crowd, over } = placed;
87
138
  const desks = nodes.filter((n) => seats.has(n.name)).map((n) => {
88
139
  const s = seats.get(n.name);
89
140
  return { key: `zone/${n.name}`, node: n, x: s.x - 24, y: s.y - 52, w: 48, h: 66, seat: { x: s.x, y: s.y }, wp: null, area: s.area, slot: s.slot, night: sleepers.has(n.name) };
90
141
  });
91
142
  return { mode: "zones", rooms: [{ key: "\u0000zones", name: null, lobby: false, x: 0, y: 0, w: LOGICAL.w, h: LOGICAL.h, desks }], areas: pack.areas,
92
- waypoints: pack.waypoints || [], edges: pack.edges || [], straight: pack.straight === true, door: pack.door, width: LOGICAL.w, height: LOGICAL.h, seats, crowd, over };
143
+ waypoints: pack.waypoints || [], edges: pack.edges || [], straight: pack.straight === true, door: pack.door, mailDrop: pack.mailDrop || null, width: LOGICAL.w, height: LOGICAL.h, seats, crowd, over };
93
144
  }
94
145
 
95
146
  // ── Walking ──
@@ -176,7 +227,9 @@ export function walkTargets(desks, graph, t, wander = true, furniture = [], nigh
176
227
  */
177
228
  export const MAIL = { MAX_WAIT: 60000, GAP: 16, SEARCH: 320, ASLEEP: new Set(["paused", "stopped"]), MAX: 20 };
178
229
  /**
179
- * Where the `n`th waiting letter lands: `n` GAPs along from the waypoint nearest the room's middle — the nearest whose
230
+ * Where the `n`th waiting letter lands. #2736: the layout's `mailDrop` when it has one — the `n`th of the slots GAP
231
+ * apart rightwards from it that no seat is on (never beyond the room's right edge); fewer than that, today's rule. Otherwise `n`
232
+ * GAPs along from the waypoint nearest the room's middle — the nearest whose
180
233
  * spot (offset included) no seat of the office is on, taken or free (someone may sit there any moment; #2561 likewise);
181
234
  * with none, the nearest point round the middle (rings GAP·2 apart, inside the room) that no seat is on; with none of
182
235
  * those either, null — the letter is not delivered here (never on a seat).
@@ -185,6 +238,12 @@ export function dropPoint(L, n = 0) {
185
238
  const mid = { x: L.width / 2, y: L.height * 0.55 }, d = (p, q) => Math.hypot(p.x - q.x, p.y - q.y);
186
239
  const seats = [...Object.values(L.areas || {}).flatMap((a) => a.seats || []), ...desksIn(L).map((q) => q.seat)];
187
240
  const onSeat = (p) => seats.some((q) => d(p, q) < 4);
241
+ // #2751 review: the nth off-seat slot (slots GAP apart from the mailDrop), so two letters never share one.
242
+ const md = L.mailDrop;
243
+ if (md) for (let i = 0, free = 0; md.x + i * MAIL.GAP <= L.width; i++) {
244
+ const p = { x: Math.round(md.x + i * MAIL.GAP), y: Math.round(md.y) };
245
+ if (!onSeat(p) && free++ === n) return p;
246
+ }
188
247
  const spots = (L.waypoints || []).map((w) => ({ x: Math.round(w.x + n * MAIL.GAP), y: Math.round(w.y), from: w }))
189
248
  .filter((p) => !onSeat(p.from) && !onSeat(p)).sort((p, q) => d(p.from, mid) - d(q.from, mid));
190
249
  if (spots.length) return { x: spots[0].x, y: spots[0].y };
@@ -386,14 +445,38 @@ export function linesFor(state, lang, pack, defaults) {
386
445
 
387
446
  // ── Furniture ──
388
447
 
389
- /** The furniture active now: an item whose `activeWhen` names a state someone shown is in. */
390
- export function activeFurniture(pack, states, byName = new Map()) {
448
+ /**
449
+ * The people an item with `activeFor` is active for now (#2570): those of them shown in an `activeWhen` state — with
450
+ * "activeOn": "seated" (#2698), only those `posture(name)` says are "seated" (at the seat zones gave them, not walking).
451
+ * `posture` null: no walkers, everyone at their seat.
452
+ */
453
+ function activeForNow(f, byName, posture) {
454
+ const seated = (n) => f.activeOn !== "seated" || !posture || posture(n) === "seated";
455
+ return (f.activeFor || [...byName.keys()]).filter((n) => byName.has(n) && f.activeWhen.includes(byName.get(n)) && seated(n));
456
+ }
457
+ /**
458
+ * The furniture active now: an item whose `activeWhen` names a state someone shown is in. With activeFor (#2570),
459
+ * only those people's states count; with "activeOn": "seated" (#2698), only once one of them is seated (`posture`,
460
+ * name → "walking" | "seated" | "standing"; null: everyone seated) — it turns back as they walk off.
461
+ */
462
+ export function activeFurniture(pack, states, byName = new Map(), posture = null) {
391
463
  const on = new Set();
392
464
  for (const f of (pack && pack.furniture) || []) {
393
465
  if (!f.activeWhen) continue;
394
- // activeFor (#2570): only those people's states count; else anyone's shown.
395
- const hit = f.activeFor ? f.activeFor.some((n) => byName.has(n) && f.activeWhen.includes(byName.get(n))) : f.activeWhen.some((s) => states.has(s));
466
+ const hit = f.activeFor || f.activeOn === "seated" ? activeForNow(f, byName, posture).length > 0 : f.activeWhen.some((s) => states.has(s));
396
467
  if (hit) on.add(f.id);
397
468
  }
398
469
  return on;
399
470
  }
471
+ /**
472
+ * #2698: who is drawn on an item instead of themselves (name → the item's id): for each `active` item with "hides",
473
+ * the people it is active for (activeFor's order) who are not walking — someone walking is always drawn. One item each.
474
+ */
475
+ export function hiddenBy(pack, active, byName = new Map(), posture = null) {
476
+ const out = new Map();
477
+ for (const f of (pack && pack.furniture) || []) {
478
+ if (!f.hides || !f.activeWhen || !active.has(f.id)) continue;
479
+ for (const n of activeForNow(f, byName, posture)) if (!out.has(n) && (!posture || posture(n) !== "walking")) out.set(n, f.id);
480
+ }
481
+ return out;
482
+ }