@khorsheed/dsh-room 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.en.md +29 -62
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +28 -61
  5. package/lib/client.js +14620 -5705
  6. package/lib/index.js +4069 -730
  7. package/lib/journal-B4FOGO6S.js +482 -0
  8. package/lib/journal-Rid9AAnD.js +485 -0
  9. package/lib/tool.js +112 -166
  10. package/lib/typert.host.js +2031 -374
  11. package/lib/typert.remote-client.d.ts +34 -10
  12. package/lib/typert.remote-client.js +1599 -210
  13. package/lib/types/agent-setup.d.ts +1 -1
  14. package/lib/types/client/InviteAgentAction.js +4 -16
  15. package/lib/types/client/InviteDialog.d.ts +28 -6
  16. package/lib/types/client/InviteDialog.js +76 -12
  17. package/lib/types/client/MemberCard.d.ts +5 -1
  18. package/lib/types/client/MemberCard.js +4 -4
  19. package/lib/types/client/MembersView.d.ts +1 -1
  20. package/lib/types/client/MembersView.js +58 -22
  21. package/lib/types/client/RemoveMemberConfirm.d.ts +40 -0
  22. package/lib/types/client/RemoveMemberConfirm.js +85 -0
  23. package/lib/types/client/RoomApprovalView.d.ts +11 -0
  24. package/lib/types/client/RoomApprovalView.js +28 -0
  25. package/lib/types/client/RoomComposer.d.ts +1 -1
  26. package/lib/types/client/RoomComposer.js +195 -29
  27. package/lib/types/client/RoomDecisionView.d.ts +22 -0
  28. package/lib/types/client/RoomDecisionView.js +35 -0
  29. package/lib/types/client/RoomHelpView.d.ts +17 -0
  30. package/lib/types/client/RoomHelpView.js +56 -0
  31. package/lib/types/client/RoomInboxView.d.ts +10 -0
  32. package/lib/types/client/RoomInboxView.js +14 -0
  33. package/lib/types/client/RoomQuestionView.d.ts +13 -0
  34. package/lib/types/client/RoomQuestionView.js +61 -0
  35. package/lib/types/client/RoomRelayView.js +1 -1
  36. package/lib/types/client/RoomRunView.d.ts +3 -1
  37. package/lib/types/client/RoomRunView.js +12 -5
  38. package/lib/types/client/RoomSettingsView.d.ts +19 -0
  39. package/lib/types/client/RoomSettingsView.js +30 -0
  40. package/lib/types/client/RoomSpeechView.d.ts +22 -8
  41. package/lib/types/client/RoomSpeechView.js +94 -11
  42. package/lib/types/client/RoomStatsLine.d.ts +26 -5
  43. package/lib/types/client/RoomStatsLine.js +53 -7
  44. package/lib/types/client/RoomTaskCapsule.d.ts +19 -0
  45. package/lib/types/client/RoomTaskCapsule.js +37 -0
  46. package/lib/types/client/TasksTab.d.ts +41 -0
  47. package/lib/types/client/TasksTab.js +43 -0
  48. package/lib/types/client/TasksView.d.ts +44 -0
  49. package/lib/types/client/TasksView.js +149 -0
  50. package/lib/types/client/TeamView.d.ts +33 -0
  51. package/lib/types/client/TeamView.js +210 -0
  52. package/lib/types/client/at-menu.d.ts +127 -0
  53. package/lib/types/client/at-menu.js +173 -0
  54. package/lib/types/client/draft-chips.d.ts +67 -0
  55. package/lib/types/client/draft-chips.js +95 -0
  56. package/lib/types/client/executions.d.ts +0 -30
  57. package/lib/types/client/executions.js +4 -34
  58. package/lib/types/client/icons-local.d.ts +25 -0
  59. package/lib/types/client/icons-local.js +10 -0
  60. package/lib/types/client/icons.d.ts +2 -1
  61. package/lib/types/client/icons.js +1 -0
  62. package/lib/types/client/index.js +276 -72
  63. package/lib/types/client/locales.d.ts +482 -52
  64. package/lib/types/client/locales.js +490 -60
  65. package/lib/types/client/nodes.d.ts +112 -17
  66. package/lib/types/client/nodes.js +197 -26
  67. package/lib/types/client/slots.d.ts +117 -47
  68. package/lib/types/client/task-tag.d.ts +32 -0
  69. package/lib/types/client/task-tag.js +33 -0
  70. package/lib/types/client/tasks.d.ts +50 -0
  71. package/lib/types/client/tasks.js +82 -0
  72. package/lib/types/client/team.d.ts +73 -0
  73. package/lib/types/client/team.js +187 -0
  74. package/lib/types/decisions.d.ts +49 -0
  75. package/lib/types/decisions.js +101 -0
  76. package/lib/types/deliveries.d.ts +17 -1
  77. package/lib/types/deliveries.js +82 -6
  78. package/lib/types/dispatch.d.ts +102 -4
  79. package/lib/types/dispatch.js +498 -109
  80. package/lib/types/help.d.ts +137 -0
  81. package/lib/types/help.js +216 -0
  82. package/lib/types/helps.d.ts +26 -0
  83. package/lib/types/helps.js +47 -0
  84. package/lib/types/index.d.ts +292 -52
  85. package/lib/types/index.js +1421 -199
  86. package/lib/types/jobs.d.ts +22 -0
  87. package/lib/types/jobs.js +77 -0
  88. package/lib/types/journal.d.ts +17 -27
  89. package/lib/types/journal.js +120 -43
  90. package/lib/types/messaging.d.ts +65 -0
  91. package/lib/types/messaging.js +160 -0
  92. package/lib/types/plan-service.js +7 -1
  93. package/lib/types/report-gate.d.ts +81 -0
  94. package/lib/types/report-gate.js +89 -0
  95. package/lib/types/tool-definition.d.ts +43 -20
  96. package/lib/types/tool-definition.js +145 -212
  97. package/lib/types/tool.d.ts +2 -2
  98. package/lib/types/tool.js +3 -2
  99. package/lib/types/types.d.ts +755 -78
  100. package/locale/zh.json +1 -1
  101. package/package.json +3 -3
  102. package/lib/types/client/RoomActivityDock.d.ts +0 -19
  103. package/lib/types/client/RoomActivityDock.js +0 -105
  104. package/lib/types/client/RoomDockCapsules.d.ts +0 -64
  105. package/lib/types/client/RoomDockCapsules.js +0 -277
  106. package/lib/types/client/RoomPlanTab.d.ts +0 -15
  107. package/lib/types/client/RoomPlanTab.js +0 -13
  108. package/lib/types/client/RoomTaskLineView.d.ts +0 -13
  109. package/lib/types/client/RoomTaskLineView.js +0 -21
