@wowyuarm/dsh-agent-team 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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/README.zh.md +103 -0
  4. package/cordis.patch.yml +36 -0
  5. package/package.json +152 -0
  6. package/packages/agent-team/README.md +58 -0
  7. package/packages/agent-team/README.zh.md +57 -0
  8. package/packages/agent-team/lib/index.js +841 -0
  9. package/packages/agent-team/lib/invariant.js +24 -0
  10. package/packages/agent-team/lib/ledger.js +2161 -0
  11. package/packages/agent-team/lib/member-context.js +69 -0
  12. package/packages/agent-team/lib/preset-roster.js +14 -0
  13. package/packages/agent-team/lib/spec.js +347 -0
  14. package/packages/agent-team/lib/typert.host.d.ts +3 -0
  15. package/packages/agent-team/lib/typert.host.js +2084 -0
  16. package/packages/agent-team/lib/typert.remote-client.d.ts +54 -0
  17. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -0
  18. package/packages/agent-team/lib/typert.remote-client.js +1218 -0
  19. package/packages/agent-team/lib/types/index.d.ts +156 -0
  20. package/packages/agent-team/lib/types/index.d.ts.map +1 -0
  21. package/packages/agent-team/lib/types/invariant.d.ts +13 -0
  22. package/packages/agent-team/lib/types/invariant.d.ts.map +1 -0
  23. package/packages/agent-team/lib/types/ledger.d.ts +271 -0
  24. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -0
  25. package/packages/agent-team/lib/types/member-context.d.ts +7 -0
  26. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -0
  27. package/packages/agent-team/lib/types/preset-roster.d.ts +5 -0
  28. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -0
  29. package/packages/agent-team/lib/types/spec.d.ts +13 -0
  30. package/packages/agent-team/lib/types/spec.d.ts.map +1 -0
  31. package/packages/agent-team/lib/types/types.d.ts +818 -0
  32. package/packages/agent-team/lib/types/types.d.ts.map +1 -0
  33. package/packages/agent-team/lib/types.js +1 -0
  34. package/packages/agent-team/preset/team-member/agent.cordis.yml +89 -0
  35. package/packages/client-agent-team/README.md +39 -0
  36. package/packages/client-agent-team/README.zh.md +39 -0
  37. package/packages/client-agent-team/lib/client.js +9633 -0
  38. package/packages/client-agent-team/lib/client.js.map +1 -0
  39. package/packages/client-agent-team/lib/index.js +5 -0
  40. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +20 -0
  41. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -0
  42. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +274 -0
  43. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +19 -0
  44. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -0
  45. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +243 -0
  46. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +21 -0
  47. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -0
  48. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +196 -0
  49. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +15 -0
  50. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -0
  51. package/packages/client-agent-team/lib/types/client/TeamComposer.js +144 -0
  52. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +3 -0
  53. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/TeamConversation.js +20 -0
  55. package/packages/client-agent-team/lib/types/client/TeamFooterAction.d.ts +3 -0
  56. package/packages/client-agent-team/lib/types/client/TeamFooterAction.d.ts.map +1 -0
  57. package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +19 -0
  58. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +13 -0
  59. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +1 -0
  60. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.js +90 -0
  61. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts +12 -0
  62. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -0
  63. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +15 -0
  64. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +5 -0
  65. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -0
  66. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +32 -0
  67. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +19 -0
  68. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -0
  69. package/packages/client-agent-team/lib/types/client/TeamMessage.js +24 -0
  70. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +11 -0
  71. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -0
  72. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +20 -0
  73. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts +15 -0
  74. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -0
  75. package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +23 -0
  76. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +3 -0
  77. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +1 -0
  78. package/packages/client-agent-team/lib/types/client/TeamSettings.js +5 -0
  79. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts +9 -0
  80. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts.map +1 -0
  81. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +9 -0
  82. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +22 -0
  83. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -0
  84. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +464 -0
  85. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +3 -0
  86. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -0
  87. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +39 -0
  88. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +11 -0
  89. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -0
  90. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.js +6 -0
  91. package/packages/client-agent-team/lib/types/client/index.d.ts +19 -0
  92. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -0
  93. package/packages/client-agent-team/lib/types/client/index.js +113 -0
  94. package/packages/client-agent-team/lib/types/client/locales.d.ts +224 -0
  95. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -0
  96. package/packages/client-agent-team/lib/types/client/locales.js +222 -0
  97. package/packages/client-agent-team/lib/types/client/navigation.d.ts +39 -0
  98. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -0
  99. package/packages/client-agent-team/lib/types/client/navigation.js +96 -0
  100. package/packages/client-agent-team/lib/types/client/slots.d.ts +74 -0
  101. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -0
  102. package/packages/client-agent-team/lib/types/client/slots.js +1 -0
  103. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +28 -0
  104. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -0
  105. package/packages/client-agent-team/lib/types/client/team-changes.js +70 -0
  106. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +53 -0
  107. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -0
  108. package/packages/client-agent-team/lib/types/client/team-formatters.js +128 -0
  109. package/packages/client-agent-team/lib/types/client/team-membership.d.ts +25 -0
  110. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -0
  111. package/packages/client-agent-team/lib/types/client/team-membership.js +39 -0
  112. package/packages/client-agent-team/lib/types/client/team-separators.d.ts +23 -0
  113. package/packages/client-agent-team/lib/types/client/team-separators.d.ts.map +1 -0
  114. package/packages/client-agent-team/lib/types/client/team-separators.js +52 -0
  115. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +18 -0
  116. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -0
  117. package/packages/client-agent-team/lib/types/client/timeline-scroll.js +54 -0
  118. package/packages/client-agent-team/lib/types/index.d.ts +3 -0
  119. package/packages/client-agent-team/lib/types/index.d.ts.map +1 -0
  120. package/packages/client-agent-team/lib/types/index.js +2 -0
  121. package/packages/tool-agent-team/README.md +23 -0
  122. package/packages/tool-agent-team/README.zh.md +23 -0
  123. package/packages/tool-agent-team/lib/index.js +294 -0
  124. package/packages/tool-agent-team/lib/types/index.d.ts +5 -0
  125. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -0
