@khorsheed/dsh-room 0.2.2 → 0.2.4

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 (109) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.en.md +29 -62
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +28 -61
  5. package/lib/client.js +14620 -5705
  6. package/lib/index.js +4069 -730
  7. package/lib/journal-B4FOGO6S.js +482 -0
  8. package/lib/journal-Rid9AAnD.js +485 -0
  9. package/lib/tool.js +112 -166
  10. package/lib/typert.host.js +2031 -374
  11. package/lib/typert.remote-client.d.ts +34 -10
  12. package/lib/typert.remote-client.js +1599 -210
  13. package/lib/types/agent-setup.d.ts +1 -1
  14. package/lib/types/client/InviteAgentAction.js +4 -16
  15. package/lib/types/client/InviteDialog.d.ts +28 -6
  16. package/lib/types/client/InviteDialog.js +76 -12
  17. package/lib/types/client/MemberCard.d.ts +5 -1
  18. package/lib/types/client/MemberCard.js +4 -4
  19. package/lib/types/client/MembersView.d.ts +1 -1
  20. package/lib/types/client/MembersView.js +58 -22
  21. package/lib/types/client/RemoveMemberConfirm.d.ts +40 -0
  22. package/lib/types/client/RemoveMemberConfirm.js +85 -0
  23. package/lib/types/client/RoomApprovalView.d.ts +11 -0
  24. package/lib/types/client/RoomApprovalView.js +28 -0
  25. package/lib/types/client/RoomComposer.d.ts +1 -1
  26. package/lib/types/client/RoomComposer.js +195 -29
  27. package/lib/types/client/RoomDecisionView.d.ts +22 -0
  28. package/lib/types/client/RoomDecisionView.js +35 -0
  29. package/lib/types/client/RoomHelpView.d.ts +17 -0
  30. package/lib/types/client/RoomHelpView.js +56 -0
  31. package/lib/types/client/RoomInboxView.d.ts +10 -0
  32. package/lib/types/client/RoomInboxView.js +14 -0
  33. package/lib/types/client/RoomQuestionView.d.ts +13 -0
  34. package/lib/types/client/RoomQuestionView.js +61 -0
  35. package/lib/types/client/RoomRelayView.js +1 -1
  36. package/lib/types/client/RoomRunView.d.ts +3 -1
  37. package/lib/types/client/RoomRunView.js +12 -5
  38. package/lib/types/client/RoomSettingsView.d.ts +19 -0
  39. package/lib/types/client/RoomSettingsView.js +30 -0
  40. package/lib/types/client/RoomSpeechView.d.ts +22 -8
  41. package/lib/types/client/RoomSpeechView.js +94 -11
  42. package/lib/types/client/RoomStatsLine.d.ts +26 -5
  43. package/lib/types/client/RoomStatsLine.js +53 -7
  44. package/lib/types/client/RoomTaskCapsule.d.ts +19 -0
  45. package/lib/types/client/RoomTaskCapsule.js +37 -0
  46. package/lib/types/client/TasksTab.d.ts +41 -0
  47. package/lib/types/client/TasksTab.js +43 -0
  48. package/lib/types/client/TasksView.d.ts +44 -0
  49. package/lib/types/client/TasksView.js +149 -0
  50. package/lib/types/client/TeamView.d.ts +33 -0
  51. package/lib/types/client/TeamView.js +210 -0
  52. package/lib/types/client/at-menu.d.ts +127 -0
  53. package/lib/types/client/at-menu.js +173 -0
  54. package/lib/types/client/draft-chips.d.ts +67 -0
  55. package/lib/types/client/draft-chips.js +95 -0
  56. package/lib/types/client/executions.d.ts +0 -30
  57. package/lib/types/client/executions.js +4 -34
  58. package/lib/types/client/icons-local.d.ts +25 -0
  59. package/lib/types/client/icons-local.js +10 -0
  60. package/lib/types/client/icons.d.ts +2 -1
  61. package/lib/types/client/icons.js +1 -0
  62. package/lib/types/client/index.js +276 -72
  63. package/lib/types/client/locales.d.ts +482 -52
  64. package/lib/types/client/locales.js +490 -60
  65. package/lib/types/client/nodes.d.ts +112 -17
  66. package/lib/types/client/nodes.js +197 -26
  67. package/lib/types/client/slots.d.ts +117 -47
  68. package/lib/types/client/task-tag.d.ts +32 -0
  69. package/lib/types/client/task-tag.js +33 -0
  70. package/lib/types/client/tasks.d.ts +50 -0
  71. package/lib/types/client/tasks.js +82 -0
  72. package/lib/types/client/team.d.ts +73 -0
  73. package/lib/types/client/team.js +187 -0
  74. package/lib/types/decisions.d.ts +49 -0
  75. package/lib/types/decisions.js +101 -0
  76. package/lib/types/deliveries.d.ts +17 -1
  77. package/lib/types/deliveries.js +82 -6
  78. package/lib/types/dispatch.d.ts +102 -4
  79. package/lib/types/dispatch.js +498 -109
  80. package/lib/types/help.d.ts +137 -0
  81. package/lib/types/help.js +216 -0
  82. package/lib/types/helps.d.ts +26 -0
  83. package/lib/types/helps.js +47 -0
  84. package/lib/types/index.d.ts +292 -52
  85. package/lib/types/index.js +1421 -199
  86. package/lib/types/jobs.d.ts +22 -0
  87. package/lib/types/jobs.js +77 -0
  88. package/lib/types/journal.d.ts +17 -27
  89. package/lib/types/journal.js +120 -43
  90. package/lib/types/messaging.d.ts +65 -0
  91. package/lib/types/messaging.js +160 -0
  92. package/lib/types/plan-service.js +7 -1
  93. package/lib/types/report-gate.d.ts +81 -0
  94. package/lib/types/report-gate.js +89 -0
  95. package/lib/types/tool-definition.d.ts +43 -20
  96. package/lib/types/tool-definition.js +145 -212
  97. package/lib/types/tool.d.ts +2 -2
  98. package/lib/types/tool.js +3 -2
  99. package/lib/types/types.d.ts +755 -78
  100. package/locale/zh.json +1 -1
  101. package/package.json +3 -3
  102. package/lib/types/client/RoomActivityDock.d.ts +0 -19
  103. package/lib/types/client/RoomActivityDock.js +0 -105
  104. package/lib/types/client/RoomDockCapsules.d.ts +0 -64
  105. package/lib/types/client/RoomDockCapsules.js +0 -277
  106. package/lib/types/client/RoomPlanTab.d.ts +0 -15
  107. package/lib/types/client/RoomPlanTab.js +0 -13
  108. package/lib/types/client/RoomTaskLineView.d.ts +0 -13
  109. package/lib/types/client/RoomTaskLineView.js +0 -21