@@ -6,7 +6,7 @@ import type { LocalAgentUi } from '@khorsheed/dsh-local-agent/client';
6
6
  import type { ReactNode } from 'react';
7
7
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
8
8
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
9
- import type { RoomProviderList } from '../types.ts';
9
+ import type { RoomProviderList, RoomQuestionAnswer, RoomRemoval, RoomSettings } from '../types.ts';
10
10
  import type { LocalAgentModelInfo, LocalAgentPromptResult } from '@khorsheed/dsh-local-agent/types';
11
11
  import type { RoomChromeVisibility } from './preset-visibility.ts';
12
12
  import type { RoomStore } from './room-store.ts';
@@ -17,23 +17,23 @@ export type RoomMutationOutcome = {
17
17
  readonly ok: false;
18
18
  readonly message: string;
19
19
  };
20
+ /** A removal's outcome: what it did (for the undo line), or the failure copy. */
21
+ export type RoomRemovalOutcome = {
22
+ readonly ok: true;
23
+ readonly value: RoomRemoval;
24
+ } | {
25
+ readonly ok: false;
26
+ readonly message: string;
27
+ };
20
28
  /** Injected action face of the session-header 邀请 agent action. */
21
29
  export type InviteAgentInjected = RoomInviteInjected;
22
30
  /** Full props of the 'conversation.session.header.actions' entry. */
23
31
  export type InviteAgentActionProps = PropsRuntime<'conversation.session.header.actions'> & InjectFace<InviteAgentInjected> & PropsLocale<'room'>;
