dsh-team-rooms 1.0.0

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 (46) hide show
  1. package/ARCHITECTURE.md +127 -0
  2. package/LICENSE +201 -0
  3. package/README-es.md +248 -0
  4. package/README-hi.md +248 -0
  5. package/README-pt.md +248 -0
  6. package/README-zh.md +248 -0
  7. package/README.md +248 -0
  8. package/THIRD_PARTY_NOTICES.md +41 -0
  9. package/cordis.patch.yml +34 -0
  10. package/lib/client.js +4713 -0
  11. package/lib/index.js +6580 -0
  12. package/lib/types/audit.d.ts +80 -0
  13. package/lib/types/client/TeamRoomsSection.d.ts +33 -0
  14. package/lib/types/client/index.d.ts +41 -0
  15. package/lib/types/client/room-locales.d.ts +61 -0
  16. package/lib/types/client/room-presenter.d.ts +67 -0
  17. package/lib/types/facts.d.ts +66 -0
  18. package/lib/types/inbound.d.ts +282 -0
  19. package/lib/types/index.d.ts +125 -0
  20. package/lib/types/room/commands.d.ts +23 -0
  21. package/lib/types/room/domain.d.ts +72 -0
  22. package/lib/types/room/events.d.ts +105 -0
  23. package/lib/types/room/hub.d.ts +243 -0
  24. package/lib/types/room/projection.d.ts +388 -0
  25. package/lib/types/room/schema.d.ts +550 -0
  26. package/lib/types/room/tools.d.ts +34 -0
  27. package/lib/types/vocabulary.d.ts +20 -0
  28. package/package.json +170 -0
  29. package/src/audit.ts +113 -0
  30. package/src/client/TeamRoomsSection.module.css +272 -0
  31. package/src/client/TeamRoomsSection.tsx +369 -0
  32. package/src/client/css-modules.d.ts +5 -0
  33. package/src/client/index.ts +215 -0
  34. package/src/client/room-locales.ts +115 -0
  35. package/src/client/room-presenter.ts +126 -0
  36. package/src/facts.ts +127 -0
  37. package/src/inbound.ts +440 -0
  38. package/src/index.ts +309 -0
  39. package/src/room/commands.ts +278 -0
  40. package/src/room/domain.ts +38 -0
  41. package/src/room/events.ts +116 -0
  42. package/src/room/hub.ts +1022 -0
  43. package/src/room/projection.ts +267 -0
  44. package/src/room/schema.ts +153 -0
  45. package/src/room/tools.ts +603 -0
  46. package/src/vocabulary.ts +20 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * dsh-team-rooms: persistent, cross-session team rooms for DeepSeek Harness.
