dsh-live-teams 0.1.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 (69) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +11 -0
  3. package/README.md +85 -0
  4. package/cordis.patch.yml +25 -0
  5. package/lib/binding.d.ts +18 -0
  6. package/lib/binding.js +42 -0
  7. package/lib/changed-paths.d.ts +26 -0
  8. package/lib/changed-paths.js +69 -0
  9. package/lib/client.js +6753 -0
  10. package/lib/command-queue.d.ts +60 -0
  11. package/lib/command-queue.js +185 -0
  12. package/lib/compatibility.js +109 -0
  13. package/lib/context-provider.d.ts +110 -0
  14. package/lib/context-provider.js +249 -0
  15. package/lib/dispatch.d.ts +174 -0
  16. package/lib/dispatch.js +624 -0
  17. package/lib/errors.d.ts +36 -0
  18. package/lib/errors.js +103 -0
  19. package/lib/git-artifacts.d.ts +50 -0
  20. package/lib/git-artifacts.js +242 -0
  21. package/lib/index.d.ts +14 -0
  22. package/lib/index.js +14 -0
  23. package/lib/mailbox.d.ts +274 -0
  24. package/lib/mailbox.js +721 -0
  25. package/lib/member-tools.d.ts +57 -0
  26. package/lib/member-tools.js +1265 -0
  27. package/lib/migrations.d.ts +17 -0
  28. package/lib/migrations.js +47 -0
  29. package/lib/plugin.d.ts +106 -0
  30. package/lib/plugin.js +1003 -0
  31. package/lib/roles.d.ts +35 -0
  32. package/lib/roles.js +284 -0
  33. package/lib/routes.d.ts +586 -0
  34. package/lib/routes.js +2816 -0
  35. package/lib/scope.d.ts +62 -0
  36. package/lib/scope.js +133 -0
  37. package/lib/session-bridge.d.ts +76 -0
  38. package/lib/session-bridge.js +147 -0
  39. package/lib/session-title.js +35 -0
  40. package/lib/storage.d.ts +9 -0
  41. package/lib/storage.js +65 -0
  42. package/lib/task-store.d.ts +729 -0
  43. package/lib/task-store.js +2205 -0
  44. package/lib/team-store.d.ts +216 -0
  45. package/lib/team-store.js +765 -0
  46. package/lib/tree-snapshot.d.ts +28 -0
  47. package/lib/tree-snapshot.js +80 -0
  48. package/lib/types/client/TeamView.d.ts +26 -0
  49. package/lib/types/client/TeamView.dom.test.d.ts +1 -0
  50. package/lib/types/client/api.d.ts +522 -0
  51. package/lib/types/client/api.test.d.ts +1 -0
  52. package/lib/types/client/attention.d.ts +65 -0
  53. package/lib/types/client/attention.test.d.ts +1 -0
  54. package/lib/types/client/index.d.ts +31 -0
  55. package/lib/types/client/locales.d.ts +577 -0
  56. package/lib/types/client/member-name.d.ts +14 -0
  57. package/lib/types/client/member-name.test.d.ts +1 -0
  58. package/lib/types/client/roster.d.ts +26 -0
  59. package/lib/types/client/roster.test.d.ts +1 -0
  60. package/lib/types/client/styles.d.ts +3 -0
  61. package/package.json +104 -0
  62. package/roles/builder.md +40 -0
  63. package/roles/delegate.md +36 -0
  64. package/roles/lead.md +46 -0
  65. package/roles/oracle.md +36 -0
  66. package/roles/researcher.md +37 -0
  67. package/roles/reviewer.md +45 -0
  68. package/roles/scout.md +36 -0
  69. package/roles/verifier.md +36 -0
