@khorsheed/dsh-room 0.2.4 → 0.2.5
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.
- package/CHANGELOG.md +13 -0
- package/lib/client.js +2256 -1074
- package/lib/index.js +548 -36
- package/lib/typert.host.js +249 -65
- package/lib/typert.remote-client.d.ts +4 -1
- package/lib/typert.remote-client.js +208 -53
- package/lib/types/client/InviteDialog.js +11 -4
- package/lib/types/client/RoomAttachments.d.ts +20 -0
- package/lib/types/client/RoomAttachments.js +36 -0
- package/lib/types/client/RoomComposer.d.ts +6 -12
- package/lib/types/client/RoomComposer.js +116 -12
- package/lib/types/client/RoomQuestionView.d.ts +10 -2
- package/lib/types/client/RoomQuestionView.js +18 -5
- package/lib/types/client/RoomRunView.d.ts +1 -1
- package/lib/types/client/RoomRunView.js +15 -5
- package/lib/types/client/RoomSpeechView.d.ts +24 -1
- package/lib/types/client/RoomSpeechView.js +39 -3
- package/lib/types/client/RoomStatsLine.d.ts +3 -1
- package/lib/types/client/RoomStatsLine.js +3 -1
- package/lib/types/client/RoomTaskCapsule.d.ts +6 -1
- package/lib/types/client/RoomTaskCapsule.js +2 -2
- package/lib/types/client/TasksView.js +1 -1
- package/lib/types/client/TeamView.js +1 -1
- package/lib/types/client/icons.d.ts +2 -1
- package/lib/types/client/icons.js +1 -0
- package/lib/types/client/index.js +94 -15
- package/lib/types/client/locales.d.ts +44 -0
- package/lib/types/client/locales.js +44 -0
- package/lib/types/client/nodes.d.ts +22 -3
- package/lib/types/client/nodes.js +28 -5
- package/lib/types/client/path-mentions.d.ts +34 -0
- package/lib/types/client/path-mentions.js +39 -0
- package/lib/types/client/slots.d.ts +51 -1
- package/lib/types/dispatch.d.ts +50 -0
- package/lib/types/dispatch.js +115 -1
- package/lib/types/index.d.ts +69 -1
- package/lib/types/index.js +362 -20
- package/lib/types/jobs.js +7 -3
- package/lib/types/journal.d.ts +1 -1
- package/lib/types/journal.js +18 -2
- package/lib/types/types.d.ts +77 -0
- package/package.json +10 -6
- package/lib/journal-B4FOGO6S.js +0 -482
- package/lib/journal-BWL3-i0s.js +0 -462
- package/lib/journal-Rid9AAnD.js +0 -485
package/lib/types/types.d.ts
CHANGED
|
@@ -133,6 +133,8 @@ export interface RoomMemberRemovedEvent {
|
|
|
133
133
|
* log.
|
|
134
134
|
*/
|
|
135
135
|
export interface RoomDispatchEvent {
|
|
136
|
+
/** The human message's admitted attachments, re-sent with the text on a retry. */
|
|
137
|
+
readonly attachments?: readonly RoomDispatchAttachment[];
|
|
136
138
|
/** Goal work/report correlation, absent for ordinary chat. */
|
|
137
139
|
readonly plan?: {
|
|
138
140
|
readonly goalId: string;
|
|
@@ -209,6 +211,11 @@ export interface RoomSpeechEvent {
|
|
|
209
211
|
readonly reportedTo?: string;
|
|
210
212
|
readonly member: string;
|
|
211
213
|
readonly text: string;
|
|
214
|
+
/** Questions the round asked, at their offsets in `text` (-1 = no split point found: shown first, marked mid-round). */
|
|
215
|
+
readonly asks?: readonly {
|
|
216
|
+
readonly id: string;
|
|
217
|
+
readonly at: number;
|
|
218
|
+
}[];
|
|
212
219
|
readonly childSessionId?: SessionId;
|
|
213
220
|
/** Native admission→settle milliseconds, excluding queue time. */
|
|
214
221
|
readonly durationMs?: number;
|
|
@@ -235,6 +242,11 @@ export interface RoomRunUsage {
|
|
|
235
242
|
readonly reasoningTokens?: number;
|
|
236
243
|
}
|
|
237
244
|
/** A member run's lifecycle edge. */
|
|
245
|
+
/** A running member's card moves to the bottom of the conversation; its run is untouched. */
|
|
246
|
+
export interface RoomRunMovedEvent {
|
|
247
|
+
readonly member: string;
|
|
248
|
+
readonly startedAt: number;
|
|
249
|
+
}
|
|
238
250
|
export interface RoomRunStateEvent {
|
|
239
251
|
readonly childSessionId?: SessionId;
|
|
240
252
|
/** The child session's round this run reached (the last turn/start it wrote); terminal edges only, live sessions only. */
|
|
@@ -489,6 +501,12 @@ export interface RoomQuestionEvent {
|
|
|
489
501
|
readonly member: string;
|
|
490
502
|
readonly memberId: string;
|
|
491
503
|
readonly questions: readonly RoomQuestionItem[];
|
|
504
|
+
/** The asker's round (its run id): the question renders inside that round's card and reply. */
|
|
505
|
+
readonly runId?: string;
|
|
506
|
+
/** The tail of what the round had said when it asked — where the reply splits around the question. */
|
|
507
|
+
readonly before?: string;
|
|
508
|
+
/** A consent gate (room_help's 允许/不允许): its answer only matters while the call waits, so it is never answered late. */
|
|
509
|
+
readonly consent?: true;
|
|
492
510
|
}
|
|
493
511
|
/** How a member's questions ended. */
|
|
494
512
|
export interface RoomQuestionResolvedEvent {
|
|
@@ -496,6 +514,20 @@ export interface RoomQuestionResolvedEvent {
|
|
|
496
514
|
/** `cancelled`: the call ended, timed out, or the host restarted before an answer. */
|
|
497
515
|
readonly outcome: 'answered' | 'cancelled';
|
|
498
516
|
readonly answers?: readonly RoomQuestionAnswer[];
|
|
517
|
+
/** Answered after the card expired: the answers went to the member as a new message instead of its (gone) waiting call. */
|
|
518
|
+
readonly late?: true;
|
|
519
|
+
}
|
|
520
|
+
/** Every question card the room has seen, with its outcome — inline question blocks read it. */
|
|
521
|
+
export interface RoomAsk {
|
|
522
|
+
readonly id: string;
|
|
523
|
+
readonly member: string;
|
|
524
|
+
readonly questions: readonly RoomQuestionItem[];
|
|
525
|
+
readonly state: 'pending' | 'answered' | 'cancelled';
|
|
526
|
+
readonly answers?: readonly RoomQuestionAnswer[];
|
|
527
|
+
readonly late?: true;
|
|
528
|
+
readonly consent?: true;
|
|
529
|
+
readonly runId?: string;
|
|
530
|
+
readonly time: number;
|
|
499
531
|
}
|
|
500
532
|
/** A member's questions still waiting for the human. */
|
|
501
533
|
export interface RoomQuestion {
|
|
@@ -696,6 +728,8 @@ declare module '@deepseek-ai/dsh-session/types' {
|
|
|
696
728
|
'room/speech': RoomSpeechEvent;
|
|
697
729
|
/** Member run lifecycle. */
|
|
698
730
|
'room/run-state': RoomRunStateEvent;
|
|
731
|
+
/** A running member's card re-anchors below a reply or human message that landed after it (presentation only). */
|
|
732
|
+
'room/run-moved': RoomRunMovedEvent;
|
|
699
733
|
/** Log-only observed metadata for a single execution. */
|
|
700
734
|
'room/execution-metadata': RoomExecutionMetadataEvent;
|
|
701
735
|
/** Notification gate: a member-to-member relay arrived (pending). */
|
|
@@ -829,6 +863,8 @@ export interface RoomExecution extends RoomMemberRun {
|
|
|
829
863
|
* design note).
|
|
830
864
|
*/
|
|
831
865
|
export interface RoomState {
|
|
866
|
+
/** All question cards with their outcomes (inline blocks in run cards and replies). */
|
|
867
|
+
readonly askLog?: readonly RoomAsk[];
|
|
832
868
|
readonly executions?: readonly RoomExecution[];
|
|
833
869
|
readonly plan?: Omit<RoomPlan, 'requests'>;
|
|
834
870
|
readonly deliveries?: readonly RoomDelivery[];
|
|
@@ -904,6 +940,11 @@ export type RoomFailure = {
|
|
|
904
940
|
} | {
|
|
905
941
|
readonly code: 'empty-text';
|
|
906
942
|
}
|
|
943
|
+
/** An attachment was refused (not uploaded to this room, too large, an unsupported image…). */
|
|
944
|
+
| {
|
|
945
|
+
readonly code: 'attachment-invalid';
|
|
946
|
+
readonly message: string;
|
|
947
|
+
}
|
|
907
948
|
/** The public role is longer than 30 characters. */
|
|
908
949
|
| {
|
|
909
950
|
readonly code: 'role-too-long';
|
|
@@ -1002,6 +1043,16 @@ export interface RoomMemberMessage {
|
|
|
1002
1043
|
*/
|
|
1003
1044
|
export type RoomMemberMessageReceipt = 'sent' | 'pending-confirm' | 'busy' | `sent ${string}` | `pending-confirm ${string}`;
|
|
1004
1045
|
/** isRoom probe: does this session carry the room marker? */
|
|
1046
|
+
/** pendingCards request: no arguments (every live room the host knows). */
|
|
1047
|
+
export interface RoomPendingCardsRequest {
|
|
1048
|
+
readonly sessionId?: SessionId;
|
|
1049
|
+
}
|
|
1050
|
+
/** One question/approval card waiting for the human in a room (the session list's 「等你处理」). */
|
|
1051
|
+
export interface RoomPendingCard {
|
|
1052
|
+
readonly sessionId: string;
|
|
1053
|
+
readonly id: string;
|
|
1054
|
+
readonly kind: 'question' | 'approval';
|
|
1055
|
+
}
|
|
1005
1056
|
export interface RoomIsRoomRequest {
|
|
1006
1057
|
/** Session to probe. */
|
|
1007
1058
|
readonly sessionId: SessionId;
|
|
@@ -1199,6 +1250,30 @@ export interface RoomPostMessageRequest {
|
|
|
1199
1250
|
readonly targets?: readonly string[];
|
|
1200
1251
|
/** The composer's task tag: `new` or a task root id (see RoomSetTaskHintRequest). */
|
|
1201
1252
|
readonly task?: string;
|
|
1253
|
+
/**
|
|
1254
|
+
* Draft attachments in the official composer's wire form: an image's
|
|
1255
|
+
* base64 bytes, or a file's upload receipt (uploaded to this room session
|
|
1256
|
+
* on pick). The host admits them into the human message; members get them
|
|
1257
|
+
* by local path.
|
|
1258
|
+
*/
|
|
1259
|
+
readonly attachments?: readonly RoomAttachmentInput[];
|
|
1260
|
+
}
|
|
1261
|
+
/** One composer attachment as it crosses the wire (the official prompt part shape). */
|
|
1262
|
+
export type RoomAttachmentInput = {
|
|
1263
|
+
readonly type: 'image';
|
|
1264
|
+
readonly data: string;
|
|
1265
|
+
readonly mediaType: string;
|
|
1266
|
+
readonly name?: string;
|
|
1267
|
+
} | {
|
|
1268
|
+
readonly type: 'file';
|
|
1269
|
+
readonly receiptId: string;
|
|
1270
|
+
};
|
|
1271
|
+
/** An admitted attachment as a dispatch records it: what members are told to read. */
|
|
1272
|
+
export interface RoomDispatchAttachment {
|
|
1273
|
+
readonly kind: 'image' | 'file';
|
|
1274
|
+
readonly name?: string;
|
|
1275
|
+
/** Absolute host path; absent when the attachment store is not file-backed. */
|
|
1276
|
+
readonly path?: string;
|
|
1202
1277
|
}
|
|
1203
1278
|
/** What the postMessage parser made of the raw text. */
|
|
1204
1279
|
export interface RoomPostParsed {
|
|
@@ -1472,10 +1547,12 @@ export interface RoomJob {
|
|
|
1472
1547
|
/** 1-based ordinal in the room, for 「任务 N」 and the coordinator's `task` argument. */
|
|
1473
1548
|
readonly number: number;
|
|
1474
1549
|
readonly title: string;
|
|
1550
|
+
/** `from`: the member id a member/coordinator dispatch came from; absent = the human. */
|
|
1475
1551
|
readonly messages: readonly {
|
|
1476
1552
|
readonly id: string;
|
|
1477
1553
|
readonly time: number;
|
|
1478
1554
|
readonly text: string;
|
|
1555
|
+
readonly from?: string;
|
|
1479
1556
|
readonly by?: RoomTaskLinkEvent['by'];
|
|
1480
1557
|
readonly probability?: number;
|
|
1481
1558
|
}[];
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"cordis",
|
|
8
8
|
"multi-agent"
|
|
9
9
|
],
|
|
10
|
-
"version": "0.2.
|
|
10
|
+
"version": "0.2.5",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"main": "lib/index.js",
|
|
13
13
|
"types": "lib/types/index.d.ts",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
76
76
|
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
77
77
|
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
78
79
|
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
79
80
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
80
81
|
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
@@ -85,9 +86,9 @@
|
|
|
85
86
|
"@deepseek-ai/dsh-tool-todo": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
86
87
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
87
88
|
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
88
|
-
"@
|
|
89
|
-
"
|
|
90
|
-
"
|
|
89
|
+
"@deepseek-ai/dsh-util-workspace-path": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
|
|
90
|
+
"@khorsheed/dsh-local-agent": "^0.2.2",
|
|
91
|
+
"react": "^18.2.0"
|
|
91
92
|
},
|
|
92
93
|
"peerDependenciesMeta": {
|
|
93
94
|
"@deepseek-ai/dsh-agent-preset-registry": {
|
|
@@ -96,6 +97,9 @@
|
|
|
96
97
|
"@deepseek-ai/dsh-agent-presets": {
|
|
97
98
|
"optional": true
|
|
98
99
|
},
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
99
103
|
"@deepseek-ai/dsh-session-stats": {
|
|
100
104
|
"optional": true
|
|
101
105
|
},
|
|
@@ -105,10 +109,10 @@
|
|
|
105
109
|
"@deepseek-ai/dsh-tool-todo": {
|
|
106
110
|
"optional": true
|
|
107
111
|
},
|
|
108
|
-
"@
|
|
112
|
+
"@deepseek-ai/dsh-util-workspace-path": {
|
|
109
113
|
"optional": true
|
|
110
114
|
},
|
|
111
|
-
"@
|
|
115
|
+
"@khorsheed/dsh-local-agent": {
|
|
112
116
|
"optional": true
|
|
113
117
|
}
|
|
114
118
|
},
|
package/lib/journal-B4FOGO6S.js
DELETED
|
@@ -1,482 +0,0 @@
|
|
|
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 };
|