24
- /** Injected face of the dock capsules (goal capsule + task capsule). */
25
- export interface RoomTasksInjected {
26
- /** The client-side room state store (tasks and the goal ride the replayed state). */
32
+ /** The room goal action (the task page's goal line). */
33
+ export interface RoomGoalInjected {
34
+ /** The room store the views read. */
27
35
  readonly roomStore: RoomStore;
28
- /** Add a pending task to a member's lane, optionally waiting on another member. */
29
- readonly addTask: (member: string, title: string, blockedBy?: string) => Promise<RoomMutationOutcome>;
30
- /**
31
- * Close an open task. The status is the closing state: 'done' by default;
32
- * the failed row's [关闭] passes 'cancelled' — the failure already speaks
33
- * for itself, and a failed task must not count as goal progress.
34
- */
35
- readonly closeTask: (taskId: string, status?: 'done' | 'cancelled') => Promise<RoomMutationOutcome>;
36
- /** Set (or, with a blank text, clear) the room's goal. */
36
+ /** Set (or, with a blank text, clear) the room goal. */
37
37
  readonly setGoal: (text: string) => Promise<RoomMutationOutcome>;
38
38
  }
39
39
  /**
@@ -48,8 +48,8 @@ export interface RoomInviteInjected {
48
48
  readonly roomCwd?: string | undefined;
49
49
  /** Invite a CLI member. */
50
50
  readonly invite: (values: RoomInviteValues) => Promise<RoomInviteOutcome>;
51
- /** List the invitable providers (undefined on transport failure). */
52
- readonly listProviders: () => Promise<RoomProviderList | undefined>;
51
+ /** List the invitable providers (undefined on transport failure); with a session, its mode rides along (`sessionPreset`). */
52
+ readonly listProviders: (sessionId?: SessionId) => Promise<RoomProviderList | undefined>;
53
53
  /**
54
54
  * Pick a member-level working directory through the official picker call
55
55
  * (`uiWorkspace.pickDirectory` on host 0.1.2 — the picker UI itself is not
@@ -149,28 +149,48 @@ export interface RoomModelDirectory {
149
149
  }
150
150
  /**
151
151
  * Injected face of the composer takeover entry: the dispatch submit plus the
152
- * task-board actions (sessionId binds at inject time), because the takeover
153
- * renders the task board itself — the `conversation.input.dock` seat rides
154
- * the hidden official fallback. The takeover also inherits the official bar's
152
+ * task capsule's entry to the task page and the composer's task tag
153
+ * (sessionId binds at inject time), because the takeover renders the dock
154
+ * itself — the `conversation.input.dock` seat rides the hidden official
155
+ * fallback. The takeover also inherits the official bar's
155
156
  * Stop duty: the fallback's Stop button hides with it, so `stop` re-homes the
156
157
  * main agent's turn cancel (the runtime session face's `cancel()`). The
157
- * invite share rides along for the fresh-room dock's invite capsule.
158
+ * invite share rides along for the fresh-room invite capsule.
158
159
  * `modelDirectory` is the official per-session model directory for the room's
159
160
  * own main agent (bare messages ARE its turns); undefined on a host without
160
161
  * ui-model-selection — the picker simply does not render.
161
162
  */
162
- export interface RoomComposerInjected extends RoomTasksInjected, RoomInviteInjected {
163
- readonly backgroundSessions?: import('./executions.ts').BackgroundSessionsStore | undefined;
164
- readonly activeChildren?: (() => Promise<readonly string[] | undefined>) | undefined;
165
- readonly stopChild?: ((childSessionId: string) => Promise<RoomMutationOutcome>) | undefined;
166
- readonly openPlan?: (() => void) | undefined;
167
- readonly stopExecution?: ((name: string, run: import('../types.ts').RoomMemberRun) => Promise<RoomMutationOutcome>) | undefined;
163
+ export interface RoomComposerInjected extends RoomGoalInjected, RoomInviteInjected {
164
+ /** Open the task page (at one task); absent without the right sidebar. */
165
+ readonly openTasks?: ((task?: string) => void) | undefined;
166
+ /** The composer's task tag store (see task-tag.ts). */
167
+ readonly taskTags?: import('./task-tag.ts').RoomTaskTags | undefined;
168
168
  readonly reconcileDelivery?: ((deliveryId: string, outcome: 'done' | 'cancelled', evidence: string) => Promise<RoomMutationOutcome>) | undefined;
169
- readonly planCommand?: ((command: string) => Promise<RoomMutationOutcome>) | undefined;
170
169
  readonly openPlanSession?: ((sessionId: string) => void) | undefined;
171
170
  readonly renderMemberInbox?: LocalAgentUi['renderMemberInbox'] | undefined;
172
171
  readonly renderMemberConfiguration?: LocalAgentUi['renderMemberConfiguration'] | undefined;
173
172
  readonly stopMember?: ((name: string, expectedRunId?: string) => void) | undefined;
173
+ /**
174
+ * Preview one draft chip through its owning trigger source (the official
175
+ * editor's chip-click gesture; a quote chip opens its full-text card).
176
+ * @returns whether a source accepted. Absent without the input-trigger service.
177
+ */
178
+ readonly openReference?: ((source: string, ref: string) => boolean) | undefined;
179
+ /**
180
+ * Other sessions for the @ menu: the host's session-reference candidates
181
+ * (the recent list merged with the query's own lookup), rooms flagged.
182
+ * Absent without the session-reference service.
183
+ */
184
+ readonly listSessions?: ((query: string, signal: AbortSignal) => Promise<readonly import('./at-menu.ts').SessionCandidate[]>) | undefined;
185
+ /**
186
+ * Insert the host's session chip for a picked session over a detect-coordinate span.
187
+ * @returns whether the input machine took it (false: the composer falls back to text).
188
+ */
189
+ readonly insertSessionReference?: ((candidate: import('./at-menu.ts').SessionCandidate, span: {
190
+ readonly start: number;
191
+ readonly end: number;
192
+ readonly draftRev: number;
193
+ }) => boolean) | undefined;
174
194
  /**
175
195
  * Dispatch a message into the room and refresh the store on success. EVERY
176
196
  * send routes here, bare text included (the host routes bare text to the
@@ -180,8 +200,10 @@ export interface RoomComposerInjected extends RoomTasksInjected, RoomInviteInjec
180
200
  * the dispatch engine runs in parallel). `targets` carries the
181
201
  * mention-menu picks (explicit addressing wherever the `@name` sits in
182
202
  * the sentence); the host unions them with the parsed leading tokens.
203
+ * `task` is the composer's tag (`new` or a task root id): it files the
204
+ * message under that task instead of letting dsh decide.
183
205
  */
184
- readonly submit: (sessionId: SessionId, text: string, targets?: readonly string[]) => Promise<RoomMutationOutcome>;
206
+ readonly submit: (sessionId: SessionId, text: string, targets?: readonly string[], task?: string) => Promise<RoomMutationOutcome>;
185
207
  /** Interrupt the room's own main-agent turn (the hidden official bar's Stop). */
186
208
  readonly stop: () => void;
187
209
  /**
@@ -206,12 +228,20 @@ export type MembersViewProps = PropsRuntime<'conversation.view'> & InjectFace<Ro
206
228
  export interface RoomInviteValues {
207
229
  readonly provider: string;
208
230
  readonly name: string;
231
+ /** Short public role (omitted = none). */
232
+ readonly role?: string;
233
+ /** `native`: a DSH child agent in this host (omitted = a CLI member). */
234
+ readonly kind?: 'cli' | 'native';
235
+ /** Native only: the host agent preset (omitted = the host default). */
236
+ readonly preset?: string;
209
237
  readonly instructions?: string;
210
238
  /** Member-level working directory (omitted = inherits the room session's cwd). */
211
239
  readonly cwd?: string;
212
240
  readonly firstTask?: string;
213
241
  /** The delegation's invite-time model (omitted = follow the harness default). */
214
242
  readonly model?: string;
243
+ /** The delegation's invite-time reasoning effort (omitted = follow the harness default). */
244
+ readonly effort?: string;
215
245
  }
216
246
  /** The invite outcome: the receipt's pendingFirstTask picks the success copy. */
217
247
  export type RoomInviteOutcome = {
@@ -230,7 +260,15 @@ export interface RoomMembersInjected extends RoomInviteInjected {
230
260
  readonly openSession: (sessionId: SessionId) => void;
231
261
  /** Remove the named member from the roster. */
232
262
  readonly setCoordinator?: ((memberId: string, expectedRevision: number) => Promise<RoomMutationOutcome>) | undefined;
233
- readonly removeMember: (member: string) => Promise<RoomMutationOutcome>;
263
+ readonly removeMember: (member: string) => Promise<RoomRemovalOutcome>;
264
+ /** Undo a removal: the member rejoins on its own session. */
265
+ readonly restoreMember: (member: string) => Promise<RoomMutationOutcome>;
266
+ /** Open the team page in the right sidebar, optionally revealing one member. Absent without a sidebar. */
267
+ readonly openTeam?: ((member?: string) => void) | undefined;
268
+ /** Take an outside session off the member list (its own session is untouched). */
269
+ readonly unlinkSession?: ((target: string) => void) | undefined;
270
+ /** Change the room's collaboration settings. */
271
+ readonly setSettings: (patch: Partial<RoomSettings>) => Promise<RoomMutationOutcome>;
234
272
  /**
235
273
  * Edit the named member: rename, cwd override (null clears back to
236
274
  * inheriting the room cwd), role instructions (null clears them), intended
@@ -265,10 +303,14 @@ export interface RoomMemberPatch {
265
303
  readonly rename?: string;
266
304
  /** New role instructions; null clears them. */
267
305
  readonly instructions?: string | null;
306
+ /** New public role; null clears it. */
307
+ readonly role?: string | null;
268
308
  /** New cwd override; null clears back to inheriting the room cwd. */
269
309
  readonly cwd?: string | null;
270
310
  /** New intended model; null clears back to the harness default. */
271
311
  readonly model?: string | null;
312
+ /** New intended reasoning effort; null clears back to the harness default. */
313
+ readonly effort?: string | null;
272
314
  }
273
315
  /** Injected face of the member-speech chat node. */
274
316
  export interface RoomSpeechInjected {
@@ -276,6 +318,11 @@ export interface RoomSpeechInjected {
276
318
  readonly roomStore: RoomStore;
277
319
  /** Open a session (the speech's child-session jump). */
278
320
  readonly openSession: (sessionId: SessionId) => void;
321
+ /**
322
+ * Open Context Lens at the member's round (its session + closing message);
323
+ * absent when the composition has no Context Lens.
324
+ */
325
+ readonly openDetail?: ((sessionId: SessionId, messageId: string) => void) | undefined;
279
326
  }
280
327
  /** Full props of the 'room-speech' chat-node renderer. */
281
328
  export type RoomSpeechViewProps = PropsRuntime<'conversation.chat.node', 'room-speech'> & InjectFace<RoomSpeechInjected> & PropsLocale<'room'>;
@@ -304,24 +351,47 @@ export interface RoomRelayInjected {
304
351
  }
305
352
  /** Full props of the 'room-relay' chat-node renderer. */
306
353
  export type RoomRelayViewProps = PropsRuntime<'conversation.chat.node', 'room-relay'> & InjectFace<RoomRelayInjected> & PropsLocale<'room'>;
307
- /** Injected face of the task-advance chat node. */
308
- export interface RoomTaskLineInjected {
309
- /** The client-side room state store (the goal-progress suffix). */
310
- readonly roomStore: RoomStore;
354
+ /** Injected face of a member's tool approval line. */
355
+ export interface RoomApprovalInjected {
356
+ /** Answer the approval (allow once / reject); resolves with an error message when it already ended. */
357
+ readonly answerApproval: (approvalId: string, outcome: 'allowed-once' | 'rejected') => Promise<string | undefined>;
311
358
  }
312
- /** Full props of the 'room-task-line' chat-node renderer. */
313
- export type RoomTaskLineViewProps = PropsRuntime<'conversation.chat.node', 'room-task-line'> & InjectFace<RoomTaskLineInjected> & PropsLocale<'room'>;
314
- /**
315
- * Props of the dock capsules (goal capsule + task capsule), rendered by the
316
- * RoomComposer itself above the input card — NOT a slot entry:
317
- * `conversation.input.dock` lives inside the official composer fallback,
318
- * which the takeover hides. The invite share serves the fresh-room state:
319
- * a room with neither a goal nor any task renders a single 「+ 邀请成员」
320
- * capsule instead of the pair, opening the invite dialog straight from the
321
- * dock.
322
- */
323
- export type RoomDockCapsulesProps = {
324
- readonly sessionId: SessionId;
325
- readonly formalPlans?: boolean;
326
- } & RoomTasksInjected & RoomInviteInjected & PropsLocale<'room'>;
359
+ /** Injected face of a room decision row. */
360
+ export interface RoomDecisionInjected {
361
+ /** Open Context Lens at this decision's model request; absent without Context Lens. */
362
+ readonly openDecision?: ((decisionId: string) => void) | undefined;
363
+ }
364
+ /** Full props of the 'room-decision' chat-node renderer. */
365
+ export type RoomDecisionViewProps = PropsRuntime<'conversation.chat.node', 'room-decision'> & InjectFace<RoomDecisionInjected> & PropsLocale<'room'>;
366
+ /** Injected face of a member's question card. */
367
+ export interface RoomQuestionInjected {
368
+ /** Submit the answers; resolves with an error message when the card already ended. */
369
+ readonly answerQuestion: (questionId: string, answers: readonly RoomQuestionAnswer[]) => Promise<string | undefined>;
370
+ }
371
+ /** Full props of the 'room-question' chat-node renderer. */
372
+ export type RoomQuestionViewProps = PropsRuntime<'conversation.chat.node', 'room-question'> & InjectFace<RoomQuestionInjected> & PropsLocale<'room'>;
373
+ /** Injected face of the help rows (the asking room's thread row, the helper's request card). */
374
+ export interface RoomHelpInjected {
375
+ /** Open another session (the helper's, or the asking room). */
376
+ readonly openSession: (sessionId: SessionId) => void;
377
+ /** Keep an outside session on the member list (the third ask's hint). */
378
+ readonly linkSession?: ((target: string, title: string) => void) | undefined;
379
+ /** Dismiss that hint for good. */
380
+ readonly dismissNudge?: ((target: string) => void) | undefined;
381
+ /** The client-side room state store (the reminder setting). */
382
+ readonly roomStore?: RoomStore | undefined;
383
+ }
384
+ /** Full props of the 'room-help' chat-node renderer. */
385
+ export type RoomHelpViewProps = PropsRuntime<'conversation.chat.node', 'room-help'> & InjectFace<RoomHelpInjected> & PropsLocale<'room'>;
386
+ /** Full props of the 'room-help-request' chat-node renderer. */
387
+ export type RoomHelpRequestViewProps = PropsRuntime<'conversation.chat.node', 'room-help-request'> & InjectFace<RoomHelpInjected> & PropsLocale<'room'>;
388
+ /** Full props of the 'room-approval' chat-node renderer. */
389
+ export type RoomApprovalViewProps = PropsRuntime<'conversation.chat.node', 'room-approval'> & InjectFace<RoomApprovalInjected> & PropsLocale<'room'>;
390
+ /** Injected face of the held-reports line. */
391
+ export interface RoomInboxInjected {
392
+ /** Hand the named member its held reports now. */
393
+ readonly releaseReports: (member: string) => Promise<void>;
394
+ }
395
+ /** Full props of the 'room-inbox' chat-node renderer. */
396
+ export type RoomInboxViewProps = PropsRuntime<'conversation.chat.node', 'room-inbox'> & InjectFace<RoomInboxInjected> & PropsLocale<'room'>;
327
397
  //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The composer's task tag, per room session: the task page's 「追问」 and
3
+ * 「+ 新任务」 put a removable 「任务 N」 / 「新任务」 chip on the main
4
+ * composer (the page has no input of its own), the next send carries it as
5
+ * postMessage's `task`, and removing the chip leaves the filing to dsh.
6
+ * Every set bumps a revision so the composer takes focus even when the
7
+ * same tag is set twice.
8
+ */
9
+ /** A tag on the composer. */
10
+ export interface RoomTaskTag {
11
+ /** `new`, or the task's root id. */
12
+ readonly task: string;
13
+ /** The chip text (「任务 3」 / 「新任务」). */
14
+ readonly label: string;
15
+ readonly revision: number;
16
+ }
17
+ /** The shared tag store; one per client plugin. */
18
+ export declare class RoomTaskTags {
19
+ private readonly tags;
20
+ private readonly listeners;
21
+ private revision;
22
+ /** Subscribe to tag changes (useSyncExternalStore shape). */
23
+ readonly subscribe: (listener: () => void) => (() => void);
24
+ /** The session's current tag. */
25
+ readonly get: (sessionId: string) => RoomTaskTag | undefined;
26
+ /** Put a tag on the session's composer, or clear it. */
27
+ set(sessionId: string, tag: {
28
+ readonly task: string;
29
+ readonly label: string;
30
+ } | undefined): void;
31
+ }
32
+ //# sourceMappingURL=task-tag.d.ts.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The composer's task tag, per room session: the task page's 「追问」 and
3
+ * 「+ 新任务」 put a removable 「任务 N」 / 「新任务」 chip on the main
4
+ * composer (the page has no input of its own), the next send carries it as
5
+ * postMessage's `task`, and removing the chip leaves the filing to dsh.
6
+ * Every set bumps a revision so the composer takes focus even when the
7
+ * same tag is set twice.
8
+ */
9
+ /** The shared tag store; one per client plugin. */
10
+ export class RoomTaskTags {
11
+ tags = new Map();
12
+ listeners = new Set();
13
+ revision = 0;
14
+ /** Subscribe to tag changes (useSyncExternalStore shape). */
15
+ subscribe = (listener) => {
16
+ this.listeners.add(listener);
17
+ return () => { this.listeners.delete(listener); };
18
+ };
19
+ /** The session's current tag. */
20
+ get = (sessionId) => this.tags.get(sessionId);
21
+ /** Put a tag on the session's composer, or clear it. */
22
+ set(sessionId, tag) {
23
+ if (tag === undefined) {
24
+ if (!this.tags.delete(sessionId))
25
+ return;
26
+ }
27
+ else {
28
+ this.tags.set(sessionId, { ...tag, revision: ++this.revision });
29
+ }
30
+ for (const listener of this.listeners)
31
+ listener();
32
+ }
33
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pure projections the task page and the composer's task capsule share:
3
+ * which tasks need the human, a task's subtask tree (who handed what to
4
+ * whom, from each delivery's `parent`), and the time segments its rows and
5
+ * swimlanes draw.
6
+ */
7
+ import type { RoomDelivery, RoomJob, RoomRelay, RoomState } from '../types.ts';
8
+ /** One stretch of a delivery's life on the timeline. */
9
+ export interface TaskSegment {
10
+ /** queue = waiting to start, held = a finished report waiting to be handed over, exec = running. */
11
+ readonly kind: 'queue' | 'held' | 'exec';
12
+ readonly from: number;
13
+ readonly to: number;
14
+ }
15
+ /** A delivery placed in its task's tree. */
16
+ export interface SubtaskNode {
17
+ readonly delivery: RoomDelivery;
18
+ readonly children: readonly SubtaskNode[];
19
+ }
20
+ /** The list filters, in order. */
21
+ export declare const TASK_FILTERS: readonly ['running', 'attention', 'done', 'all'];
22
+ export type TaskFilter = typeof TASK_FILTERS[number];
23
+ /** Completed tasks shown by default; older ones stay in the log. */
24
+ export declare const DONE_SHOWN = 20;
25
+ /** Whether a delivery is still open (queued, running or a held report). */
26
+ export declare function isOpenDelivery(row: RoomDelivery): boolean;
27
+ /** Relays waiting for the human's confirmation. */
28
+ export declare function pendingRelays(state: RoomState): readonly RoomRelay[];
29
+ /** How many things wait on the human: deliveries with an unknown outcome, held relays, and members' tool approvals. */
30
+ export declare function attentionCount(state: RoomState): number;
31
+ /** Whether a task needs the human: an uncertain delivery, or a relay held inside it. */
32
+ export declare function needsAttention(state: RoomState, job: RoomJob): boolean;
33
+ /** The tasks a filter shows (`running` includes those needing attention, as both are open). */
34
+ export declare function filterJobs(state: RoomState, filter: TaskFilter): readonly RoomJob[];
35
+ /** A task's deliveries, in dispatch order. */
36
+ export declare function jobDeliveries(state: RoomState, job: RoomJob): readonly RoomDelivery[];
37
+ /**
38
+ * The task's subtask tree: work handed out (reports travel back up the tree
39
+ * and are not nodes), each delivery under the delivery whose round sent it.
40
+ * A delivery whose sender is outside the task — the human, or a round of
41
+ * another task — is a root.
42
+ */
43
+ export declare function subtaskTree(state: RoomState, job: RoomJob): readonly SubtaskNode[];
44
+ /** A delivery's time segments up to `now` for the open edges. */
45
+ export declare function segmentsOf(row: RoomDelivery, now: number): readonly TaskSegment[];
46
+ /** How long a task has taken: to its last settlement, or until now while open. */
47
+ export declare function jobElapsed(job: RoomJob, now: number): number;
48
+ /** The members still working on a task (running), by id, in first-start order. */
49
+ export declare function runningMembers(state: RoomState, job: RoomJob): readonly string[];
50
+ //# sourceMappingURL=tasks.d.ts.map
@@ -0,0 +1,82 @@
1
+ /** The list filters, in order. */
2
+ export const TASK_FILTERS = ['running', 'attention', 'done', 'all'];
3
+ /** Completed tasks shown by default; older ones stay in the log. */
4
+ export const DONE_SHOWN = 20;
5
+ const OPEN = new Set(['queued', 'running', 'held']);
6
+ /** Whether a delivery is still open (queued, running or a held report). */
7
+ export function isOpenDelivery(row) {
8
+ return OPEN.has(row.status);
9
+ }
10
+ /** Relays waiting for the human's confirmation. */
11
+ export function pendingRelays(state) {
12
+ return state.relays.filter(relay => relay.state === 'pending');
13
+ }
14
+ /** How many things wait on the human: deliveries with an unknown outcome, held relays, and members' tool approvals. */
15
+ export function attentionCount(state) {
16
+ return (state.deliveries ?? []).filter(row => row.status === 'uncertain').length + pendingRelays(state).length + (state.approvals?.length ?? 0) + (state.questions?.length ?? 0);
17
+ }
18
+ /** Whether a task needs the human: an uncertain delivery, or a relay held inside it. */
19
+ export function needsAttention(state, job) {
20
+ return job.status === 'attention' || pendingRelays(state).some(relay => relay.intent?.task === job.id);
21
+ }
22
+ /** The tasks a filter shows (`running` includes those needing attention, as both are open). */
23
+ export function filterJobs(state, filter) {
24
+ const jobs = state.jobs ?? [];
25
+ switch (filter) {
26
+ case 'running': return jobs.filter(job => job.status === 'running' || needsAttention(state, job));
27
+ case 'attention': return jobs.filter(job => needsAttention(state, job));
28
+ case 'done': return jobs.filter(job => job.status === 'done' || job.status === 'stopped').slice(0, DONE_SHOWN);
29
+ case 'all': return jobs.slice(0, DONE_SHOWN + jobs.filter(job => job.status !== 'done' && job.status !== 'stopped').length);
30
+ }
31
+ }
32
+ /** A task's deliveries, in dispatch order. */
33
+ export function jobDeliveries(state, job) {
34
+ const ids = new Set(job.deliveries);
35
+ return (state.deliveries ?? []).filter(row => ids.has(row.id));
36
+ }
37
+ /**
38
+ * The task's subtask tree: work handed out (reports travel back up the tree
39
+ * and are not nodes), each delivery under the delivery whose round sent it.
40
+ * A delivery whose sender is outside the task — the human, or a round of
41
+ * another task — is a root.
42
+ */
43
+ export function subtaskTree(state, job) {
44
+ const rows = jobDeliveries(state, job).filter(row => row.origin !== 'report');
45
+ const ids = new Set(rows.map(row => row.id));
46
+ const children = new Map();
47
+ for (const row of rows) {
48
+ const parent = row.parent !== undefined && ids.has(row.parent) ? row.parent : undefined;
49
+ children.set(parent, [...children.get(parent) ?? [], row]);
50
+ }
51
+ const build = (parent, seen) => (children.get(parent) ?? []).filter(row => !seen.has(row.id))
52
+ .map(row => ({ delivery: row, children: build(row.id, new Set([...seen, row.id])) }));
53
+ return build(undefined, new Set());
54
+ }
55
+ /** A delivery's time segments up to `now` for the open edges. */
56
+ export function segmentsOf(row, now) {
57
+ const segments = [];
58
+ const end = row.endedAt ?? (isOpenDelivery(row) ? now : row.startedAt ?? row.queuedAt);
59
+ const started = row.startedAt ?? (row.status === 'queued' || row.status === 'held' ? now : end);
60
+ if (row.heldAt !== undefined) {
61
+ const released = row.releasedAt ?? Math.min(started, now);
62
+ if (row.heldAt > row.queuedAt)
63
+ segments.push({ kind: 'queue', from: row.queuedAt, to: row.heldAt });
64
+ segments.push({ kind: 'held', from: row.heldAt, to: released });
65
+ if (started > released)
66
+ segments.push({ kind: 'queue', from: released, to: started });
67
+ }
68
+ else if (started > row.queuedAt) {
69
+ segments.push({ kind: 'queue', from: row.queuedAt, to: started });
70
+ }
71
+ if (row.startedAt !== undefined && end > row.startedAt)
72
+ segments.push({ kind: 'exec', from: row.startedAt, to: end });
73
+ return segments;
74
+ }
75
+ /** How long a task has taken: to its last settlement, or until now while open. */
76
+ export function jobElapsed(job, now) {
77
+ return Math.max(0, (job.endedAt ?? now) - job.startedAt);
78
+ }
79
+ /** The members still working on a task (running), by id, in first-start order. */
80
+ export function runningMembers(state, job) {
81
+ return [...new Set(jobDeliveries(state, job).filter(row => row.status === 'running').map(row => row.memberId))];
82
+ }
@@ -0,0 +1,73 @@
1
+ import type { RoomDelivery, RoomHelp, RoomMember, RoomState } from '../types.ts';
2
+ /** The human, as a tree root. */
3
+ export declare const TEAM_ROOT = "you";
4
+ /** One delivery a member received. */
5
+ export interface TeamDelivery {
6
+ readonly id: string;
7
+ readonly seq: number;
8
+ /** Sender name, or {@link TEAM_ROOT} for the human. */
9
+ readonly from: string;
10
+ readonly text: string;
11
+ readonly status: RoomDelivery['status'];
12
+ readonly kind: 'task' | 'notify' | 'report';
13
+ }
14
+ /** One member node. */
15
+ export interface TeamNode {
16
+ /** Member id (name for legacy members without one). */
17
+ readonly id: string;
18
+ readonly member: RoomMember;
19
+ /** Parent node id, or {@link TEAM_ROOT}. */
20
+ readonly parent: string;
21
+ readonly coordinator: boolean;
22
+ readonly status: 'running' | 'idle' | 'failed';
23
+ /** The running delivery's task title, when running. */
24
+ readonly current?: string;
25
+ readonly model?: string;
26
+ readonly tokens: number;
27
+ readonly elapsedMs: number;
28
+ readonly rounds: number;
29
+ /** Newest first. */
30
+ readonly deliveries: readonly TeamDelivery[];
31
+ /** An outside session: asked for help (and/or kept on the member list). */
32
+ readonly external?: {
33
+ readonly sessionId: string;
34
+ /** On the member list (「加入房间成员」). */
35
+ readonly linked: boolean;
36
+ /** A room target (asked through that room's roster): never linkable. */
37
+ readonly room: boolean;
38
+ /** Every ask has ended (replied or failed). */
39
+ readonly done: boolean;
40
+ readonly helps: readonly RoomHelp[];
41
+ };
42
+ }
43
+ /** What flowed from one node to another. */
44
+ export interface TeamEdge {
45
+ readonly from: string;
46
+ readonly to: string;
47
+ readonly tasks: number;
48
+ readonly reports: number;
49
+ readonly notifies: number;
50
+ }
51
+ /** The whole projection. */
52
+ export interface Team {
53
+ readonly nodes: readonly TeamNode[];
54
+ readonly edges: readonly TeamEdge[];
55
+ readonly totals: {
56
+ readonly members: number;
57
+ readonly running: number;
58
+ readonly deliveries: number;
59
+ readonly tokens: number;
60
+ readonly elapsedMs: number;
61
+ };
62
+ }
63
+ /**
64
+ * Project the room state into the team tree.
65
+ * @param state - the replayed room state.
66
+ * @returns nodes in tree order (parents before children), edges and totals.
67
+ */
68
+ export declare function teamOf(state: RoomState): Team;
69
+ /** The id of an outside session's node. */
70
+ export declare const externalId: (sessionId: string, member?: string) => string;
71
+ /** Compact token count: 1.2M / 288k / 512. */
72
+ export declare function formatTokens(tokens: number): string;
73
+ //# sourceMappingURL=team.d.ts.map