@@ -0,0 +1,586 @@
1
+ import { MailboxService, MessageDelivery, MessageKind, TeamMessage } from "./mailbox.js";
2
+ import { MemberRecord, TeamState } from "./team-store.js";
3
+ import { ReadTaskResult, TaskStore, TeamTask } from "./task-store.js";
4
+ import { DispatchEvaluation } from "./dispatch.js";
5
+ import { SessionDescriptor } from "./session-bridge.js";
6
+ import { RoleRegistry } from "./roles.js";
7
+ //#region src/routes.d.ts
8
+ export declare const LIVE_TEAMS_MEMBERS_ROUTE = "/api/live-teams/members";
9
+ export declare const LIVE_TEAMS_CANDIDATES_ROUTE = "/api/live-teams/candidates";
10
+ export declare const LIVE_TEAMS_MEMBER_ROLE_ROUTE = "/api/live-teams/members/role";
11
+ export declare const LIVE_TEAMS_MEMBER_AVAILABILITY_ROUTE = "/api/live-teams/members/availability";
12
+ export declare const LIVE_TEAMS_MEMBER_NAME_ROUTE = "/api/live-teams/members/name";
13
+ export declare const LIVE_TEAMS_MEMBER_REMOVE_ROUTE = "/api/live-teams/members/remove";
14
+ export declare const LIVE_TEAMS_MEMBER_CREATE_SESSION_ROUTE = "/api/live-teams/members/create-session";
15
+ export declare const LIVE_TEAMS_MEMBER_INTERRUPT_ROUTE = "/api/live-teams/member/interrupt";
16
+ export declare const LIVE_TEAMS_ROLES_ROUTE = "/api/live-teams/roles";
17
+ export declare const LIVE_TEAMS_ROLES_OPEN_ROUTE = "/api/live-teams/roles/open";
18
+ export declare const LIVE_TEAMS_ROLES_OVERRIDE_ROUTE = "/api/live-teams/roles/override";
19
+ export declare const LIVE_TEAMS_TEAM_ROUTE = "/api/live-teams/team";
20
+ export declare const LIVE_TEAMS_TEAM_ENABLE_ROUTE = "/api/live-teams/team/enable";
21
+ export declare const LIVE_TEAMS_TEAM_DISABLE_ROUTE = "/api/live-teams/team/disable";
22
+ export declare const LIVE_TEAMS_TEAM_NAME_ROUTE = "/api/live-teams/team/name";
23
+ export declare const LIVE_TEAMS_TEAM_CANDIDATES_ROUTE = "/api/live-teams/team/candidates";
24
+ export declare const LIVE_TEAMS_TEAM_ADOPT_ROUTE = "/api/live-teams/team/adopt";
25
+ export declare const LIVE_TEAMS_MESSAGES_ROUTE = "/api/live-teams/messages";
26
+ export declare const LIVE_TEAMS_ATTENTION_ROUTE = "/api/live-teams/attention";
27
+ export declare const LIVE_TEAMS_MESSAGES_ACKNOWLEDGE_ROUTE = "/api/live-teams/messages/acknowledge";
28
+ export declare const LIVE_TEAMS_JOURNAL_ROUTE = "/api/live-teams/journal";
29
+ export declare const LIVE_TEAMS_TEAM_LEAD_ROUTE = "/api/live-teams/team/lead";
30
+ export declare const LIVE_TEAMS_TEAM_BRIEFING_OPEN_ROUTE = "/api/live-teams/team/briefing/open";
31
+ export declare const LIVE_TEAMS_MEMBER_GRANT_ROUTE = "/api/live-teams/member/grant";
32
+ export declare const LIVE_TEAMS_DISPATCH_ROUTE = "/api/live-teams/dispatch";
33
+ export declare const LIVE_TEAMS_TASKS_ROUTE = "/api/live-teams/tasks";
34
+ export declare const LIVE_TEAMS_TASK_ROUTE = "/api/live-teams/task";
35
+ export declare const LIVE_TEAMS_TASK_ACCEPT_ROUTE = "/api/live-teams/task/accept";
36
+ export declare const LIVE_TEAMS_TASK_REVISE_ROUTE = "/api/live-teams/task/revise";
37
+ /** The human's own control over work: create it, change its contract, release a held attempt. */
38
+ export declare const LIVE_TEAMS_TASK_CREATE_ROUTE = "/api/live-teams/task/create";
39
+ export declare const LIVE_TEAMS_TASK_UPDATE_ROUTE = "/api/live-teams/task/update";
40
+ export declare const LIVE_TEAMS_TASK_REVOKE_ROUTE = "/api/live-teams/task/revoke";
41
+ type HeaderValue = string | readonly string[] | undefined;
42
+ type RequestHeaders = Record<string, HeaderValue>;
43
+ type BodyValue = string | Uint8Array | AsyncIterable<Uint8Array | string>;
44
+ export type LiveTeamsRequest = {
45
+ readonly method?: string;
46
+ readonly headers: RequestHeaders;
47
+ readonly socket: {
48
+ readonly remoteAddress?: string;
49
+ };
50
+ readonly url?: string;
51
+ readonly body?: BodyValue;
52
+ };
53
+ export type LiveTeamsResponse = {
54
+ statusCode: number;
55
+ setHeader(name: string, value: string): void;
56
+ end(body?: string): void;
57
+ };
58
+ type ExactRoute = {
59
+ kind: 'exact';
60
+ path: string;
61
+ handler: (request: LiveTeamsRequest, response: LiveTeamsResponse) => void | Promise<void>;
62
+ };
63
+ export type LiveTeamsWebServer = {
64
+ readonly port: number;
65
+ register(route: ExactRoute): () => void;
66
+ };
67
+ export type LiveTeamsRouteContext = {
68
+ readonly webServer: LiveTeamsWebServer;
69
+ };
70
+ /** The registry is deliberately queried only for the bound Session's liveness. */
71
+ export type LiveAgentRegistry = {
72
+ get(sessionId: string): unknown;
73
+ };
74
+ export type LiveTeamsMutation = {
75
+ readonly kind: 'role' | 'availability' | 'name';
76
+ readonly memberId: string;
77
+ readonly value: string;
78
+ readonly expectedRevision: number;
79
+ };
80
+ export type LiveTeamsRemoveMemberRequest = {
81
+ readonly memberId: string;
82
+ readonly expectedRevision: number;
83
+ };
84
+ export type LiveTeamsRemoveMemberResult = {
85
+ readonly state: TeamState;
86
+ readonly removed: MemberRecord;
87
+ };
88
+ export type LiveTeamsTitleResult = LiveTeamsSessionTitleResult;
89
+ export type LiveTeamsMutationResult = {
90
+ readonly state: TeamState;
91
+ readonly member: MemberRecord;
92
+ readonly from: string;
93
+ readonly to: string;
94
+ readonly sessionTitle?: LiveTeamsTitleResult;
95
+ };
96
+ export type LiveTeamsCreateMemberRequest = {
97
+ readonly sessionId: string;
98
+ readonly displayName: string;
99
+ readonly role: string;
100
+ readonly memberId?: string;
101
+ readonly expectedRevision: number;
102
+ };
103
+ export type LiveTeamsSessionTitleResult = {
104
+ readonly from: string | null;
105
+ readonly to: string;
106
+ readonly reason: 'member-added' | 'member-renamed';
107
+ readonly renamed: boolean;
108
+ };
109
+ export type LiveTeamsCreateMemberResult = {
110
+ readonly state: TeamState;
111
+ readonly member: MemberRecord;
112
+ readonly sessionTitle?: LiveTeamsSessionTitleResult;
113
+ };
114
+ export type LiveTeamsCreateSessionMemberRequest = {
115
+ readonly sessionId: string;
116
+ readonly displayName: string;
117
+ readonly role: string;
118
+ readonly expectedRevision: number;
119
+ };
120
+ export type LiveTeamsCreateSessionMemberResult = {
121
+ readonly state: TeamState;
122
+ readonly member: MemberRecord;
123
+ readonly sessionTitle?: LiveTeamsSessionTitleResult;
124
+ };
125
+ export type LiveTeamsCandidate = {
126
+ readonly sessionId: string;
127
+ readonly title: string | null;
128
+ readonly cwd: string;
129
+ readonly live: boolean;
130
+ readonly status: 'idle' | 'running' | 'unknown';
131
+ };
132
+ export type LiveTeamsJournalCategory = 'message' | 'delivery' | 'task' | 'review' | 'contact' | 'team' | 'member' | 'other';
133
+ export type JournalCategory = LiveTeamsJournalCategory;
134
+ export type JournalRefs = {
135
+ readonly taskId?: string;
136
+ readonly taskTitle?: string;
137
+ readonly messageId?: string;
138
+ readonly round?: number;
139
+ };
140
+ export type LiveTeamsJournalEntry = {
141
+ readonly id: string;
142
+ readonly kind: string;
143
+ readonly category: LiveTeamsJournalCategory;
144
+ readonly at: number;
145
+ readonly actor?: string;
146
+ readonly actorName?: string;
147
+ readonly summary: string;
148
+ readonly refs: JournalRefs;
149
+ readonly detail?: string;
150
+ /**
151
+ * Everything the row knows, resolved for a person: who, to whom, which task, which attempt.
152
+ * The summary is one line; this is what the row unfolds into.
153
+ */
154
+ fields?: {
155
+ label: string;
156
+ value: string;
157
+ }[];
158
+ /**
159
+ * The summary split into plain and emphasised runs. The server knows exactly which words are
160
+ * people's names, so the panel does not have to guess them back out of a sentence.
161
+ */
162
+ summaryParts?: {
163
+ text: string;
164
+ strong?: boolean;
165
+ }[];
166
+ };
167
+ export type JournalEntry = LiveTeamsJournalEntry;
168
+ export type LiveTeamsJournal = {
169
+ readonly teamId: string;
170
+ readonly entries: LiveTeamsJournalEntry[];
171
+ };
172
+ export type LiveTeamsAuditRecord = {
173
+ readonly kind: 'briefing/updated';
174
+ readonly actor: string;
175
+ readonly length: number;
176
+ readonly at: number;
177
+ } | {
178
+ readonly kind: 'member/note';
179
+ readonly memberId: string;
180
+ readonly note: string;
181
+ readonly actor: string;
182
+ readonly at: number;
183
+ } | {
184
+ readonly kind: 'team/lead';
185
+ readonly memberId: string;
186
+ readonly actor: MutationActor;
187
+ readonly at: number;
188
+ } | {
189
+ readonly kind: 'member/interrupted';
190
+ readonly memberId: string;
191
+ readonly sessionId: string;
192
+ readonly actor: MutationActor;
193
+ readonly at: number;
194
+ } | {
195
+ /**
196
+ * The host refused to interrupt — the member was idle, unbound, or the interrupt failed.
197
+ * Recorded because an operator action that changes nothing is still an operator action,
198
+ * and without this the panel's "nothing happened" is indistinguishable from a lost click
199
+ * (F18 of the third live run).
200
+ */
201
+ readonly kind: 'member/interrupt-refused';
202
+ readonly memberId: string;
203
+ readonly sessionId?: string;
204
+ readonly reason: string;
205
+ readonly actor: MutationActor;
206
+ readonly at: number;
207
+ } | {
208
+ readonly kind: 'team/adopted';
209
+ readonly teamId: string;
210
+ readonly fromTeamId: string;
211
+ readonly name: string;
212
+ readonly memberCount: number;
213
+ readonly at: number;
214
+ readonly actor: MutationActor;
215
+ } | {
216
+ readonly kind: 'member/role-changed' | 'member/availability-changed' | 'member/name-changed';
217
+ readonly memberId: string;
218
+ readonly from: string;
219
+ readonly to: string;
220
+ readonly at: number;
221
+ readonly actor: MutationActor;
222
+ readonly expectedRevision: number;
223
+ readonly appliedRevision: number;
224
+ } | {
225
+ readonly kind: 'member/added';
226
+ readonly memberId: string;
227
+ readonly sessionId: string;
228
+ readonly displayName: string;
229
+ readonly role: string;
230
+ readonly at: number;
231
+ readonly actor: MutationActor;
232
+ readonly expectedRevision: number;
233
+ readonly appliedRevision: number;
234
+ } | {
235
+ readonly kind: 'member/removed';
236
+ readonly memberId: string;
237
+ readonly displayName: string;
238
+ readonly role: string;
239
+ readonly sessionId: string | null;
240
+ readonly at: number;
241
+ readonly actor: MutationActor;
242
+ readonly expectedRevision: number;
243
+ readonly appliedRevision: number;
244
+ } | {
245
+ readonly kind: 'session/titled';
246
+ readonly sessionId: string;
247
+ readonly from: string | null;
248
+ readonly to: string;
249
+ readonly reason: 'member-added' | 'member-renamed';
250
+ readonly at: number;
251
+ readonly actor: MutationActor;
252
+ } | {
253
+ readonly kind: 'team/enabled' | 'team/disabled';
254
+ readonly teamId: string;
255
+ readonly name?: string;
256
+ readonly at: number;
257
+ readonly actor: MutationActor;
258
+ } | {
259
+ readonly kind: 'team/renamed';
260
+ readonly teamId: string;
261
+ readonly from: string;
262
+ readonly to: string;
263
+ readonly at: number;
264
+ readonly actor: MutationActor;
265
+ };
266
+ export type LiveTeamsRoleSummary = {
267
+ readonly id: string;
268
+ readonly name: string;
269
+ readonly description?: string;
270
+ readonly aliases: string[];
271
+ readonly source: 'global' | 'workspace';
272
+ readonly overridden: boolean;
273
+ };
274
+ export type LiveTeamsRoles = {
275
+ readonly roles: LiveTeamsRoleSummary[];
276
+ readonly globalDirectory: string;
277
+ readonly workspaceDirectory: string;
278
+ readonly workspaceDirectoryExists: boolean;
279
+ readonly openAvailable: boolean;
280
+ readonly globalOpenAvailable: boolean;
281
+ };
282
+ export type LiveTeamsRoleOpenRequest = {
283
+ readonly scope: 'global' | 'workspace';
284
+ readonly id?: string;
285
+ };
286
+ export type LiveTeamsTeamStatus = {
287
+ readonly enabled: boolean;
288
+ /** What the plugin found when it asked the host for the surfaces it calls. */
289
+ readonly compatibility?: {
290
+ readonly verdict: 'compatible' | 'degraded' | 'incompatible';
291
+ readonly hostVersion?: string;
292
+ readonly missing: readonly string[];
293
+ readonly absent: readonly string[];
294
+ readonly detail: string;
295
+ };
296
+ readonly briefing?: string;
297
+ readonly openAvailable?: boolean;
298
+ readonly teamId: string;
299
+ readonly teamName: string;
300
+ readonly workspacePath: string;
301
+ readonly revision?: number;
302
+ readonly memberCount: number;
303
+ };
304
+ export type LiveTeamsAdoptionCandidate = {
305
+ readonly teamId: string;
306
+ readonly name?: string;
307
+ readonly memberCount?: number;
308
+ readonly updatedAt?: number;
309
+ readonly usable?: boolean;
310
+ readonly reason?: string;
311
+ };
312
+ export type LiveTeamsTeamCandidates = {
313
+ readonly teamId: string;
314
+ readonly workspacePath: string;
315
+ readonly candidates: readonly LiveTeamsAdoptionCandidate[];
316
+ };
317
+ export type LiveTeamsTaskRouteSummary = {
318
+ readonly type: 'report';
319
+ readonly to: string | 'human';
320
+ readonly toName?: string;
321
+ } | {
322
+ readonly type: 'review';
323
+ readonly reviewer: string;
324
+ readonly reviewerName?: string;
325
+ } | {
326
+ readonly type: 'interactive';
327
+ readonly then: LiveTeamsTaskRouteSummary;
328
+ } | {
329
+ readonly type: 'custom';
330
+ readonly description: string;
331
+ };
332
+ export type LiveTeamsTaskSummary = {
333
+ readonly id: string;
334
+ readonly title: string;
335
+ readonly status: TeamTask['status'];
336
+ readonly assignee?: string;
337
+ readonly assigneeName?: string;
338
+ readonly route?: LiveTeamsTaskRouteSummary;
339
+ readonly round?: number;
340
+ readonly roundLimit?: number;
341
+ readonly updatedAt: number;
342
+ readonly archivedPath?: string;
343
+ readonly documentMissing?: true;
344
+ readonly dependsOn?: readonly string[];
345
+ /**
346
+ * Dependencies that are not `completed`, in contract order. A client must be
347
+ * able to tell a claimable task from a blocked one without joining two lists,
348
+ * and a dependency that died is included on purpose: it blocks forever.
349
+ */
350
+ readonly blockedBy?: readonly LiveTeamsTaskBlocker[];
351
+ };
352
+ export type LiveTeamsTaskBlocker = {
353
+ readonly id: string;
354
+ readonly title: string;
355
+ readonly status: TeamTask['status'];
356
+ };
357
+ /**
358
+ * The dispatch projection: for every task, the decision the reconciler would take
359
+ * and the reason for it, in the same words the journal uses. One source answers
360
+ * both the lead's question ("why is this not running?") and the panel's chip.
361
+ */
362
+ export type LiveTeamsDispatch = {
363
+ readonly teamId: string;
364
+ readonly paused: boolean;
365
+ /** `manual` means coding work is never started automatically (design §22, Gate C). */
366
+ readonly coding: 'manual' | 'automatic';
367
+ readonly pausedBy?: string;
368
+ readonly pausedReason?: string;
369
+ readonly counts: Record<'assign' | 'active' | 'wait' | 'refused', number>;
370
+ readonly tasks: readonly DispatchEvaluation[];
371
+ };
372
+ export type LiveTeamsTasks = {
373
+ readonly teamId: string;
374
+ readonly tasks: LiveTeamsTaskSummary[];
375
+ };
376
+ export type LiveTeamsTaskDetail = ReadTaskResult & {
377
+ readonly documentMissing?: true;
378
+ };
379
+ export type LiveTeamsRouteOptions = {
380
+ /** Resolve a request to the workspace/team context. Undefined means invalid request. */
381
+ readonly resolveRequest?: (request: LiveTeamsRequest, sessionId?: string) => Promise<LiveTeamsRouteOptions | undefined>;
382
+ /** Read lifecycle state without creating a team. */
383
+ readonly teamStatus?: () => Promise<LiveTeamsTeamStatus>;
384
+ readonly enableTeam?: (name?: string, createNew?: boolean) => Promise<LiveTeamsTeamStatus>;
385
+ readonly disableTeam?: () => Promise<LiveTeamsTeamStatus>;
386
+ readonly renameTeam?: (name: string, expectedRevision: number) => Promise<{
387
+ status: LiveTeamsTeamStatus;
388
+ from: string;
389
+ }>;
390
+ readonly requestSessionId?: string;
391
+ readonly teamEnabled?: () => Promise<boolean>;
392
+ readonly teamId: string;
393
+ /** Human-readable team name shown in the interface; falls back to the id. */
394
+ readonly teamName?: string;
395
+ readonly workspacePath: string;
396
+ readonly agents?: LiveAgentRegistry;
397
+ /** Read the current state without creating or mutating it. Runtime shape is validated here. */
398
+ readonly readState?: () => Promise<unknown>;
399
+ /** Apply one compare-and-set mutation. A REVISION_CONFLICT result is retried once. */
400
+ readonly applyMutation?: (request: LiveTeamsMutation) => Promise<LiveTeamsMutationResult>;
401
+ /** Discover cold-readable Sessions; the route applies workspace and ownership filtering. */
402
+ readonly listCandidates?: () => Promise<readonly SessionDescriptor[]>;
403
+ /** Create one member after the provider has performed the workspace guard. */
404
+ readonly createMember?: (request: LiveTeamsCreateMemberRequest) => Promise<LiveTeamsCreateMemberResult>;
405
+ /** Create and bind a new Session-backed member in the team's workspace. */
406
+ readonly createSessionMember?: (request: LiveTeamsCreateSessionMemberRequest) => Promise<LiveTeamsCreateSessionMemberResult>;
407
+ /** Resolve whether the team's canonical workspace is registered in DSH. */
408
+ readonly workspaceRegistered?: () => boolean | Promise<boolean>;
409
+ /** Remove one member record without touching its Session. */
410
+ readonly removeMember?: (request: LiveTeamsRemoveMemberRequest) => Promise<LiveTeamsRemoveMemberResult>;
411
+ /** Append the audit record before a successful mutation is reported to the caller. */
412
+ readonly appendAudit?: (event: LiveTeamsAuditRecord) => Promise<void>;
413
+ /** Read the existing append-only audit; this route never writes to it. */
414
+ readonly readEvents?: (after?: number) => Promise<{
415
+ events: readonly unknown[];
416
+ torn: string | null;
417
+ nextOffset?: number;
418
+ truncated?: boolean;
419
+ }>;
420
+ /** Cancel one currently-running member turn through the existing Session bridge. */
421
+ readonly interrupt?: (sessionId: string, reason: string) => Promise<{
422
+ accepted: true;
423
+ }>;
424
+ /** Discover legacy team directories for adoption. */
425
+ readonly listTeamCandidates?: () => Promise<readonly LiveTeamsAdoptionCandidate[]>;
426
+ /** Adopt a legacy team into this workspace's derived id. */
427
+ readonly adoptTeam?: (teamId: string, replaceEmptyDerived: boolean) => Promise<{
428
+ status: LiveTeamsTeamStatus;
429
+ adoptedFrom: string;
430
+ }>;
431
+ /** Apply a Session title after membership state commits. */
432
+ readonly titleSession?: (request: {
433
+ sessionId: string;
434
+ displayName: string;
435
+ reason: 'member-added' | 'member-renamed';
436
+ previousTitle?: string;
437
+ }) => Promise<LiveTeamsSessionTitleResult | undefined>;
438
+ /** Report best-effort title/audit failures without changing membership success. */
439
+ readonly logger?: {
440
+ warn?: (message: string) => void;
441
+ };
442
+ readonly roles?: RoleRegistry;
443
+ readonly readRoles?: () => Promise<RoleRegistry>;
444
+ readonly roleRegistry?: () => Promise<RoleRegistry>;
445
+ readonly canOpenWorkspacePath?: () => boolean | Promise<boolean>;
446
+ readonly openWorkspacePath?: (request: {
447
+ path: string;
448
+ action: 'reveal' | 'open';
449
+ }, signal: AbortSignal) => Promise<unknown>;
450
+ readonly copyRoleOverride?: (id: string) => Promise<{
451
+ id: string;
452
+ path: string;
453
+ }>;
454
+ /** Human-facing mailbox for the bound team. */
455
+ readonly mailbox?: MailboxService;
456
+ /** The host-compatibility verdict, so the panel can say why the team is off. */
457
+ readonly compatibility?: () => LiveTeamsTeamStatus['compatibility'];
458
+ /** Durable B19-B22 task store for the human task surface. */
459
+ readonly taskStore?: TaskStore;
460
+ readonly setLead?: (memberId: string, expectedRevision: number) => Promise<{
461
+ state: TeamState;
462
+ leadMemberId: string;
463
+ }>;
464
+ readonly setContactGrant?: (from: string, to: string, action: 'add' | 'remove', actor: string) => Promise<{
465
+ grants: {
466
+ from: string;
467
+ to: string;
468
+ grantedBy: string;
469
+ at: number;
470
+ }[];
471
+ }>;
472
+ /** Read the dispatch projection; never mutates and never creates a team. */
473
+ readonly readDispatch?: () => Promise<LiveTeamsDispatch | undefined>;
474
+ /** Flip the dispatch switch under the actor's own authority; the store refuses a no-op. */
475
+ readonly setDispatch?: (request: {
476
+ mode: 'auto' | 'paused';
477
+ reason?: string;
478
+ actor: string;
479
+ }) => Promise<{
480
+ from: string;
481
+ to: string;
482
+ }>;
483
+ };
484
+ export type LiveTeamsSnapshotMember = {
485
+ memberId: string;
486
+ permittedContacts: readonly string[];
487
+ displayName: string;
488
+ role: string;
489
+ availability: string;
490
+ sessionId: string | null;
491
+ live: boolean;
492
+ status: 'idle' | 'running' | 'unknown';
493
+ bindingGeneration: number;
494
+ contractRevision: number;
495
+ };
496
+ export type LiveTeamsSnapshot = {
497
+ teamId: string;
498
+ leadMemberId?: string;
499
+ /** Human-readable team name when one is configured; the id otherwise. */
500
+ teamName: string;
501
+ workspacePath: string;
502
+ revision: number;
503
+ members: LiveTeamsSnapshotMember[];
504
+ };
505
+ export type LiveTeamsCandidates = {
506
+ teamId: string;
507
+ /**
508
+ * The name the Sessions above were titled with, so a client can undo the
509
+ * `<member> - <team>` suffix without asking a second endpoint. The two
510
+ * endpoints that report a team name must never disagree: `/team` answers from
511
+ * the persisted state, so this one does too.
512
+ */
513
+ teamName: string;
514
+ workspacePath: string;
515
+ revision: number;
516
+ candidates: LiveTeamsCandidate[];
517
+ };
518
+ /**
519
+ * Who is waiting for the human, in one entry per member.
520
+ *
521
+ * This exists because the inbox was the wrong shape for the question the human
522
+ * actually asks — "does anyone need me?" — and the answer must be reachable from
523
+ * any Session, not only from the Team tab.
524
+ */
525
+ export type LiveTeamsAttentionEntry = {
526
+ memberId: string;
527
+ name: string;
528
+ /** Session to open on click; `null` when the member lost its binding. */
529
+ sessionId: string | null;
530
+ /** Unread human-addressed deliveries from this member. */
531
+ waiting: number;
532
+ /** Arrival of the oldest unread one: how long they have been waiting. */
533
+ since: number;
534
+ /** Kind of the newest unread message, so the chip can say what it is. */
535
+ kind: MessageKind;
536
+ };
537
+ export type LiveTeamsAttention = {
538
+ teamId: string;
539
+ attention: LiveTeamsAttentionEntry[];
540
+ };
541
+ export type LiveTeamsRequestContext = {
542
+ readonly remoteAddress: string | undefined;
543
+ readonly host: string | undefined;
544
+ readonly secFetchSite?: string;
545
+ readonly expectedPort: number;
546
+ };
547
+ /**
548
+ * Who a mutation is attributed to: a request that carried browser evidence, or one that did not.
549
+ * Both exist and the journal says which — see `actorOf`.
550
+ */
551
+ export type MutationActor = 'human:desktop' | 'local:unverified';
552
+ /** Mirrors the shipped community-market read authority fence. */
553
+ export declare function liveTeamsRequestAllowed(context: LiveTeamsRequestContext): boolean;
554
+ /**
555
+ * Who a mutation is attributed to, decided by what the request actually proves.
556
+ *
557
+ * The fence (`liveTeamsRequestAllowed`) keeps the network and a cross-site page out, but it cannot
558
+ * tell a browser from `curl` on the same machine: any local process can send the same headers, and a
559
+ * member is a full Session with a shell. Pretending otherwise put "the human accepted this" into the
560
+ * journal for work a process did.
561
+ *
562
+ * So the audit says which of the two it saw. `human:desktop` means the request carried the evidence
563
+ * only a same-origin page sends — `Sec-Fetch-Site: same-origin` and an `Origin` whose host is the
564
+ * `Host` it was sent to. Everything else is `local:unverified`: it may well be the human's own
565
+ * script, and the journal does not claim to know. This is attribution, not authentication; making it
566
+ * authentication needs a credential the panel holds and a local process cannot read (see
567
+ * `docs/implementation/open-questions-to-dsh.md`).
568
+ */
569
+ export declare function actorOf(context: {
570
+ host: string | undefined;
571
+ origin: string | undefined;
572
+ secFetchSite: string | undefined;
573
+ }): 'human:desktop' | 'local:unverified';
574
+ /**
575
+ * One entry per member who has something the human has not read yet.
576
+ *
577
+ * Only deliveries the human has not acknowledged count: an acknowledged delivery
578
+ * is the record that the human saw it, and that is the only thing that makes the
579
+ * chip stop asking. `since` is the *oldest* unread arrival, because "waiting
580
+ * since 11:33" is the number that matters, not the newest.
581
+ */
582
+ export declare function projectLiveTeamsAttention(state: TeamState, messages: readonly TeamMessage[], deliveries: readonly MessageDelivery[]): LiveTeamsAttention;
583
+ export declare function projectLiveTeamsSnapshot(state: TeamState, options: Pick<LiveTeamsRouteOptions, 'teamId' | 'teamName' | 'workspacePath' | 'agents'>): LiveTeamsSnapshot;
584
+ export declare function projectLiveTeamsCandidates(state: TeamState, descriptors: readonly SessionDescriptor[], options: Pick<LiveTeamsRouteOptions, 'teamId' | 'teamName' | 'workspacePath'>): LiveTeamsCandidates;
585
+ export declare function registerLiveTeamsRoutes(ctx: LiveTeamsRouteContext, options: LiveTeamsRouteOptions): () => void;
586
+ //#endregion