@@ -0,0 +1,96 @@
1
+ const STORAGE_KEY = 'dsh.agent-team.navigation';
2
+ function readSnapshot() {
3
+ if (typeof localStorage === 'undefined')
4
+ return { mode: 'conversation' };
5
+ try {
6
+ const parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '');
7
+ return {
8
+ mode: parsed.mode === 'team' ? 'team' : 'conversation',
9
+ ...(typeof parsed.workspaceId === 'string' ? { workspaceId: parsed.workspaceId } : {}),
10
+ };
11
+ }
12
+ catch {
13
+ return { mode: 'conversation' };
14
+ }
15
+ }
16
+ function persistSnapshot(snapshot) {
17
+ if (typeof localStorage === 'undefined')
18
+ return;
19
+ try {
20
+ const { mode, workspaceId } = snapshot;
21
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ mode, ...(workspaceId === undefined ? {} : { workspaceId }) }));
22
+ }
23
+ catch {
24
+ // Local persistence is a convenience; private mode and quota failures do not block navigation.
25
+ }
26
+ }
27
+ /** Root-scoped Team mode state. Slot lifetimes subscribe to this source. */
28
+ export class TeamNavigation {
29
+ snapshot = readSnapshot();
30
+ listeners = new Set();
31
+ getSnapshot = () => this.snapshot;
32
+ subscribe = (listener) => {
33
+ this.listeners.add(listener);
34
+ return () => { this.listeners.delete(listener); };
35
+ };
36
+ actions() {
37
+ return {
38
+ enterTeam: () => { this.setMode('team'); },
39
+ leaveTeam: () => { this.setMode('conversation'); },
40
+ selectWorkspace: workspaceId => { this.setWorkspace(workspaceId); },
41
+ selectChannel: channelRef => { this.setChannel(channelRef); },
42
+ selectThread: (taskRef, threadRef, channelRef, taskNumber) => { this.setThread(taskRef, threadRef, channelRef, taskNumber); },
43
+ backToWorkspace: () => { this.setThread(undefined); },
44
+ backToChannels: () => { this.clearChannel(); },
45
+ };
46
+ }
47
+ dispose() {
48
+ this.listeners.clear();
49
+ }
50
+ setMode(mode) {
51
+ if (this.snapshot.mode === mode)
52
+ return;
53
+ this.snapshot = { ...this.snapshot, mode };
54
+ this.commit();
55
+ }
56
+ setWorkspace(workspaceId) {
57
+ if (this.snapshot.workspaceId === workspaceId)
58
+ return;
59
+ const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
60
+ this.snapshot = { ...base, workspaceId };
61
+ this.commit();
62
+ }
63
+ setChannel(channelRef) {
64
+ if (this.snapshot.channelRef === channelRef && this.snapshot.threadRef === undefined)
65
+ return;
66
+ const { taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
67
+ this.snapshot = { ...base, channelRef };
68
+ this.commit();
69
+ }
70
+ clearChannel() {
71
+ const { channelRef: _channelRef, ...base } = this.snapshot;
72
+ if (this.snapshot.channelRef === undefined)
73
+ return;
74
+ this.snapshot = base;
75
+ this.commit();
76
+ }
77
+ setThread(taskRef, threadRef, channelRef, taskNumber) {
78
+ if (this.snapshot.threadRef === threadRef && this.snapshot.taskRef === taskRef && this.snapshot.channelRef === channelRef && this.snapshot.taskNumber === taskNumber)
79
+ return;
80
+ if (threadRef === undefined || taskRef === undefined) {
81
+ const { taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
82
+ this.snapshot = base;
83
+ }
84
+ else {
85
+ const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
86
+ this.snapshot = { ...base, taskRef, threadRef, ...(channelRef === undefined ? {} : { channelRef }), ...(taskNumber === undefined ? {} : { taskNumber }) };
87
+ }
88
+ this.commit();
89
+ }
90
+ commit() {
91
+ persistSnapshot(this.snapshot);
92
+ for (const listener of this.listeners)
93
+ listener();
94
+ }
95
+ }
96
+ export { STORAGE_KEY };
@@ -0,0 +1,74 @@
1
+ import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { AgentTeamAddMemberRequest, AgentTeamChannelRef, AgentTeamClientMemberStatus, AgentTeamCreateChannelRequest, AgentTeamCreateChannelResult, AgentTeamJoinChannelRequest, AgentTeamJoinChannelResult, AgentTeamMemberResult, AgentTeamMembersRequest, AgentTeamRemoveChannelMemberRequest, AgentTeamRemoveChannelMemberResult, AgentTeamReplyRequest, AgentTeamReplyResult, AgentTeamConfirmationRequired, AgentTeamThreadHistory, AgentTeamThreadHistoryRequest, AgentTeamThreadReadRequest, AgentTeamThreadReadResult, AgentTeamTaskRequest, AgentTeamTaskResult, AgentTeamSendMessageRequest, AgentTeamSendMessageResult, AgentTeamUpdateChannelRequest, AgentTeamUpdateChannelResult, AgentTeamUpdateMemberRequest, AgentTeamView, AgentTeamViewRequest } from '@wowyuarm/dsh-agent-team/types';
3
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
4
+ import type { TeamNavigationActions, TeamNavigationSnapshot } from './navigation.ts';
5
+ import type { TeamChangeListener, TeamChangeScope } from './team-changes.ts';
6
+ export interface TeamNavigationSource {
7
+ getSnapshot: () => TeamNavigationSnapshot;
8
+ subscribe: (listener: () => void) => () => void;
9
+ }
10
+ /** Subscribe to one projection scope's invalidation stream; disposal aborts the shared poll. */
11
+ export type SubscribeTeamChanges = (scope: TeamChangeScope, listener: TeamChangeListener) => () => void;
12
+ /** One selectable model row inside one provider group of the Host catalog. */
13
+ export interface TeamModelOption {
14
+ readonly id: string;
15
+ readonly name: string;
16
+ }
17
+ /** Provider-grouped slice of the Host model catalog the editors render. */
18
+ export interface TeamModelProviderGroup {
19
+ readonly id: string;
20
+ readonly name: string;
21
+ readonly models: readonly TeamModelOption[];
22
+ }
23
+ /** Host-scoped catalog load result; per-provider listing failures ride `failures`. */
24
+ export interface TeamModelCatalog {
25
+ readonly groups: readonly TeamModelProviderGroup[];
26
+ readonly failures: readonly {
27
+ readonly id: string;
28
+ readonly name: string;
29
+ readonly message: string;
30
+ }[];
31
+ }
32
+ export type TeamSidebarProps = PropsRuntime<'sidebar.workspaces'> & PropsLocale<'team'> & TeamNavigationActions & {
33
+ navigation: TeamNavigationSource;
34
+ loadMembers: (request: AgentTeamMembersRequest) => Promise<RemoteResult<readonly AgentTeamClientMemberStatus[]>>;
35
+ subscribeChanges: SubscribeTeamChanges;
36
+ addMember: (request: AgentTeamAddMemberRequest) => Promise<RemoteResult<AgentTeamMemberResult>>;
37
+ loadChannels: (request: AgentTeamViewRequest) => Promise<RemoteResult<AgentTeamView>>;
38
+ createChannel: (request: AgentTeamCreateChannelRequest) => Promise<RemoteResult<AgentTeamCreateChannelResult>>;
39
+ updateChannel: (request: AgentTeamUpdateChannelRequest) => Promise<RemoteResult<AgentTeamUpdateChannelResult>>;
40
+ updateMember: (request: AgentTeamUpdateMemberRequest) => Promise<RemoteResult<AgentTeamMemberResult>>;
41
+ joinChannel: (request: AgentTeamJoinChannelRequest) => Promise<RemoteResult<AgentTeamJoinChannelResult>>;
42
+ removeChannelMember: (request: AgentTeamRemoveChannelMemberRequest) => Promise<RemoteResult<AgentTeamRemoveChannelMemberResult>>;
43
+ /** Session-independent Host model catalog (`llm.models`); needs no live Member. */
44
+ loadModels: () => Promise<RemoteResult<TeamModelCatalog>>;
45
+ /** Focus the Member's own Session conversation page in the ordinary shell. */
46
+ openMemberSession: (sessionId: AgentTeamClientMemberStatus['member']['sessionId']) => void;
47
+ selectedChannelRef?: AgentTeamChannelRef;
48
+ };
49
+ export type TeamConversationProps = PropsRuntime<'conversation'> & PropsLocale<'team'> & TeamNavigationActions & {
50
+ navigation: TeamNavigationSource;
51
+ loadChannels: (request: AgentTeamViewRequest) => Promise<RemoteResult<AgentTeamView>>;
52
+ readThread: (request: AgentTeamThreadReadRequest) => Promise<RemoteResult<AgentTeamThreadReadResult>>;
53
+ loadThreadHistory: (request: AgentTeamThreadHistoryRequest) => Promise<RemoteResult<AgentTeamThreadHistory>>;
54
+ subscribeChanges: SubscribeTeamChanges;
55
+ sendMessage: (request: AgentTeamSendMessageRequest) => Promise<RemoteResult<AgentTeamSendMessageResult>>;
56
+ joinChannel: (request: AgentTeamJoinChannelRequest) => Promise<RemoteResult<AgentTeamJoinChannelResult>>;
57
+ removeChannelMember: (request: AgentTeamRemoveChannelMemberRequest) => Promise<RemoteResult<AgentTeamRemoveChannelMemberResult>>;
58
+ reply: (request: AgentTeamReplyRequest) => Promise<RemoteResult<AgentTeamReplyResult | AgentTeamConfirmationRequired>>;
59
+ changeTask: (request: AgentTeamTaskRequest) => Promise<RemoteResult<AgentTeamTaskResult>>;
60
+ loadMembers: (request: AgentTeamMembersRequest) => Promise<RemoteResult<readonly AgentTeamClientMemberStatus[]>>;
61
+ };
62
+ export type TeamSettingsProps = PropsRuntime<'sidebar.settings'> & PropsLocale<'team'> & {
63
+ loadMemberGroups: () => Promise<readonly TeamMemberGroup[]>;
64
+ };
65
+ export interface TeamMemberGroup {
66
+ readonly workspaceId: string;
67
+ readonly workspaceTitle: string;
68
+ readonly members: readonly AgentTeamClientMemberStatus[];
69
+ }
70
+ export type TeamFooterProps = PropsRuntime<'sidebar.footer.action'> & PropsLocale<'team'> & TeamNavigationActions & {
71
+ navigation: TeamNavigationSource;
72
+ loadMemberGroups: () => Promise<readonly TeamMemberGroup[]>;
73
+ };
74
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
2
+ import type { AgentTeamChangeScope, AgentTeamChangesRequest, AgentTeamChangesResult } from '@wowyuarm/dsh-agent-team/types';
3
+ export type TeamChangeScope = AgentTeamChangeScope;
4
+ /** One invalidation delivered to every surface subscribed to one scope. */
5
+ export type TeamChangeUpdate = {
6
+ readonly type: 'changed';
7
+ readonly version: number;
8
+ } | {
9
+ readonly type: 'failed';
10
+ readonly message: string;
11
+ };
12
+ export type TeamChangeListener = (update: TeamChangeUpdate) => void;
13
+ type ChangesFn = (request: AgentTeamChangesRequest, signal?: AbortSignal) => Promise<RemoteResult<AgentTeamChangesResult>>;
14
+ /**
15
+ * One long-poll per change scope, shared by every listening surface: panels
16
+ * and pages never open parallel `changes` requests for the same scope, and
17
+ * the poll is aborted as soon as the last subscriber leaves.
18
+ */
19
+ export declare class TeamChangeStream {
20
+ private readonly changes;
21
+ private readonly polls;
22
+ constructor(changes: ChangesFn);
23
+ subscribe(scope: TeamChangeScope, listener: TeamChangeListener): () => void;
24
+ private run;
25
+ private fail;
26
+ }
27
+ export {};
28
+ //# sourceMappingURL=team-changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-changes.d.ts","sourceRoot":"","sources":["../../../src/client/team-changes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAE3H,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAA;AAElD,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzD,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAA;AAEnE,KAAK,SAAS,GAAG,CAAC,OAAO,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC,CAAA;AAa1H;;;;GAIG;AACH,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;gBAExB,OAAO,EAAE,SAAS;IAE/C,SAAS,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;YAmB7D,GAAG;IAyBjB,OAAO,CAAC,IAAI;CAKb"}
@@ -0,0 +1,70 @@
1
+ function scopeKey(scope) {
2
+ return scope.kind === 'workspace' ? `workspace:${scope.workspaceId}`
3
+ : scope.kind === 'channel' ? `channel:${scope.channelRef}`
4
+ : `thread:${scope.threadRef}`;
5
+ }
6
+ /**
7
+ * One long-poll per change scope, shared by every listening surface: panels
8
+ * and pages never open parallel `changes` requests for the same scope, and
9
+ * the poll is aborted as soon as the last subscriber leaves.
10
+ */
11
+ export class TeamChangeStream {
12
+ changes;
13
+ polls = new Map();
14
+ constructor(changes) {
15
+ this.changes = changes;
16
+ }
17
+ subscribe(scope, listener) {
18
+ const key = scopeKey(scope);
19
+ let poll = this.polls.get(key);
20
+ if (poll === undefined) {
21
+ poll = { controller: new AbortController(), listeners: new Set() };
22
+ this.polls.set(key, poll);
23
+ void this.run(key, scope, poll);
24
+ }
25
+ poll.listeners.add(listener);
26
+ return () => {
27
+ const current = this.polls.get(key);
28
+ if (current === undefined || !current.listeners.delete(listener))
29
+ return;
30
+ if (current.listeners.size === 0) {
31
+ this.polls.delete(key);
32
+ current.controller.abort();
33
+ }
34
+ };
35
+ }
36
+ async run(key, scope, poll) {
37
+ const { signal } = poll.controller;
38
+ // Sample the current version silently first: subscribers just fetched
39
+ // their initial projection, and an immediate wake would double-fetch.
40
+ const probe = await this.changes({ afterVersion: 0, scope }, signal);
41
+ if (signal.aborted)
42
+ return;
43
+ if (!probe.ok) {
44
+ this.fail(key, poll, probe.error.message);
45
+ return;
46
+ }
47
+ let version = probe.value.version;
48
+ while (!signal.aborted) {
49
+ const result = await this.changes({ afterVersion: version, scope }, signal);
50
+ if (signal.aborted)
51
+ return;
52
+ if (!result.ok) {
53
+ this.fail(key, poll, result.error.message);
54
+ return;
55
+ }
56
+ if (result.value.version > version) {
57
+ version = result.value.version;
58
+ for (const listener of poll.listeners)
59
+ listener({ type: 'changed', version });
60
+ }
61
+ }
62
+ }
63
+ fail(key, poll, message) {
64
+ // A dead poll must not stay registered: the next subscriber restarts it.
65
+ if (this.polls.get(key) === poll)
66
+ this.polls.delete(key);
67
+ for (const listener of poll.listeners)
68
+ listener({ type: 'failed', message });
69
+ }
70
+ }
@@ -0,0 +1,53 @@
1
+ import type { AgentTeamActivity, AgentTeamClaim, AgentTeamMemberId, AgentTeamTask } from '@wowyuarm/dsh-agent-team/types';
2
+ import type { TeamConversationProps } from './slots.ts';
3
+ export declare function formatTaskStatus(status: AgentTeamTask['status'], t: TeamConversationProps['t']): string;
4
+ export declare function formatClaimState(state: AgentTeamClaim['state'], t: TeamConversationProps['t']): string;
5
+ /**
6
+ * Status indicator variant for a Task status. Active states map to StateDot
7
+ * variants; every status renders a dot so all Task chips share one shape
8
+ * language — todo is a hollow ring (not started), closed a quiet gray dot
9
+ * (archived).
10
+ */
11
+ export declare function taskStatusDot(status: AgentTeamTask['status']): 'ongoing' | 'warning' | 'done' | 'todo' | 'closed';
12
+ /** One-line title snippet derived from the Task's root Message body. */
13
+ export declare function formatTaskTitle(body: string): string;
14
+ /** Deterministic avatar hue for one Member identity; stable across sessions and themes. */
15
+ export declare function memberHue(memberId: string): number;
16
+ export interface MentionSegment {
17
+ readonly text: string;
18
+ readonly mention: boolean;
19
+ /** Canonical handle of the mentioned Member; present only on mention segments. */
20
+ readonly name?: string;
21
+ }
22
+ /**
23
+ * Locate one Message's structured mention names inside its literal body. A
24
+ * name matches case-insensitively with an optional leading '@' on Unicode word
25
+ * boundaries, and longer names win at the same position. Mention segments
26
+ * render the canonical `@Handle`; names absent from the body come back
27
+ * unmatched so the consumer can append them as a fallback chip row.
28
+ */
29
+ export declare function splitMentionNames(text: string, names: readonly string[]): {
30
+ segments: MentionSegment[];
31
+ unmatched: readonly string[];
32
+ };
33
+ /** Canonical chip handles for one Message's structured mention refs. */
34
+ export declare function mentionNamesOf(mentions: readonly AgentTeamMemberId[], handles: ReadonlyMap<AgentTeamMemberId, string>): string[];
35
+ /**
36
+ * Whether an Agent body survives literal rendering unchanged: no fences,
37
+ * inline code, emphasis markers, links, images, tables, or block constructs.
38
+ * Only such plain-prose bodies may reuse the Human inline mention flow —
39
+ * anything richer keeps the trailing chip row because the Markdown primitive
40
+ * renders block-level documents that cannot interleave inline chips.
41
+ */
42
+ export declare function isPlainTextBody(text: string): boolean;
43
+ /**
44
+ * Wall-clock label for one Message instant: time within the current day,
45
+ * month-day time within the year, full date otherwise.
46
+ */
47
+ export declare function formatMessageTime(occurredAt: string, now?: Date): string;
48
+ export declare function formatActivity(activity: AgentTeamActivity, options: {
49
+ readonly t: TeamConversationProps['t'];
50
+ readonly actorName: (memberId: AgentTeamMemberId) => string;
51
+ readonly claims: readonly AgentTeamClaim[];
52
+ }): string;
53
+ //# sourceMappingURL=team-formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-formatters.d.ts","sourceRoot":"","sources":["../../../src/client/team-formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AACzH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,MAAM,CAQvG;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,MAAM,CAMtG;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAEjH;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,kFAAkF;IAClF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAwBtI;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAEhI;AAKD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,MAAM,CAS9E;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE;IACnE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,MAAM,CAAA;IAC3D,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAA;CAC3C,GAAG,MAAM,CAaT"}
@@ -0,0 +1,128 @@
1
+ export function formatTaskStatus(status, t) {
2
+ return t({
3
+ todo: 'taskStatusTodo',
4
+ in_progress: 'taskStatusInProgress',
5
+ in_review: 'taskStatusInReview',
6
+ done: 'taskStatusDone',
7
+ closed: 'taskStatusClosed',
8
+ }[status]);
9
+ }
10
+ export function formatClaimState(state, t) {
11
+ return t({
12
+ active: 'claimStateActive',
13
+ done: 'claimStateDone',
14
+ released: 'claimStateReleased',
15
+ }[state]);
16
+ }
17
+ /**
18
+ * Status indicator variant for a Task status. Active states map to StateDot
19
+ * variants; every status renders a dot so all Task chips share one shape
20
+ * language — todo is a hollow ring (not started), closed a quiet gray dot
21
+ * (archived).
22
+ */
23
+ export function taskStatusDot(status) {
24
+ return { todo: 'todo', in_progress: 'ongoing', in_review: 'warning', done: 'done', closed: 'closed' }[status];
25
+ }
26
+ /** One-line title snippet derived from the Task's root Message body. */
27
+ export function formatTaskTitle(body) {
28
+ const firstLine = body.split('\n', 1)[0]?.trim() ?? '';
29
+ return firstLine.length > 120 ? `${firstLine.slice(0, 119)}…` : firstLine;
30
+ }
31
+ /** Deterministic avatar hue for one Member identity; stable across sessions and themes. */
32
+ export function memberHue(memberId) {
33
+ let hash = 0;
34
+ for (let index = 0; index < memberId.length; index += 1)
35
+ hash = (hash * 31 + memberId.charCodeAt(index)) % 360;
36
+ return hash;
37
+ }
38
+ function escapeRegExp(value) {
39
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
40
+ }
41
+ /**
42
+ * Locate one Message's structured mention names inside its literal body. A
43
+ * name matches case-insensitively with an optional leading '@' on Unicode word
44
+ * boundaries, and longer names win at the same position. Mention segments
45
+ * render the canonical `@Handle`; names absent from the body come back
46
+ * unmatched so the consumer can append them as a fallback chip row.
47
+ */
48
+ export function splitMentionNames(text, names) {
49
+ if (names.length === 0)
50
+ return { segments: [{ text, mention: false }], unmatched: [] };
51
+ const ordered = [...names].sort((left, right) => right.length - left.length);
52
+ // The lookbehind keeps the optional '@' from consuming the separator of an
53
+ // email-like occurrence: the character before the name (or before its '@')
54
+ // must not be a letter, digit, underscore, or another '@'.
55
+ const pattern = new RegExp(`(?<![\\p{L}\\p{N}_@])@?(?:${ordered.map(name => `(${escapeRegExp(name)})`).join('|')})(?=$|[^\\p{L}\\p{N}_])`, 'giu');
56
+ const segments = [];
57
+ const matched = new Set();
58
+ let cursor = 0;
59
+ for (const match of text.matchAll(pattern)) {
60
+ const groupIndex = match.findIndex((group, index) => index >= 1 && group !== undefined);
61
+ if (groupIndex < 1)
62
+ continue;
63
+ if (match.index > cursor)
64
+ segments.push({ text: text.slice(cursor, match.index), mention: false });
65
+ const name = ordered[groupIndex - 1];
66
+ segments.push({ text: `@${name}`, mention: true, name });
67
+ matched.add(name.toLowerCase());
68
+ cursor = match.index + match[0].length;
69
+ }
70
+ if (cursor < text.length)
71
+ segments.push({ text: text.slice(cursor), mention: false });
72
+ return { segments, unmatched: names.filter(name => !matched.has(name.toLowerCase())) };
73
+ }
74
+ /** Canonical chip handles for one Message's structured mention refs. */
75
+ export function mentionNamesOf(mentions, handles) {
76
+ return mentions.map(memberId => handles.get(memberId)).filter((name) => name !== undefined);
77
+ }
78
+ const MARKDOWN_BLOCK_CONSTRUCT = /(^|\n)[ \t]{0,3}(?:#{1,6}[ \t]|>[ \t]|[-*+][ \t]|\d+[.)][ \t])|^[ \t]*\|.+\|/m;
79
+ const MARKDOWN_INLINE_CONSTRUCT = /[`*_[\]!]|~~~|```/;
80
+ /**
81
+ * Whether an Agent body survives literal rendering unchanged: no fences,
82
+ * inline code, emphasis markers, links, images, tables, or block constructs.
83
+ * Only such plain-prose bodies may reuse the Human inline mention flow —
84
+ * anything richer keeps the trailing chip row because the Markdown primitive
85
+ * renders block-level documents that cannot interleave inline chips.
86
+ */
87
+ export function isPlainTextBody(text) {
88
+ return !(MARKDOWN_BLOCK_CONSTRUCT.test(text) || MARKDOWN_INLINE_CONSTRUCT.test(text));
89
+ }
90
+ const pad = (value) => String(value).padStart(2, '0');
91
+ /**
92
+ * Wall-clock label for one Message instant: time within the current day,
93
+ * month-day time within the year, full date otherwise.
94
+ */
95
+ export function formatMessageTime(occurredAt, now = new Date()) {
96
+ const at = new Date(occurredAt);
97
+ if (Number.isNaN(at.getTime()))
98
+ return '';
99
+ const time = `${pad(at.getHours())}:${pad(at.getMinutes())}`;
100
+ const sameDay = at.getFullYear() === now.getFullYear() && at.getMonth() === now.getMonth() && at.getDate() === now.getDate();
101
+ if (sameDay)
102
+ return time;
103
+ const date = `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
104
+ if (at.getFullYear() === now.getFullYear())
105
+ return `${date.slice(5)} ${time}`;
106
+ return `${date} ${time}`;
107
+ }
108
+ export function formatActivity(activity, options) {
109
+ const actor = options.actorName(activity.actor);
110
+ if (activity.kind === 'accept')
111
+ return options.t('activityAccepted', { actor });
112
+ if (activity.kind === 'close')
113
+ return options.t('activityClosed', { actor });
114
+ if (activity.kind === 'reopen')
115
+ return options.t('activityReopened', { actor });
116
+ const direction = 'claimRef' in activity
117
+ ? options.claims.find(claim => claim.claimRef === activity.claimRef)?.direction ?? options.t('claims')
118
+ : options.t('claims');
119
+ if (activity.kind === 'claim')
120
+ return options.t('activityClaimed', { actor, direction });
121
+ if (activity.kind === 'done')
122
+ return options.t('activityClaimDone', { actor, direction });
123
+ if (activity.kind === 'release')
124
+ return options.t('activityClaimReleased', { actor, direction });
125
+ if (activity.kind === 'claims_released')
126
+ return options.t('activityClaimsReleased', { actor, count: activity.claimRefs.length });
127
+ throw new Error(`unknown Team Activity kind: ${activity.kind}`);
128
+ }
@@ -0,0 +1,25 @@
1
+ import type { AgentTeamChannelRef, AgentTeamJoinChannelRequest, AgentTeamJoinChannelResult, AgentTeamMemberId, AgentTeamRemoveChannelMemberRequest, AgentTeamRemoveChannelMemberResult } from '@wowyuarm/dsh-agent-team/types';
2
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
3
+ import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
4
+ /** One idempotent membership intent: `joined` rows leave, others join. */
5
+ export interface ChannelMembershipChange {
6
+ readonly workspaceId: WorkspaceId;
7
+ readonly channelRef: AgentTeamChannelRef;
8
+ readonly memberId: AgentTeamMemberId;
9
+ readonly joined: boolean;
10
+ }
11
+ export interface ChannelMembershipTransport {
12
+ readonly joinChannel: (request: AgentTeamJoinChannelRequest) => Promise<RemoteResult<AgentTeamJoinChannelResult>>;
13
+ readonly removeChannelMember: (request: AgentTeamRemoveChannelMemberRequest) => Promise<RemoteResult<AgentTeamRemoveChannelMemberResult>>;
14
+ }
15
+ /**
16
+ * Shared Channel membership mutation. One stable requestId per direction,
17
+ * Member, and Channel survives transport failures until the Host commits it;
18
+ * rows observe pending flags and error text keyed by `rowKeyOf`.
19
+ */
20
+ export declare function useChannelMembership(transport: ChannelMembershipTransport, rowKeyOf: (change: ChannelMembershipChange) => string, onCommitted: (change: ChannelMembershipChange) => void | Promise<void>): {
21
+ readonly pending: ReadonlySet<string>;
22
+ readonly errors: ReadonlyMap<string, string>;
23
+ readonly change: (change: ChannelMembershipChange) => Promise<void>;
24
+ };
25
+ //# sourceMappingURL=team-membership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-membership.d.ts","sourceRoot":"","sources":["../../../src/client/team-membership.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,mCAAmC,EACnC,kCAAkC,EAEnC,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,OAAO,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC,CAAA;IACjH,QAAQ,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,OAAO,CAAC,YAAY,CAAC,kCAAkC,CAAC,CAAC,CAAA;CAC1I;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,MAAM,EACrD,WAAW,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACrE;IACD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5C,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpE,CA6BA"}
@@ -0,0 +1,39 @@
1
+ import { useRef, useState } from 'react';
2
+ /**
3
+ * Shared Channel membership mutation. One stable requestId per direction,
4
+ * Member, and Channel survives transport failures until the Host commits it;
5
+ * rows observe pending flags and error text keyed by `rowKeyOf`.
6
+ */
7
+ export function useChannelMembership(transport, rowKeyOf, onCommitted) {
8
+ const [pending, setPending] = useState(new Set());
9
+ const [errors, setErrors] = useState(new Map());
10
+ const requestIds = useRef(new Map());
11
+ const change = async (membership) => {
12
+ const rowKey = rowKeyOf(membership);
13
+ if (pending.has(rowKey))
14
+ return;
15
+ setPending(current => new Set(current).add(rowKey));
16
+ setErrors(current => { const next = new Map(current); next.delete(rowKey); return next; });
17
+ const key = `${membership.joined ? 'remove' : 'join'}:${membership.memberId}:${membership.channelRef}`;
18
+ const requestId = requestIds.current.get(key) ?? crypto.randomUUID();
19
+ requestIds.current.set(key, requestId);
20
+ const request = { requestId, workspaceId: membership.workspaceId, channelRef: membership.channelRef, memberId: membership.memberId };
21
+ try {
22
+ const result = membership.joined ? await transport.removeChannelMember(request) : await transport.joinChannel(request);
23
+ if (result.ok) {
24
+ requestIds.current.delete(key);
25
+ await onCommitted(membership);
26
+ }
27
+ else {
28
+ setErrors(current => new Map(current).set(rowKey, result.error.message));
29
+ }
30
+ }
31
+ catch (cause) {
32
+ setErrors(current => new Map(current).set(rowKey, cause instanceof Error ? cause.message : String(cause)));
33
+ }
34
+ finally {
35
+ setPending(current => { const next = new Set(current); next.delete(rowKey); return next; });
36
+ }
37
+ };
38
+ return { pending, errors, change };
39
+ }
@@ -0,0 +1,23 @@
1
+ /** One rendered timeline block: a same-sender run or an injected day marker. */
2
+ export type TimelineBlock<T> = {
3
+ readonly kind: 'run';
4
+ readonly items: readonly T[];
5
+ } | {
6
+ readonly kind: 'day';
7
+ readonly label: string;
8
+ };
9
+ /** Local-calendar day key for one wall-clock instant; shared by bespoke loops. */
10
+ export declare function timelineDayKey(occurredAt: string): string;
11
+ /**
12
+ * Numeric day label matching the message-time convention: MM-DD within the
13
+ * current year, full YYYY-MM-DD across years.
14
+ */
15
+ export declare function daySeparatorLabel(occurredAt: string, now?: Date): string;
16
+ /**
17
+ * Chunk one ordered timeline into same-sender runs, breaking a run at every
18
+ * calendar-day change so identity chrome restarts across days. Items without a
19
+ * wall-clock instant (activities) inherit the preceding message's day and never
20
+ * trigger a boundary; callers interleave activity rows between returned blocks.
21
+ */
22
+ export declare function chunkRunsWithDays<T>(items: readonly T[], senderOf: (item: T) => string | undefined, occurredAtOf: (item: T) => string | undefined): readonly TimelineBlock<T>[];
23
+ //# sourceMappingURL=team-separators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-separators.d.ts","sourceRoot":"","sources":["../../../src/client/team-separators.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,aAAa,CAAC,CAAC,IACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAIpD,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,MAAM,CAG9E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,SAAS,aAAa,CAAC,CAAC,CAAC,EAAE,CA0B/K"}
@@ -0,0 +1,52 @@
1
+ const pad = (value) => String(value).padStart(2, '0');
2
+ /** Local-calendar day key for one wall-clock instant; shared by bespoke loops. */
3
+ export function timelineDayKey(occurredAt) {
4
+ return localDateKey(occurredAt);
5
+ }
6
+ function localDateKey(occurredAt) {
7
+ const at = new Date(occurredAt);
8
+ return `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
9
+ }
10
+ /**
11
+ * Numeric day label matching the message-time convention: MM-DD within the
12
+ * current year, full YYYY-MM-DD across years.
13
+ */
14
+ export function daySeparatorLabel(occurredAt, now = new Date()) {
15
+ const key = localDateKey(occurredAt);
16
+ return key.startsWith(String(now.getFullYear())) ? key.slice(5) : key;
17
+ }
18
+ /**
19
+ * Chunk one ordered timeline into same-sender runs, breaking a run at every
20
+ * calendar-day change so identity chrome restarts across days. Items without a
21
+ * wall-clock instant (activities) inherit the preceding message's day and never
22
+ * trigger a boundary; callers interleave activity rows between returned blocks.
23
+ */
24
+ export function chunkRunsWithDays(items, senderOf, occurredAtOf) {
25
+ const blocks = [];
26
+ let lastDate;
27
+ for (const item of items) {
28
+ const occurredAt = occurredAtOf(item);
29
+ if (occurredAt === undefined) {
30
+ blocks.push({ kind: 'run', items: [item] });
31
+ continue;
32
+ }
33
+ const date = localDateKey(occurredAt);
34
+ if (lastDate !== undefined && date !== lastDate) {
35
+ blocks.push({ kind: 'day', label: daySeparatorLabel(occurredAt) });
36
+ // The boundary also ends any open same-sender run before it.
37
+ lastDate = date;
38
+ }
39
+ else if (lastDate === undefined) {
40
+ lastDate = date;
41
+ }
42
+ let last = blocks[blocks.length - 1];
43
+ if (last?.kind === 'run' && senderOf(last.items[last.items.length - 1]) === senderOf(item)) {
44
+ last = { kind: 'run', items: [...last.items, item] };
45
+ blocks[blocks.length - 1] = last;
46
+ }
47
+ else {
48
+ blocks.push({ kind: 'run', items: [item] });
49
+ }
50
+ }
51
+ return blocks;
52
+ }
@@ -0,0 +1,18 @@
1
+ export interface TimelineScroll {
2
+ readonly ref: React.RefObject<HTMLElement>;
3
+ onScroll: () => void;
4
+ /** Whether the reader currently sits within the follow margin of the bottom. */
5
+ isPinned: () => boolean;
6
+ /** Jump to the unread-boundary marker on the next content commit. */
7
+ jumpToBoundary: () => void;
8
+ /** Jump to the latest fact on the next content commit. */
9
+ jumpToLatest: () => void;
10
+ }
11
+ /**
12
+ * Chat-timeline scroll policy shared by the Channel and Thread pages: follow
13
+ * new facts only while the reader stays pinned to the bottom, keep prepended
14
+ * history visually stable, and honor explicit jump requests exactly once per
15
+ * content commit. The content key must change whenever rendered facts change.
16
+ */
17
+ export declare function useTimelineScroll(contentKey: string): TimelineScroll;
18
+ //# sourceMappingURL=timeline-scroll.d.ts.map