@@ -0,0 +1,482 @@
1
+ //#region lib/types/deliveries.js
2
+ /** Accepted delivery intents and their latest durable outcomes. */
3
+ function replayDeliveries(events) {
4
+ const rows = /* @__PURE__ */ new Map();
5
+ for (const event of events) if (event.type === "room/dispatch" && event.data.targetIds !== void 0) for (const memberId of event.data.targetIds) {
6
+ const id = `${event.seq}:${memberId}`;
7
+ if (!rows.has(id)) rows.set(id, {
8
+ id,
9
+ dispatchSeq: event.seq,
10
+ memberId,
11
+ status: "queued",
12
+ text: event.data.text,
13
+ origin: event.data.origin ?? "human",
14
+ ...event.data.replyTo === void 0 ? {} : { replyTo: event.data.replyTo },
15
+ ...event.data.from === void 0 ? {} : { from: event.data.from },
16
+ ...event.data.kind === void 0 ? {} : { kind: event.data.kind },
17
+ ...event.data.plan === void 0 ? {} : { plan: event.data.plan }
18
+ });
19
+ }
20
+ else if (event.type === "room/delivery-state") {
21
+ const row = rows.get(event.data.id);
22
+ if (row === void 0 || row.memberId !== event.data.memberId || row.dispatchSeq !== event.data.dispatchSeq) continue;
23
+ rows.set(row.id, {
24
+ ...row,
25
+ status: event.data.state,
26
+ ...event.data.error === void 0 ? {} : { error: event.data.error }
27
+ });
28
+ }
29
+ return [...rows.values()];
30
+ }
31
+ //#endregion
32
+ //#region lib/types/journal.js
33
+ /**
34
+ * Room journal replay: folds the room session's `room/*` custom events into
35
+ * the derived {@link RoomState} — roster, notification relays, the task
36
+ * board, and run states. Pure: the Remote surface reads through it, the
37
+ * dispatch engine reads instruction cursors through it, and the unit tests
38
+ * exercise it without a cordis composition. Reload-replay recovers the exact
39
+ * same state, which is what makes the journal the room's single source of
40
+ * truth. There is NO blackboard fold: member prompts never consume the
41
+ * room's running log (see the design note) — speech/dispatch events are
42
+ * journaled for UI projection and replay only.
43
+ * @module @khorsheed/dsh-room/journal
44
+ */
45
+ /**
46
+ * The native DSH member's addressing name in new rooms. It starts as the
47
+ * coordinator; bare messages follow the selected coordinator thereafter.
48
+ * Existing journals retain their recorded names, including legacy main.
49
+ */
50
+ const MAIN_AGENT_MEMBER = "dsh";
51
+ /**
52
+ * Every `room/*` session-event type this package introduces. Room registers
53
+ * them into the harness's KNOWN_SESSION_EVENT_TYPES persistence catalog at
54
+ * apply time (see the RoomService constructor): the catalog's read-path
55
+ * refusal exists so a build that does NOT understand an event type never
56
+ * silently mangles the log — a build with room mounted understands these, so
57
+ * registration is the semantically correct declaration, and a build without
58
+ * room keeps refusing them. The catalog's own header defers a registration
59
+ * surface for out-of-repo plugins "until such a consumer exists"; this
60
+ * in-place add is that surface's temporary form — migrate to the official
61
+ * one when it lands upstream.
62
+ */
63
+ const ROOM_EVENT_TYPES = [
64
+ "room/created",
65
+ "room/plan-state",
66
+ "room/coordinator",
67
+ "room/delivery-state",
68
+ "room/member-added",
69
+ "room/member-updated",
70
+ "room/member-removed",
71
+ "room/dispatch",
72
+ "room/speech",
73
+ "room/run-state",
74
+ "room/execution-metadata",
75
+ "room/relay",
76
+ "room/relay-resolved",
77
+ "room/task-added",
78
+ "room/task-updated",
79
+ "room/task-edited",
80
+ "room/goal",
81
+ "room/settings"
82
+ ];
83
+ /** Collaboration settings of a room that never changed them. */
84
+ const DEFAULT_ROOM_SETTINGS = {
85
+ memberMessages: "free",
86
+ hopLimit: 10,
87
+ paused: false
88
+ };
89
+ /** Whether an event log carries the room identity marker. */
90
+ function isRoomLog(events) {
91
+ return events.some((event) => event.type === "room/created");
92
+ }
93
+ /**
94
+ * Parse the leading `@name` tokens of a raw composer message.
95
+ * @param raw - the raw text.
96
+ * @returns the addressed names (deduped, order-preserving) and the body with
97
+ * the mention prefix stripped. Only LEADING tokens address: an `@name`
98
+ * inside prose is plain text (a bare message goes to the selected coordinator).
99
+ */
100
+ function parseMentions(raw) {
101
+ const targets = [];
102
+ let rest = raw.trim();
103
+ for (;;) {
104
+ const match = /^@(\S+)(?:\s+|$)/.exec(rest);
105
+ if (match === null) break;
106
+ const name = match[1];
107
+ if (!targets.includes(name)) targets.push(name);
108
+ rest = rest.slice(match[0].length).trim();
109
+ }
110
+ return {
111
+ targets,
112
+ text: rest
113
+ };
114
+ }
115
+ /**
116
+ * Fold a session's event log into the room state.
117
+ *
118
+ * Write-side invariants (invite/update/remove validate before appending) are
119
+ * NOT re-enforced here: a replayed log may be hand-edited or forked, so the
120
+ * fold is defensive — a duplicate member-added keeps the first record (first
121
+ * wins, a replay cannot re-seat a member), and updates/removals naming an
122
+ * unknown member are dropped. Unknown event types (e.g. a `room/note` from
123
+ * the dropped blackboard design) fall through the switch and are skipped.
124
+ * Removal also drops the member's run state: the roster reads clean after a
125
+ * remove. Relays and tasks fold by id, latest resolution winning.
126
+ * @param events - the session's event log.
127
+ * @returns the folded room state (empty for a non-room log).
128
+ */
129
+ function replay(events) {
130
+ const members = [];
131
+ const byName = /* @__PURE__ */ new Map();
132
+ const relays = [];
133
+ const relayById = /* @__PURE__ */ new Map();
134
+ const tasks = [];
135
+ const taskById = /* @__PURE__ */ new Map();
136
+ const runs = /* @__PURE__ */ new Map();
137
+ const executions = /* @__PURE__ */ new Map();
138
+ let goal;
139
+ let settings = DEFAULT_ROOM_SETTINGS;
140
+ let coordinator;
141
+ for (const event of events) switch (event.type) {
142
+ case "room/coordinator":
143
+ if (event.data.revision > (coordinator?.revision ?? 0)) coordinator = event.data;
144
+ break;
145
+ case "room/member-added": {
146
+ if (byName.has(event.data.name)) break;
147
+ const added = {
148
+ id: event.data.id ?? `legacy:${event.seq}`,
149
+ name: event.data.name,
150
+ kind: event.data.kind,
151
+ invitedBy: event.data.invitedBy,
152
+ ...event.data.provider === void 0 ? {} : { provider: event.data.provider },
153
+ ...event.data.instructions === void 0 ? {} : { instructions: event.data.instructions },
154
+ ...event.data.role === void 0 ? {} : { role: event.data.role },
155
+ ...event.data.preset === void 0 ? {} : { preset: event.data.preset },
156
+ ...event.data.cwd === void 0 ? {} : { cwd: event.data.cwd },
157
+ ...event.data.model === void 0 ? {} : { model: event.data.model },
158
+ ...event.data.childSessionId === void 0 ? {} : { childSessionId: event.data.childSessionId }
159
+ };
160
+ byName.set(added.name, added);
161
+ members.push(added);
162
+ break;
163
+ }
164
+ case "room/member-updated": {
165
+ const member = byName.get(event.data.name);
166
+ if (member === void 0) break;
167
+ const withoutInstructions = event.data.instructions === null ? (({ instructions: _cleared, ...rest }) => rest)(member) : {
168
+ ...member,
169
+ ...event.data.instructions === void 0 ? {} : { instructions: event.data.instructions }
170
+ };
171
+ const withoutRole = event.data.role === null ? (({ role: _cleared, ...rest }) => rest)(withoutInstructions) : {
172
+ ...withoutInstructions,
173
+ ...event.data.role === void 0 ? {} : { role: event.data.role }
174
+ };
175
+ const withoutCwd = event.data.cwd === null ? (({ cwd: _cleared, ...rest }) => rest)(withoutRole) : {
176
+ ...withoutRole,
177
+ ...event.data.cwd === void 0 ? {} : { cwd: event.data.cwd }
178
+ };
179
+ const patched = {
180
+ ...event.data.model === null ? (({ model: _cleared, ...rest }) => rest)(withoutCwd) : {
181
+ ...withoutCwd,
182
+ ...event.data.model === void 0 ? {} : { model: event.data.model }
183
+ },
184
+ ...event.data.childSessionId === void 0 ? {} : { childSessionId: event.data.childSessionId }
185
+ };
186
+ for (const [id, execution] of executions) if (execution.memberId === member.id && execution.childSessionId === void 0 && patched.childSessionId !== void 0) executions.set(id, {
187
+ ...execution,
188
+ childSessionId: patched.childSessionId
189
+ });
190
+ const rename = event.data.rename;
191
+ if (rename === void 0 || rename === member.name || byName.has(rename)) {
192
+ byName.set(member.name, patched);
193
+ members[members.indexOf(member)] = patched;
194
+ break;
195
+ }
196
+ const renamed = {
197
+ ...patched,
198
+ name: rename
199
+ };
200
+ byName.delete(member.name);
201
+ byName.set(rename, renamed);
202
+ members[members.indexOf(member)] = renamed;
203
+ for (let index = 0; index < tasks.length; index++) {
204
+ const task = tasks[index];
205
+ if (task.member !== member.name && task.blockedBy !== member.name) continue;
206
+ const migrated = {
207
+ ...task,
208
+ ...task.member === member.name ? { member: rename } : {},
209
+ ...task.blockedBy === member.name ? { blockedBy: rename } : {}
210
+ };
211
+ taskById.set(migrated.id, migrated);
212
+ tasks[index] = migrated;
213
+ }
214
+ for (let index = 0; index < relays.length; index++) {
215
+ const relay = relays[index];
216
+ if (relay.from !== member.name && relay.to !== member.name) continue;
217
+ const migrated = {
218
+ ...relay,
219
+ from: relay.from === member.name ? rename : relay.from,
220
+ to: relay.to === member.name ? rename : relay.to
221
+ };
222
+ relayById.set(migrated.id, migrated);
223
+ relays[index] = migrated;
224
+ }
225
+ const run = runs.get(member.name);
226
+ if (run !== void 0) {
227
+ runs.delete(member.name);
228
+ runs.set(rename, {
229
+ ...run,
230
+ member: rename
231
+ });
232
+ }
233
+ break;
234
+ }
235
+ case "room/member-removed": {
236
+ const member = byName.get(event.data.name);
237
+ if (member === void 0) break;
238
+ byName.delete(event.data.name);
239
+ members.splice(members.indexOf(member), 1);
240
+ runs.delete(event.data.name);
241
+ break;
242
+ }
243
+ case "room/relay": {
244
+ if (relayById.has(event.data.id)) break;
245
+ const relay = {
246
+ id: event.data.id,
247
+ from: event.data.from,
248
+ to: event.data.to,
249
+ content: event.data.content,
250
+ state: "pending",
251
+ ...event.data.provenance === void 0 ? {} : { provenance: event.data.provenance },
252
+ ...event.data.reason === void 0 ? {} : { reason: event.data.reason },
253
+ ...event.data.intent === void 0 ? {} : { intent: event.data.intent }
254
+ };
255
+ relayById.set(relay.id, relay);
256
+ relays.push(relay);
257
+ break;
258
+ }
259
+ case "room/relay-resolved": {
260
+ const relay = relayById.get(event.data.id);
261
+ if (relay === void 0) break;
262
+ const resolved = {
263
+ ...relay,
264
+ state: event.data.state
265
+ };
266
+ relayById.set(resolved.id, resolved);
267
+ relays[relays.indexOf(relay)] = resolved;
268
+ break;
269
+ }
270
+ case "room/task-added": {
271
+ if (taskById.has(event.data.id)) break;
272
+ const task = {
273
+ id: event.data.id,
274
+ member: event.data.member,
275
+ title: event.data.title,
276
+ ...event.data.deliveryId === void 0 ? {} : { deliveryId: event.data.deliveryId },
277
+ status: event.data.status,
278
+ ...event.data.blockedBy === void 0 ? {} : { blockedBy: event.data.blockedBy },
279
+ updatedAt: event.time
280
+ };
281
+ taskById.set(task.id, task);
282
+ tasks.push(task);
283
+ break;
284
+ }
285
+ case "room/task-updated": {
286
+ const task = taskById.get(event.data.id);
287
+ if (task === void 0) break;
288
+ const updated = {
289
+ ...task,
290
+ status: event.data.status,
291
+ updatedAt: event.time
292
+ };
293
+ taskById.set(updated.id, updated);
294
+ tasks[tasks.indexOf(task)] = updated;
295
+ break;
296
+ }
297
+ case "room/task-edited": {
298
+ const task = taskById.get(event.data.id);
299
+ if (task === void 0) break;
300
+ const edited = {
301
+ ...event.data.blockedBy === null ? (({ blockedBy: _cleared, ...rest }) => rest)(task) : {
302
+ ...task,
303
+ ...event.data.blockedBy === void 0 ? {} : { blockedBy: event.data.blockedBy }
304
+ },
305
+ ...event.data.title === void 0 ? {} : { title: event.data.title },
306
+ ...event.data.member === void 0 ? {} : { member: event.data.member },
307
+ updatedAt: event.time
308
+ };
309
+ taskById.set(edited.id, edited);
310
+ tasks[tasks.indexOf(task)] = edited;
311
+ break;
312
+ }
313
+ case "room/settings":
314
+ settings = {
315
+ memberMessages: event.data.memberMessages ?? settings.memberMessages,
316
+ hopLimit: event.data.hopLimit ?? settings.hopLimit,
317
+ paused: event.data.paused ?? settings.paused
318
+ };
319
+ break;
320
+ case "room/goal": {
321
+ const text = event.data.text.trim();
322
+ goal = text === "" ? void 0 : text;
323
+ break;
324
+ }
325
+ case "room/execution-metadata": {
326
+ const previous = executions.get(event.data.runId);
327
+ if (previous) executions.set(previous.id, {
328
+ ...previous,
329
+ ...event.data
330
+ });
331
+ break;
332
+ }
333
+ case "room/run-state": {
334
+ const member = byName.get(event.data.member);
335
+ const id = event.data.runId ?? `legacy:${member?.id ?? event.data.member}:${event.data.startedAt}`;
336
+ const previous = executions.get(id);
337
+ executions.set(id, {
338
+ id,
339
+ ...member?.id === void 0 ? {} : { memberId: member.id },
340
+ ...member?.childSessionId === void 0 ? {} : { childSessionId: member.childSessionId },
341
+ ...member?.provider === void 0 ? {} : { provider: member.provider },
342
+ ...previous,
343
+ ...event.data,
344
+ ...event.data.state === "running" || event.data.elapsedMs !== void 0 ? {} : { elapsedMs: Math.max(0, event.time - event.data.startedAt) }
345
+ });
346
+ const current = runs.get(event.data.member);
347
+ if (event.data.state !== "running" && event.data.runId !== void 0 && current?.runId !== void 0 && current.runId !== event.data.runId) break;
348
+ runs.set(event.data.member, {
349
+ member: event.data.member,
350
+ state: event.data.state,
351
+ startedAt: event.data.startedAt,
352
+ ...event.data.runId === void 0 ? {} : { runId: event.data.runId },
353
+ ...event.data.elapsedMs === void 0 ? {} : { elapsedMs: event.data.elapsedMs },
354
+ ...event.data.error === void 0 ? {} : { error: event.data.error }
355
+ });
356
+ break;
357
+ }
358
+ }
359
+ const planEvent = events.filter((event) => event.type === "room/plan-state").at(-1);
360
+ const plan = planEvent?.type === "room/plan-state" ? (({ requests: _requests, ...view }) => view)(planEvent.data) : void 0;
361
+ const deliveries = replayDeliveries(events);
362
+ return {
363
+ members,
364
+ relays,
365
+ tasks,
366
+ ...executions.size === 0 ? {} : { executions: [...executions.values()] },
367
+ ...plan === void 0 ? {} : { plan },
368
+ ...deliveries.length === 0 ? {} : { deliveries },
369
+ runs: [...runs.values()],
370
+ ...coordinator === void 0 ? {} : { coordinator },
371
+ ...goal === void 0 ? {} : { goal },
372
+ settings
373
+ };
374
+ }
375
+ /**
376
+ * The member's dispatch cursor as of JUST BEFORE a given dispatch event: the
377
+ * latest earlier dispatch naming them. The engine reads the role-instruction
378
+ * carry against this cursor (pass {@link Number.MAX_SAFE_INTEGER} for a
379
+ * dispatch that is not itself journaled, e.g. a relay delivery).
380
+ * @param events - the session's event log.
381
+ * @param member - the dispatch target.
382
+ * @param beforeSeq - the current dispatch event's seq.
383
+ * @returns the previous dispatch seq, or undefined on the member's first dispatch.
384
+ */
385
+ function previousCursor(events, member, beforeSeq) {
386
+ let cursor;
387
+ for (const event of events) {
388
+ if (event.type !== "room/dispatch" || event.seq >= beforeSeq) continue;
389
+ if (event.data.targets.includes(member)) cursor = event.seq;
390
+ }
391
+ return cursor;
392
+ }
393
+ /**
394
+ * What changed in the room since a member's last dispatch, as one-line
395
+ * notes for the next prompt: other members joining or leaving, renames, role
396
+ * changes, the member's own role change, and a goal change. The full roster
397
+ * rides only the first dispatch's briefing; later dispatches carry these
398
+ * deltas instead (the member's own session holds the rest), and a member who
399
+ * wants the whole picture calls `room_read`. Model, cwd and delegation-handle
400
+ * updates never reach a prompt. A member with no cursor gets no deltas — the
401
+ * briefing already covers everything.
402
+ * @param events - the session's event log.
403
+ * @param cursor - the member's pre-dispatch cursor ({@link previousCursor}).
404
+ * @param self - the member's CURRENT name (its own rename is not news to it).
405
+ * @returns the change notes (empty when nothing changed).
406
+ */
407
+ function roomChangesSince(events, cursor, self) {
408
+ if (cursor === void 0) return [];
409
+ const notes = [];
410
+ let goal;
411
+ for (const event of events) {
412
+ if (event.seq <= cursor) continue;
413
+ if (event.type === "room/member-added" && event.data.name !== self && event.data.kind !== "main-agent") {
414
+ const detail = [event.data.role, event.data.provider].filter(Boolean).join(" · ");
415
+ notes.push(`${event.data.name} 加入${detail === "" ? "" : `(${detail})`}`);
416
+ } else if (event.type === "room/member-removed" && event.data.name !== self) notes.push(`${event.data.name} 离开`);
417
+ else if (event.type === "room/member-updated") {
418
+ if (event.data.rename !== void 0 && event.data.rename !== self) notes.push(`${event.data.name} 改名为 ${event.data.rename}`);
419
+ if (event.data.role !== void 0) {
420
+ const who = event.data.rename ?? event.data.name;
421
+ const subject = who === self ? "你的职责" : `${who} 的职责`;
422
+ notes.push(event.data.role === null ? `${subject}已清除` : `${subject}改为:${event.data.role}`);
423
+ }
424
+ } else if (event.type === "room/goal") goal = { text: event.data.text.trim() };
425
+ }
426
+ if (goal !== void 0) notes.push(goal.text === "" ? "房间目标已清除" : `房间目标更新为:${goal.text}`);
427
+ return notes;
428
+ }
429
+ /**
430
+ * The role-instructions carry for the next dispatch to a member: 'initial'
431
+ * when the member has never been dispatched (their instructions open the
432
+ * first prompt), 'update' when the latest member-added/member-updated event
433
+ * naming them postdates their dispatch cursor (an edit rides the next
434
+ * dispatch as a context update). Instructions events older than the cursor
435
+ * were already carried.
436
+ * @param events - the session's event log.
437
+ * @param member - the dispatch target.
438
+ * @param cursor - the member's pre-dispatch cursor ({@link previousCursor}).
439
+ * @returns the carry, or undefined when nothing needs carrying.
440
+ */
441
+ function pendingInstructions(events, member, cursor) {
442
+ let lastSeq;
443
+ let lastInstructions;
444
+ for (const event of events) if (event.type === "room/member-added" && event.data.name === member && event.data.instructions !== void 0) {
445
+ lastSeq = event.seq;
446
+ lastInstructions = event.data.instructions;
447
+ } else if (event.type === "room/member-updated" && event.data.name === member) {
448
+ if (event.data.instructions === null) {
449
+ lastSeq = void 0;
450
+ lastInstructions = void 0;
451
+ } else if (event.data.instructions !== void 0) {
452
+ lastSeq = event.seq;
453
+ lastInstructions = event.data.instructions;
454
+ }
455
+ }
456
+ if (lastSeq === void 0 || lastInstructions === void 0) return void 0;
457
+ if (cursor === void 0) return {
458
+ kind: "initial",
459
+ instructions: lastInstructions
460
+ };
461
+ if (lastSeq > cursor) return {
462
+ kind: "update",
463
+ instructions: lastInstructions
464
+ };
465
+ }
466
+ /** Resolve legacy join identity through renames; never key a new dispatch by display name. */
467
+ function memberId(events, member) {
468
+ if (member.id !== void 0) return member.id;
469
+ let name = member.name;
470
+ for (let index = events.length - 1; index >= 0; index--) {
471
+ const event = events[index];
472
+ if (event.type === "room/member-updated" && event.data.rename === name) name = event.data.name;
473
+ if (event.type === "room/member-added" && event.data.name === name) return event.data.id ?? `legacy:${event.seq}`;
474
+ }
475
+ throw new Error("Room member has no join identity");
476
+ }
477
+ function coordinatorMember(state, events) {
478
+ if (state.coordinator === void 0) return state.members.find((member) => member.kind === "main-agent");
479
+ return state.members.find((member) => (member.id ?? (events === void 0 ? void 0 : memberId(events, member))) === state.coordinator.memberId);
480
+ }
481
+ //#endregion
482
+ export { isRoomLog as a, pendingInstructions as c, roomChangesSince as d, replayDeliveries as f, coordinatorMember as i, previousCursor as l, MAIN_AGENT_MEMBER as n, memberId as o, ROOM_EVENT_TYPES as r, parseMentions as s, DEFAULT_ROOM_SETTINGS as t, replay as u };