3
+ *
4
+ * A room is `{members (each an independent session), message bus
5
+ * (directed/broadcast), task board, shared timeline}` persisted in the
6
+ * harness's own storage layer — the `team_rooms` storage domain (SQLite or
7
+ * JSONL backend; the deployment chooses, the plugin adds no service of its
8
+ * own) — and it recovers across DSH restarts. The `/room` command family
9
+ * drives rooms from the user; the model gets eight `room_*` tools;
10
+ * cross-member task handoffs route through the official approval seam; and
11
+ * member sessions receive a one-line role-statement brief (Minimal persona
12
+ * style) on join and resume.
13
+ *
14
+ * Model-visible ⟺ recorded: every delivered room message is a durable
15
+ * `user/message` in the member's own session log, and the shared timeline
16
+ * mirrors as log-only `team-room/fact` events folded by the `teamRoom`
17
+ * projection the settings panel renders. The storage domain stays the
18
+ * cross-session authority; the projection is each member's own reconstructed
19
+ * copy.
20
+ *
21
+ * Extracted verbatim from `dsh-background-agents` 0.9.6, whose background-agent
22
+ * half is superseded by native DSH continuable subagents. Three identity
23
+ * strings must stay byte-identical to that version so existing profiles keep
24
+ * working: the storage domain `team_rooms`, the projection key `teamRoom`,
25
+ * and the session event type `team-room/fact` (plus the client slot id
26
+ * `team-rooms`).
27
+ *
28
+ * This half never touches `ctx.subagents`: no room operation spawns or steers
29
+ * a child agent, so the package carries no subagent dependency at all.
30
+ *
31
+ * @module dsh-team-rooms
32
+ */
33
+ import type { Context } from '@deepseek-ai/cordis';
34
+ import Schema from '@deepseek-ai/schemastery';
35
+ /** The cordis row name; the mounted graph id and the logger channel share it. */
36
+ export declare const name = "team-rooms";
37
+ /** Hard service dependencies: the tool registry, the agent registry, and the session store. */
38
+ export declare const inject: string[];
39
+ /**
40
+ * Lifecycle policy. Every tunable is a validated Config field: thresholds and
41
+ * retentions belong in cordis.yml, never in code. Nothing is required (rooms
42
+ * mount wherever the storage domain is composed); the Schemastery schema
43
+ * materializes the documented defaults from {@link DEFAULTS}, and direct
44
+ * apply() callers keep the same defaults.
45
+ */
46
+ export interface Config {
47
+ /** Hard cap on team rooms across the profile. */
48
+ maxRooms?: number;
49
+ /** Hard cap on members per room. */
50
+ maxMembersPerRoom?: number;
51
+ /** Hard cap on rooms one member session may join. */
52
+ maxRoomsPerMember?: number;
53
+ /** Bus messages kept per room (the message retention window). */
54
+ busRetention?: number;
55
+ /** Timeline events kept per room. */
56
+ timelineRetention?: number;
57
+ /** Completed tasks kept per room; older `done` rows are pruned. */
58
+ taskRetention?: number;
59
+ /** Hard cap on one room message's text (rejected above, never truncated). */
60
+ maxMessageChars?: number;
61
+ /** Inject the short room brief into member sessions (join + resume). */
62
+ injectRoomBrief?: boolean;
63
+ /**
64
+ * How long the `team_rooms` storage-domain open may take before every
65
+ * room operation fails loud (`store-unavailable`) instead of hanging.
66
+ */
67
+ roomOpenTimeoutMs?: number;
68
+ /**
69
+ * Force the log-only `team-room/fact` events even on hosts that drop the
70
+ * `ignorable` envelope marker (every released rc line through
71
+ * `0.1.0-rc.8`). Deliberately dangerous: unmarked fact events make
72
+ * sessions unresumable on stricter harness builds. Default `false` — the
73
+ * runtime detects such hosts and skips fact appends (the `teamRoom`
74
+ * projection degrades to an empty fold; the durable store and the
75
+ * model-visible deliveries keep working).
76
+ */
77
+ allowUnmarkedFacts?: boolean;
78
+ /** Cross-ecosystem inbound bridge (P2) policy; absent = disabled. */
79
+ inbound?: InboundConfig;
80
+ }
81
+ /** Cross-ecosystem inbound bridge (P2) policy. */
82
+ export interface InboundConfig {
83
+ /**
84
+ * Enable the newline-delimited JSON-RPC 2.0 stdio inbound bridge that lets
85
+ * external agent runtimes (OpenAI Agents SDK / CrewAI) publish into team
86
+ * rooms. Default `false` (fail-closed — the bridge never spawns unless
87
+ * explicitly enabled).
88
+ */
89
+ enabled?: boolean;
90
+ /**
91
+ * External runtime launch command. When `enabled` and present, the plugin
92
+ * spawns it and listens for notifications on its stdout. Absent or
93
+ * unspawnable = the bridge stays dormant with a logged warning.
94
+ */
95
+ command?: string;
96
+ }
97
+ /**
98
+ * The single source of truth for every optional policy default: the schema
99
+ * materializes from it and apply() falls back to it, so the two can never
100
+ * drift apart.
101
+ */
102
+ export declare const DEFAULTS: {
103
+ readonly maxRooms: 16;
104
+ readonly maxMembersPerRoom: 8;
105
+ readonly maxRoomsPerMember: 4;
106
+ readonly busRetention: 200;
107
+ readonly timelineRetention: 500;
108
+ readonly taskRetention: 50;
109
+ readonly maxMessageChars: 4000;
110
+ readonly injectRoomBrief: true;
111
+ readonly roomOpenTimeoutMs: 15000;
112
+ readonly allowUnmarkedFacts: false;
113
+ readonly inbound: {
114
+ readonly enabled: false;
115
+ };
116
+ };
117
+ export declare const Config: Schema<Config>;
118
+ /**
119
+ * Mount the room hub, the eight `room_*` tools, the `/room` command family,
120
+ * the `teamRoom` projection unit, and the room prompt section.
121
+ * @param ctx - context carrying the tool registry, the agent registry, and the session store.
122
+ * @param config - room policy (Schemastery-validated).
123
+ */
124
+ export declare function apply(ctx: Context, config: Config): void;
125
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The human-facing `/room` command family: create, join, leave, list, send,
3
+ * tasks, and the task board subcommands (add / assign / claim / done). The
4
+ * command executes directly against the receiving session's agent — it never
5
+ * goes through the model — and every write lands on the shared durable room
6
+ * store. Because the human typed the command, command-side handoffs are
7
+ * already user-authorized (the model-facing `room_transfer_task` tool is
8
+ * what routes through the approval seam).
9
+ *
10
+ * @module dsh-team-rooms/room/commands
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import { type RoomHub } from './hub.js';
14
+ /**
15
+ * Register the `/room` command when the harness composes the command
16
+ * registry. The handler runs outside any model turn, so it performs the
17
+ * mutations directly (the typing user is the authorizer). The command surface
18
+ * is optional: without it the room_* tools still work from the model side.
19
+ * @returns the exact command disposer, or undefined when no command registry
20
+ * is composed.
21
+ */
22
+ export declare function registerRoomCommand(ctx: Context, hub: RoomHub): (() => void) | undefined;
23
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The `team_rooms` storage-domain declaration: rooms, the message bus, the
3
+ * task board, and the shared timeline as four KV tables over the harness's
4
+ * own storage layer (SQLite or JSONL backend — the deployment chooses; the
5
+ * plugin adds no service of its own). Records are validated at the durable
6
+ * boundary by the same zod schemas the projection and the client share.
7
+ *
8
+ * The domain's single write chain is the ordering authority: every bus
9
+ * append and cursor bump queues on it, so concurrent posters can never
10
+ * interleave a read-modify-write.
11
+ *
12
+ * @module dsh-team-rooms/room/domain
13
+ */
14
+ /** Branded-ish key aliases: append-table keys are plain `${roomId}/${…}` strings. */
15
+ export type RoomKey = string & {
16
+ readonly __roomKey?: never;
17
+ };
18
+ export type AppendKey = string & {
19
+ readonly __appendKey?: never;
20
+ };
21
+ /**
22
+ * The domain spec: identity, format version, and the four declared tables.
23
+ * The same schemas validate every record at the durable read boundary.
24
+ */
25
+ export declare const teamRoomsDomainSpec: {
26
+ name: string;
27
+ version: number;
28
+ tables: {
29
+ rooms: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<RoomKey, {
30
+ name: string;
31
+ roomId: string;
32
+ createdAt: number;
33
+ members: {
34
+ sessionId: string;
35
+ role: "owner" | "member";
36
+ joinedAt: number;
37
+ lastDeliveredSeq: number;
38
+ lastFactSeq: number;
39
+ }[];
40
+ busNext: number;
41
+ timelineNext: number;
42
+ }>;
43
+ bus: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<AppendKey, {
44
+ text: string;
45
+ roomId: string;
46
+ createdAt: number;
47
+ seq: number;
48
+ senderSessionId: string;
49
+ toSessionId?: string | undefined;
50
+ }>;
51
+ tasks: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<AppendKey, {
52
+ status: "todo" | "in-progress" | "done";
53
+ title: string;
54
+ roomId: string;
55
+ createdAt: number;
56
+ taskId: string;
57
+ description: string;
58
+ assigneeSessionId: string | null;
59
+ createdBy: string;
60
+ updatedAt: number;
61
+ completedAt?: number | undefined;
62
+ }>;
63
+ timeline: import("@deepseek-ai/dsh-storage-domain").DomainTableSpec<AppendKey, {
64
+ at: number;
65
+ data: Record<string, unknown>;
66
+ roomId: string;
67
+ seq: number;
68
+ kind: "room-created" | "member-joined" | "member-left" | "message-posted" | "message-directed" | "task-created" | "task-claimed" | "task-assigned" | "task-completed";
69
+ }>;
70
+ };
71
+ };
72
+ //# sourceMappingURL=domain.d.ts.map
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The room-owned durable fact event: `team-room/fact`, appended to every
3
+ * MEMBER session log as a log-only record stamped with the envelope's
4
+ * `ignorable: true` marker (the same discipline as the background-agents
5
+ * fact channel). Two invariants hold:
6
+ *
7
+ * - model-visible ⟺ recorded: the messages the member's model sees are
8
+ * delivered through the official inbox (`agent.followup`/`agent.inject`,
9
+ * durable `user/message` events), while the shared timeline rides these
10
+ * log-only facts — the room store (`team_rooms` domain) stays the
11
+ * cross-session authority;
12
+ * - catch-up completeness: members offline while a fact landed receive every
13
+ * missed fact (and bus message) in store order on their next activation, so
14
+ * the `teamRoom` projection always reconstructs the shared view from the
15
+ * member's own log.
16
+ *
17
+ * Every timeline-bearing fact carries the canonical store `timelineSeq`, so
18
+ * the folded view orders exactly like the room store.
19
+ *
20
+ * @module dsh-team-rooms/room/events
21
+ */
22
+ import type { RoomMember, TaskRecord, TimelineEvent } from './schema.js';
23
+ /** The log-only fact event type this plugin appends to member sessions. */
24
+ export declare const TEAM_ROOM_FACT: "team-room/fact";
25
+ /** One structured room fact, discriminated on `kind`. */
26
+ export type TeamRoomFact = {
27
+ /**
28
+ * This session became a member (create or join). Carries the full
29
+ * snapshot — room identity, member list, open tasks, and the retained
30
+ * timeline — so a cold member log reconstructs the shared view without
31
+ * reading the room store.
32
+ */
33
+ readonly kind: 'room-joined';
34
+ readonly roomId: string;
35
+ readonly name: string;
36
+ readonly createdAt: number;
37
+ readonly members: RoomMember[];
38
+ readonly tasks: TaskRecord[];
39
+ readonly timeline: TimelineEvent[];
40
+ } | {
41
+ /** Another session joined the room. */
42
+ readonly kind: 'member-joined';
43
+ readonly roomId: string;
44
+ readonly sessionId: string;
45
+ readonly role: RoomMember['role'];
46
+ readonly joinedAt: number;
47
+ readonly timelineSeq: number;
48
+ } | {
49
+ /** A member left (or the room was deleted). */
50
+ readonly kind: 'member-left';
51
+ readonly roomId: string;
52
+ readonly sessionId: string;
53
+ readonly timelineSeq: number;
54
+ } | {
55
+ /** One bus message; `toSessionId` marks a directed delivery. */
56
+ readonly kind: 'message-posted';
57
+ readonly roomId: string;
58
+ readonly seq: number;
59
+ readonly timelineSeq: number;
60
+ readonly senderSessionId: string;
61
+ readonly toSessionId?: string;
62
+ readonly text: string;
63
+ readonly createdAt: number;
64
+ } | {
65
+ /** A new task entered the board. */
66
+ readonly kind: 'task-created';
67
+ readonly roomId: string;
68
+ readonly taskId: string;
69
+ readonly title: string;
70
+ readonly description: string;
71
+ readonly assigneeSessionId: string | null;
72
+ readonly createdBy: string;
73
+ readonly createdAt: number;
74
+ readonly timelineSeq: number;
75
+ } | {
76
+ /** A member claimed a task (assignee + in-progress). */
77
+ readonly kind: 'task-claimed';
78
+ readonly roomId: string;
79
+ readonly taskId: string;
80
+ readonly assigneeSessionId: string;
81
+ readonly at: number;
82
+ readonly timelineSeq: number;
83
+ } | {
84
+ /** The owner handed a task to another member. */
85
+ readonly kind: 'task-assigned';
86
+ readonly roomId: string;
87
+ readonly taskId: string;
88
+ readonly assigneeSessionId: string;
89
+ readonly bySessionId: string;
90
+ readonly at: number;
91
+ readonly timelineSeq: number;
92
+ } | {
93
+ /** A task reached `done`. */
94
+ readonly kind: 'task-completed';
95
+ readonly roomId: string;
96
+ readonly taskId: string;
97
+ readonly at: number;
98
+ readonly timelineSeq: number;
99
+ };
100
+ declare module '@deepseek-ai/dsh-session/types' {
101
+ interface SessionEventMap {
102
+ [TEAM_ROOM_FACT]: TeamRoomFact;
103
+ }
104
+ }
105
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1,243 @@
1
+ /**
2
+ * RoomHub: the host-side service behind team rooms. It opens the
3
+ * `team_rooms` storage domain, owns every room mutation (membership, the
4
+ * message bus, the task board, the timeline), and drives delivery:
5
+ *
6
+ * - every write queues on ONE hub chain (the domain's single write chain is
7
+ * the ordering authority — concurrent posters cannot interleave a
8
+ * read-modify-write, and bus seqs mint strictly in commit order);
9
+ * - model-visible delivery goes through the official inbox
10
+ * (`agent.followup` wakes live members; offline members receive their
11
+ * backlog through `agent.inject` when their session next starts), so every
12
+ * model-visible room message is a durable `user/message` event in the
13
+ * member's own session log — model-visible ⟺ recorded;
14
+ * - the shared timeline mirrors into every member's log as log-only
15
+ * `team-room/fact` events (ignorable), so the `teamRoom` projection
16
+ * reconstructs the room view from each member's own durable log;
17
+ * - per-member delivery is at-least-once and ordered: a crash between bus
18
+ * commit and delivery re-delivers on catch-up (the cursor only advances
19
+ * after delivery), and per-member chains serialize delivery order.
20
+ *
21
+ * @module dsh-team-rooms/room/hub
22
+ */
23
+ import { Context, Service } from '@deepseek-ai/cordis';
24
+ import type { Agent } from '@deepseek-ai/dsh-agent';
25
+ import { SessionId, type Session } from '@deepseek-ai/dsh-session';
26
+ import { type BusMessage, type RoomMember, type RoomRecord, type TaskRecord, type TimelineEvent } from './schema.js';
27
+ import type { FactAppender } from '../facts.js';
28
+ /** Tunables the room feature honors; every threshold is a validated Config field. */
29
+ export interface RoomConfig {
30
+ /** Hard cap on rooms across the profile. */
31
+ readonly maxRooms: number;
32
+ /** Hard cap on members per room. */
33
+ readonly maxMembersPerRoom: number;
34
+ /** Hard cap on rooms one member session may join. */
35
+ readonly maxRoomsPerMember: number;
36
+ /** Bus entries kept per room (the message retention window). */
37
+ readonly busRetention: number;
38
+ /** Timeline entries kept per room. */
39
+ readonly timelineRetention: number;
40
+ /** Completed tasks kept per room (older `done` rows are pruned). */
41
+ readonly taskRetention: number;
42
+ /** Hard cap on one bus message's text (rejected above, never truncated). */
43
+ readonly maxMessageChars: number;
44
+ /** Inject the short room brief into member sessions (join + resume). */
45
+ readonly injectRoomBrief: boolean;
46
+ /**
47
+ * How long the `team_rooms` storage-domain open may take before every
48
+ * room operation fails loud (`store-unavailable`) instead of hanging
49
+ * forever (a stuck storage provider used to leave `/room` commands
50
+ * without a `command/done`).
51
+ */
52
+ readonly roomOpenTimeoutMs: number;
53
+ }
54
+ /** A domain-level rejection with a stable code; tools and commands render it. */
55
+ export declare class RoomError extends Error {
56
+ readonly code: string;
57
+ constructor(code: string, message: string);
58
+ }
59
+ /** Read face of the live agent registry (kept narrow for tests). */
60
+ export interface LiveAgents {
61
+ get(id: SessionId): Agent | undefined;
62
+ }
63
+ /** Read face of the live session store (kept narrow for tests). */
64
+ export interface LiveSessions {
65
+ get(id: SessionId): Session | undefined;
66
+ }
67
+ /** Bus record the hub mints from a committed post. */
68
+ export interface PostedMessage {
69
+ readonly roomId: string;
70
+ readonly seq: number;
71
+ readonly senderSessionId: string;
72
+ readonly toSessionId?: string;
73
+ readonly text: string;
74
+ readonly createdAt: number;
75
+ }
76
+ /**
77
+ * The team-room service. Constructed in apply() with the validated room
78
+ * policy once the storage domain is available; {@link open} opens the
79
+ * `team_rooms` storage domain and the owning fiber closes it.
80
+ */
81
+ export declare class RoomHub extends Service {
82
+ private readonly config;
83
+ private readonly agents;
84
+ private readonly sessions;
85
+ private readonly facts;
86
+ private rooms?;
87
+ private bus?;
88
+ private tasks?;
89
+ private timeline?;
90
+ /** One write chain: every mutation (create/join/post/task) queues here. */
91
+ private tail;
92
+ /** Per-room delivery chains: posts to one room serialize delivery order. */
93
+ private readonly roomChains;
94
+ /** Per-member delivery chains: live delivery and catch-up never interleave. */
95
+ private readonly memberChains;
96
+ /** Resolves once the storage domain is open (or failed); gates every operation. */
97
+ private readonly ready;
98
+ private readyResolve;
99
+ private initError;
100
+ constructor(ctx: Context, config: RoomConfig, agents: LiveAgents, sessions: LiveSessions, facts: FactAppender);
101
+ /**
102
+ * Open the `team_rooms` storage domain and load its four tables. Called
103
+ * once by the mount site after the storage domain becomes available; every
104
+ * hub operation gates on this resolution. A failed open fails every
105
+ * operation loud through {@link requireRooms} instead of hanging — and a
106
+ * STUCK open (a storage provider whose open promise never settles) is cut
107
+ * off by the `roomOpenTimeoutMs` timer so `/room` commands still settle
108
+ * with a `store-unavailable` error instead of never emitting
109
+ * `command/done`.
110
+ */
111
+ open(): Promise<void>;
112
+ /** One room record, or undefined. */
113
+ room(roomId: string): Promise<RoomRecord | undefined>;
114
+ /** Every room one session is a member of, in creation order. */
115
+ roomsOfMember(sessionId: SessionId): Promise<RoomRecord[]>;
116
+ /** All rooms (used by the command surface for the roster). */
117
+ allRooms(): Promise<RoomRecord[]>;
118
+ /** Bus messages of one room with seq > since, in seq order. */
119
+ busMessages(roomId: string, since?: number): Promise<BusMessage[]>;
120
+ /** The task board of one room, creation order. */
121
+ tasksOf(roomId: string): Promise<TaskRecord[]>;
122
+ /** The timeline of one room with seq > since, in seq order. */
123
+ timelineOf(roomId: string, since?: number): Promise<TimelineEvent[]>;
124
+ /** The member slot of one session in one room, or undefined. */
125
+ memberOf(room: RoomRecord, sessionId: SessionId): RoomMember | undefined;
126
+ /**
127
+ * Create one room; the creator becomes its owner member. Enforces the
128
+ * profile-wide `maxRooms` cap inside the write chain.
129
+ */
130
+ createRoom(sessionId: SessionId, name: string, now?: number): Promise<RoomRecord>;
131
+ /**
132
+ * Register one session as a member of an existing room (cross-session
133
+ * membership). Enforces the per-room and per-member caps inside the chain.
134
+ */
135
+ joinRoom(sessionId: SessionId, roomId: string, now?: number): Promise<RoomRecord>;
136
+ /** Remove one member. The owner leaving deletes the room. */
137
+ leaveRoom(sessionId: SessionId, roomId: string, now?: number): Promise<RoomRecord | undefined>;
138
+ /** Owner-only room deletion. */
139
+ deleteRoom(sessionId: SessionId, roomId: string): Promise<void>;
140
+ /**
141
+ * Post one message onto the bus: broadcast, or directed when `toSessionId`
142
+ * names a member. Runs on the per-room delivery chain so per-member
143
+ * delivery order always equals bus seq order, and commits the store write
144
+ * before any delivery (cursors advance only after delivery — at-least-once).
145
+ */
146
+ postMessage(input: {
147
+ readonly roomId: string;
148
+ readonly senderSessionId: SessionId;
149
+ readonly text: string;
150
+ readonly toSessionId?: SessionId;
151
+ }, now?: number): Promise<PostedMessage>;
152
+ /** Create a task on the board (assignee optional; default unassigned). */
153
+ createTask(input: {
154
+ readonly roomId: string;
155
+ readonly bySessionId: SessionId;
156
+ readonly title: string;
157
+ readonly description?: string;
158
+ readonly assigneeSessionId?: SessionId;
159
+ }, now?: number): Promise<TaskRecord>;
160
+ /** Claim a task for the calling member (in-progress + assignee). */
161
+ claimTask(input: {
162
+ readonly roomId: string;
163
+ readonly bySessionId: SessionId;
164
+ readonly taskId: string;
165
+ }, now?: number): Promise<TaskRecord>;
166
+ /**
167
+ * Hand a task to another member. Callers outside a tool (the /room command)
168
+ * are the user themselves; the room_transfer_task TOOL gates this same
169
+ * mutation behind the approval service.
170
+ */
171
+ assignTask(input: {
172
+ readonly roomId: string;
173
+ readonly bySessionId: SessionId;
174
+ readonly taskId: string;
175
+ readonly toSessionId: SessionId;
176
+ }, now?: number): Promise<TaskRecord>;
177
+ /** Complete a task (done). Only the assignee or the owner may complete it. */
178
+ completeTask(input: {
179
+ readonly roomId: string;
180
+ readonly bySessionId: SessionId;
181
+ readonly taskId: string;
182
+ }, now?: number): Promise<TaskRecord>;
183
+ /**
184
+ * Deliver everything a member missed while offline: the log-only facts
185
+ * (shared timeline) and the model-visible bus backlog, both in store order.
186
+ * Runs on the member's delivery chain so a live post cannot interleave.
187
+ * Idempotent: cursors make a second call a no-op.
188
+ */
189
+ catchUp(sessionId: SessionId): Promise<void>;
190
+ /**
191
+ * The member brief: a SHORT injected paragraph that starts with the
192
+ * one-line role statement (Minimal-persona style) and names the room id,
193
+ * the member count, and the collaboration tools. Injected on join and on
194
+ * every session start (resume included), as a durable user message — the
195
+ * member's model sees exactly what the member's log records.
196
+ */
197
+ injectBrief(sessionId: SessionId, room: RoomRecord): void;
198
+ /** Build the minimal brief paragraph for one room. */
199
+ briefText(room: RoomRecord): string;
200
+ /**
201
+ * Queue one mutation on the single write chain; rejections are contained.
202
+ * The previous tail is captured SYNCHRONOUSLY: reading `this.tail` after
203
+ * `ready` resolves would see the just-assigned tail (a promise that settles
204
+ * with this very result) and deadlock the whole write chain — the exact
205
+ * hang that left `/room create` without a `command/done`.
206
+ */
207
+ private enqueue;
208
+ /** Serialize work per room (delivery order = bus order). */
209
+ private onRoomChain;
210
+ /** Serialize delivery per member (live delivery vs catch-up). */
211
+ private onMemberChain;
212
+ /** One task-board mutation shared by claim/assign/complete. */
213
+ private mutateTask;
214
+ /** Append the room-joined snapshot fact to one session's live log. */
215
+ private appendFactTo;
216
+ /** Append one fact to every LIVE member session (offline members catch up). */
217
+ private broadcastFact;
218
+ /** Deliver one posted message's fact + model-visible copy, per member. */
219
+ private deliverPosted;
220
+ /** Advance delivery cursors for the members that just received the post. */
221
+ private advanceCursors;
222
+ /** Advance the fact cursor for every member whose session is live now. */
223
+ private advanceFactsForLive;
224
+ /** One model-visible delivery: a durable user message with a room header. */
225
+ private roomUserMessage;
226
+ /** The room-joined snapshot fact. */
227
+ private joinFact;
228
+ /** Rebuild the fact one timeline event corresponds to (catch-up replay). */
229
+ private factFromTimeline;
230
+ private taskCreatedFact;
231
+ private taskMutationFact;
232
+ private timelineEvent;
233
+ /** Retention pruning: bus, timeline, and completed tasks. */
234
+ private pruneRoom;
235
+ /** Delete every bus/task/timeline row of a deleted room. */
236
+ private purgeRoom;
237
+ private requireRoom;
238
+ private requireRooms;
239
+ private requireBus;
240
+ private requireTasks;
241
+ private requireTimeline;
242
+ }
243
+ //# sourceMappingURL=hub.d.ts.map