@wowyuarm/dsh-agent-team 0.1.10 → 0.1.12

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 (135) hide show
  1. package/README.md +17 -4
  2. package/README.zh.md +17 -4
  3. package/package.json +53 -47
  4. package/packages/agent-team/README.md +6 -4
  5. package/packages/agent-team/README.zh.md +6 -4
  6. package/packages/agent-team/lib/context-management.js +40 -50
  7. package/packages/agent-team/lib/context-projection.js +15 -1
  8. package/packages/agent-team/lib/index.js +244 -154
  9. package/packages/agent-team/lib/invariant.js +38 -4
  10. package/packages/agent-team/lib/ledger.js +775 -383
  11. package/packages/agent-team/lib/member-context.js +6 -2
  12. package/packages/agent-team/lib/member-runtime.js +47 -9
  13. package/packages/agent-team/lib/member-time-context.js +20 -5
  14. package/packages/agent-team/lib/mentions.js +56 -0
  15. package/packages/agent-team/lib/pressure-policy.js +33 -9
  16. package/packages/agent-team/lib/session-event-cursor.js +116 -0
  17. package/packages/agent-team/lib/session-remediation.js +38 -26
  18. package/packages/agent-team/lib/spec.js +47 -34
  19. package/packages/agent-team/lib/stored-session-reader.js +138 -0
  20. package/packages/agent-team/lib/typert.host.js +157 -41
  21. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  22. package/packages/agent-team/lib/typert.remote-client.js +129 -32
  23. package/packages/agent-team/lib/types/context-management.d.ts +7 -0
  24. package/packages/agent-team/lib/types/context-management.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/context-projection.d.ts +8 -0
  26. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  27. package/packages/agent-team/lib/types/index.d.ts +77 -7
  28. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  29. package/packages/agent-team/lib/types/invariant.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/ledger.d.ts +251 -20
  31. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/member-runtime.d.ts +2 -1
  34. package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
  35. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/mentions.d.ts +34 -0
  37. package/packages/agent-team/lib/types/mentions.d.ts.map +1 -0
  38. package/packages/agent-team/lib/types/pressure-policy.d.ts +18 -0
  39. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/session-event-cursor.d.ts +119 -0
  41. package/packages/agent-team/lib/types/session-event-cursor.d.ts.map +1 -0
  42. package/packages/agent-team/lib/types/session-remediation.d.ts +23 -0
  43. package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -1
  44. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  45. package/packages/agent-team/lib/types/stored-session-reader.d.ts +92 -0
  46. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -0
  47. package/packages/agent-team/lib/types/types/entities.d.ts +28 -20
  48. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  49. package/packages/agent-team/lib/types/types/operations.d.ts +45 -19
  50. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
  51. package/packages/agent-team/lib/types/types/requests-results.d.ts +87 -4
  52. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  53. package/packages/agent-team/preset/team-member/agent.cordis.yml +49 -7
  54. package/packages/client-agent-team/README.md +1 -1
  55. package/packages/client-agent-team/README.zh.md +1 -1
  56. package/packages/client-agent-team/lib/client.js +2357 -1207
  57. package/packages/client-agent-team/lib/client.js.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +27 -7
  60. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +18 -0
  61. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -0
  62. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +22 -0
  63. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
  64. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  65. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +167 -35
  66. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +23 -27
  68. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +11 -5
  69. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/TeamComposer.js +26 -26
  71. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  72. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  73. package/packages/client-agent-team/lib/types/client/TeamConversation.js +9 -2
  74. package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts +20 -0
  75. package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts.map +1 -0
  76. package/packages/client-agent-team/lib/types/client/TeamCountBadge.js +25 -0
  77. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +25 -0
  78. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -0
  79. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +139 -0
  80. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +8 -24
  82. package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts +43 -0
  83. package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts.map +1 -0
  84. package/packages/client-agent-team/lib/types/client/TeamMemberRow.js +27 -0
  85. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +2 -2
  86. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -1
  87. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
  88. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +9 -2
  89. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  90. package/packages/client-agent-team/lib/types/client/TeamMessage.js +11 -4
  91. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +8 -0
  92. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  93. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +21 -1
  94. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  95. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +108 -30
  96. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  97. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  98. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +68 -7
  99. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  100. package/packages/client-agent-team/lib/types/client/index.js +19 -9
  101. package/packages/client-agent-team/lib/types/client/locales.d.ts +40 -2
  102. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  103. package/packages/client-agent-team/lib/types/client/locales.js +40 -2
  104. package/packages/client-agent-team/lib/types/client/navigation.d.ts +10 -0
  105. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  106. package/packages/client-agent-team/lib/types/client/navigation.js +18 -7
  107. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +41 -0
  108. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +1 -0
  109. package/packages/client-agent-team/lib/types/client/scope-coverage.js +64 -0
  110. package/packages/client-agent-team/lib/types/client/slots.d.ts +7 -2
  111. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  112. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +33 -2
  113. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
  114. package/packages/client-agent-team/lib/types/client/team-changes.js +107 -25
  115. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts +20 -0
  116. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts.map +1 -0
  117. package/packages/client-agent-team/lib/types/client/team-dialog-save.js +36 -0
  118. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +43 -1
  119. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  120. package/packages/client-agent-team/lib/types/client/team-formatters.js +81 -6
  121. package/packages/tool-agent-team/README.md +2 -2
  122. package/packages/tool-agent-team/README.zh.md +2 -2
  123. package/packages/tool-agent-team/lib/context-tools.js +18 -14
  124. package/packages/tool-agent-team/lib/host-access.js +20 -0
  125. package/packages/tool-agent-team/lib/index.js +44 -32
  126. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  127. package/packages/tool-agent-team/lib/types/host-access.d.ts +14 -0
  128. package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -0
  129. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  130. package/packages/agent-team/lib/progress-nudge.js +0 -328
  131. package/packages/agent-team/lib/types/progress-nudge.d.ts +0 -136
  132. package/packages/agent-team/lib/types/progress-nudge.d.ts.map +0 -1
  133. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +0 -3
  134. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +0 -1
  135. package/packages/client-agent-team/lib/types/client/TeamSettings.js +0 -5
@@ -1,13 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
3
- import { IconAgentPresetOutline16, IconListPenOutline16, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { IconAgentPresetOutline16, IconListPenOutline16, IconQueueOutline14, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamWorkspaceRow } from "./TeamWorkspaceRow.js";
5
5
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
6
6
  import { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
7
7
  import { TeamAgentsPanel } from "./TeamAgentsPanel.js";
8
8
  import { TeamChannelsPanel } from "./TeamChannelsPanel.js";
9
9
  import css from './sidebar.module.css';
10
- export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, archiveChannel, updateMember, recoverMember, archiveMember, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
10
+ /**
11
+ * The Inbox entry's icon with its unread mark. The quantity left this surface
12
+ * and lives in the control's accessible name: the sidebar answers whether
13
+ * anything is waiting at a glance, and the number — which moves on every fact —
14
+ * is what a reader asks for on purpose. The mark hangs off the icon rather than
15
+ * off the control, so the wide card and the 36px rail button put the same dot
16
+ * on the same corner of the same glyph.
17
+ */
18
+ function InboxMark({ unread }) {
19
+ return _jsxs("span", { className: css.inboxMark, children: [_jsx(IconQueueOutline14, { size: 16 }), unread > 0 && _jsx("span", { className: css.inboxDot, "data-team-inbox-dot": true, "aria-hidden": "true" })] });
20
+ }
21
+ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, selectInbox, t, useWorkspaces, loadMembers, loadInbox, subscribeChanges, subscribeReads, addMember, loadChannels, createChannel, updateChannel, archiveChannel, updateMember, recoverMember, archiveMember, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
11
22
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
12
23
  const workspaces = useWorkspaces(state => state.items);
13
24
  const selected = navigationState.workspaceId;
@@ -16,19 +27,69 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
16
27
  // Exactly one sidebar row carries aria-current='page': the open Channel while
17
28
  // one is set, the selected Agent card while a Member Session view is open,
18
29
  // otherwise the browsed Workspace's overview. The selected row keeps its
19
- // quiet folder tint (data-selected) in every case.
20
- const overviewIsCurrent = navigationState.channelRef === undefined && navigationState.memberSessionId === undefined;
30
+ // quiet folder tint (data-selected) in every case. The mention-Inbox card
31
+ // takes the marker while the Inbox page stands, and stands down while the
32
+ // embedded Member Session covers that page: the Inbox stays the remembered
33
+ // face underneath — closing the overlay returns to it, marker included —
34
+ // but the card is not a second current page while an Agent holds the seat.
35
+ const overviewIsCurrent = navigationState.channelRef === undefined && navigationState.memberSessionId === undefined && navigationState.inbox !== true;
36
+ const inboxIsCurrent = navigationState.inbox === true && navigationState.memberSessionId === undefined;
21
37
  const [creatingAgents, setCreatingAgents] = useState([]);
22
38
  // Rail icons request expansion and name the section to reveal once wide.
23
39
  const [pendingSection, setPendingSection] = useState();
24
40
  const channelsRef = useRef(null);
25
41
  const agentsRef = useRef(null);
26
42
  const workspacesOpen = useSidebarSectionOpen(undefined, 'workspaces');
43
+ // The cross-Workspace Inbox badge: one scope-less subscription while the
44
+ // Team sidebar stands; every wake re-pulls each Workspace's unread total
45
+ // (limit 1 — totals cover every row, never the list). Same-burst wakes
46
+ // coalesce behind a short debounce; the number is whatever the Host's whole
47
+ // unread slice returns, mentions included rather than alone.
48
+ const [inboxTotal, setInboxTotal] = useState(0);
49
+ // One name for both entries: what the control is, and how much waits behind
50
+ // it. The rail repeats it as its hover hint, which is the only place a reader
51
+ // still meets the quantity without opening the page.
52
+ const inboxLabel = inboxTotal > 0 ? t('inboxTitleWithCount', { count: inboxTotal }) : t('inboxTitle');
53
+ useEffect(() => {
54
+ let disposed = false;
55
+ let scheduled;
56
+ const refresh = async () => {
57
+ const results = await Promise.all(workspaces.map(workspace => loadInbox({ workspaceId: workspace.workspaceId, limit: 1 })));
58
+ if (disposed)
59
+ return;
60
+ setInboxTotal(results.reduce((sum, result) => result.ok ? sum + result.value.totalUnreadCount : sum, 0));
61
+ };
62
+ const schedule = () => {
63
+ if (scheduled !== undefined)
64
+ return;
65
+ scheduled = setTimeout(() => { scheduled = undefined; void refresh(); }, 200);
66
+ };
67
+ void refresh();
68
+ const unsubscribe = subscribeChanges(undefined, update => {
69
+ if (update.type === 'changed')
70
+ schedule();
71
+ });
72
+ // A durable Thread read consumes this reader's mention markers without a
73
+ // changes wake (reads change no shared projection), so the badge also
74
+ // refreshes from every completed read.
75
+ const unsubscribeReads = subscribeReads(schedule);
76
+ return () => {
77
+ disposed = true;
78
+ if (scheduled !== undefined)
79
+ clearTimeout(scheduled);
80
+ unsubscribe();
81
+ unsubscribeReads();
82
+ };
83
+ }, [loadInbox, subscribeChanges, subscribeReads, workspaces]);
27
84
  useEffect(() => {
85
+ // The Inbox page is global and needs no selected Workspace, so the
86
+ // auto-select must not yank the seat back to a Workspace overview.
87
+ if (navigationState.inbox === true)
88
+ return;
28
89
  if (navigationState.mode === 'team' && selectedId !== undefined && selectedId !== selected) {
29
90
  selectWorkspace(selectedId);
30
91
  }
31
- }, [navigationState.mode, selected, selectedId, selectWorkspace]);
92
+ }, [navigationState.inbox, navigationState.mode, selected, selectedId, selectWorkspace]);
32
93
  useEffect(() => {
33
94
  if (!wide || pendingSection === undefined)
34
95
  return;
@@ -37,7 +98,7 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
37
98
  queueMicrotask(() => { node?.querySelector('button')?.focus(); });
38
99
  }, [wide, pendingSection]);
39
100
  if (!wide) {
40
- return _jsxs("nav", { className: css.railWorkspace, "aria-label": t('workspaceSections'), children: [_jsx(Tooltip, { label: t('channels'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('channels'), onClick: () => { setPendingSection('channels'); expandSidebar(); }, children: _jsx(IconListPenOutline16, { size: 16 }) }) }), _jsx(Tooltip, { label: t('agents'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('agents'), onClick: () => { setPendingSection('agents'); expandSidebar(); }, children: _jsx(IconAgentPresetOutline16, { size: 16 }) }) })] });
101
+ return _jsxs("nav", { className: css.railWorkspace, "aria-label": t('workspaceSections'), children: [_jsx(Tooltip, { label: inboxLabel, side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": inboxLabel, "aria-current": inboxIsCurrent ? 'page' : undefined, onClick: () => { selectInbox(); expandSidebar(); }, children: _jsx(InboxMark, { unread: inboxTotal }) }) }), _jsx(Tooltip, { label: t('channels'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('channels'), onClick: () => { setPendingSection('channels'); expandSidebar(); }, children: _jsx(IconListPenOutline16, { size: 16 }) }) }), _jsx(Tooltip, { label: t('agents'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('agents'), onClick: () => { setPendingSection('agents'); expandSidebar(); }, children: _jsx(IconAgentPresetOutline16, { size: 16 }) }) })] });
41
102
  }
42
- return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx(TeamSidebarSection, { title: t('workspaces'), open: workspacesOpen, onToggle: open => { setSidebarSectionOpen(undefined, 'workspaces', open); }, children: _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }) }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, archiveChannel: archiveChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, archiveMember: archiveMember, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
103
+ return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsxs("button", { type: "button", className: css.inboxCard, "aria-label": inboxLabel, "aria-current": inboxIsCurrent ? 'page' : undefined, onClick: selectInbox, children: [_jsx(InboxMark, { unread: inboxTotal }), _jsx("span", { className: css.inboxCardLabel, children: t('inboxTitle') })] }), _jsx(TeamSidebarSection, { title: t('workspaces'), open: workspacesOpen, onToggle: open => { setSidebarSectionOpen(undefined, 'workspaces', open); }, children: _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }) }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, archiveChannel: archiveChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, archiveMember: archiveMember, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
43
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoCnE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAK5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAmLD,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoCnE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAK5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AA6LD,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
@@ -1,9 +1,9 @@
1
- import agentTeamRemote from '../../../agent-team/lib/typert.remote-client.js';
1
+ import agentTeamRemote from '@wowyuarm/dsh-agent-team/remote';
2
2
  import { TeamNavigation } from "./navigation.js";
3
- import { TeamChangeStream } from "./team-changes.js";
3
+ import { TeamChangeStream, TeamReadStream } from "./team-changes.js";
4
4
  import { TeamDraftStore } from "./drafts.js";
5
5
  import { TeamFooterAction } from "./TeamFooterAction.js";
6
- import { TeamSettings } from "./TeamSettings.js";
6
+ import { TeamMembersAction } from "./TeamMembersAction.js";
7
7
  import { TeamConversation } from "./TeamConversation.js";
8
8
  import { TeamWorkspaceBrowser } from "./TeamWorkspaceBrowser.js";
9
9
  import { en, zh } from "./locales.js";
@@ -12,7 +12,7 @@ const NS = 'team';
12
12
  export const inject = [
13
13
  'slots', 'workspaces', 'locale', 'remote', 'remote.session', 'sessions', 'connection', 'conversation',
14
14
  ];
15
- function registerModeShadow(ctx, navigation, changes, drafts, name, component, extraInject,
15
+ function registerModeShadow(ctx, navigation, changes, reads, drafts, name, component, extraInject,
16
16
  // Keyed seats (`main`) address one panel by key; the reserved 'conversation'
17
17
  // key is where the shipped Conversation registers, so the Team seat shadows
18
18
  // that same panel instead of adding a second one.
@@ -33,6 +33,8 @@ entryKey) {
33
33
  // Remote bindings shared by every Team slot; surface-specific entries extend it below.
34
34
  const sharedRemotes = {
35
35
  loadChannels: (request) => ctx.remote.agentTeam.view(request),
36
+ loadInbox: (request) => ctx.remote.agentTeam.inbox(request),
37
+ subscribeReads: (listener) => reads.subscribe(listener),
36
38
  subscribeChanges: (scope, listener) => changes.subscribe(scope, listener),
37
39
  drafts,
38
40
  loadMembers: (request) => ctx.remote.agentTeam.members(request),
@@ -68,7 +70,15 @@ entryKey) {
68
70
  ...navigation.actions(),
69
71
  ...sharedRemotes,
70
72
  ...(name === 'main' ? {
71
- readThread: (request) => ctx.remote.agentTeam.readThread(request),
73
+ // A committed durable read consumes this reader's mention
74
+ // markers; the Host's changes stream never wakes on reads, so
75
+ // the Human's badge refreshes from the completed read itself.
76
+ readThread: async (request) => {
77
+ const result = await ctx.remote.agentTeam.readThread(request);
78
+ if (result.ok)
79
+ reads.bump();
80
+ return result;
81
+ },
72
82
  loadThreadHistory: (request) => ctx.remote.agentTeam.threadHistory(request),
73
83
  threadObservations: (request) => ctx.remote.agentTeam.threadObservations(request),
74
84
  sendMessage: (request) => ctx.remote.agentTeam.sendMessage(request),
@@ -143,6 +153,7 @@ function applyUi(ctx) {
143
153
  };
144
154
  }, 'agent-team: member session restore');
145
155
  const changes = new TeamChangeStream((request, signal) => ctx.remote.agentTeam.changes(request, signal));
156
+ const reads = new TeamReadStream();
146
157
  const loadMemberGroups = async () => {
147
158
  const workspaces = ctx.workspaces.list.getSnapshot().items;
148
159
  const groups = await Promise.all(workspaces.map(async (workspace) => {
@@ -172,12 +183,11 @@ function applyUi(ctx) {
172
183
  navigation.actions().exitMemberSession();
173
184
  navigation.actions().leaveTeam();
174
185
  },
175
- loadMemberGroups,
176
186
  }),
177
187
  }, TeamFooterAction));
178
- registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.workspaces', TeamWorkspaceBrowser);
179
- registerModeShadow(ctx, navigation, changes, drafts, 'main', TeamConversation, undefined, 'conversation');
180
- registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.settings', TeamSettings, () => ({ loadMemberGroups }));
188
+ registerModeShadow(ctx, navigation, changes, reads, drafts, 'sidebar.workspaces', TeamWorkspaceBrowser);
189
+ registerModeShadow(ctx, navigation, changes, reads, drafts, 'main', TeamConversation, undefined, 'conversation');
190
+ registerModeShadow(ctx, navigation, changes, reads, drafts, 'sidebar.settings', TeamMembersAction, () => ({ loadMemberGroups }));
181
191
  }
182
192
  export async function apply(ctx) {
183
193
  const disposeRemote = await ctx.remote.$mount(agentTeamRemote);
@@ -27,6 +27,20 @@ export declare const zh: {
27
27
  readonly statusError: "错误";
28
28
  readonly statusUnavailable: "不可用";
29
29
  readonly addChannel: "新建频道";
30
+ readonly inboxTitle: "收件箱";
31
+ readonly inboxTitleWithCount: "收件箱,{count} 条未读";
32
+ readonly loadingInbox: "正在加载收件箱…";
33
+ readonly inboxEmptyTitle: "收件箱是空的";
34
+ readonly inboxEmptyHint: "你参与的 Thread 有新活动、或有人提到你时,会出现在这里";
35
+ readonly inboxTimeYesterday: "昨天";
36
+ readonly inboxHeaderThreads: "{count} 个 Thread";
37
+ readonly inboxHeaderUnread: "{count} 条未读";
38
+ readonly inboxHeaderMentions: "{count} 条提及";
39
+ readonly inboxRowUnread: "{count} 条未读";
40
+ readonly inboxRowUnreadMentions: "{count} 条未读,其中 {mentions} 条提及";
41
+ readonly inboxRowActor: "最新来自 {name}";
42
+ readonly inboxSectionNeedsMe: "需要我";
43
+ readonly inboxSectionRecent: "最近活跃";
30
44
  readonly channelName: "名称";
31
45
  readonly channelDescription: "说明";
32
46
  readonly initialMembers: "初始成员";
@@ -40,6 +54,7 @@ export declare const zh: {
40
54
  readonly resumeAgent: "恢复";
41
55
  readonly restartAgent: "重启";
42
56
  readonly restartStillUnavailable: "重启已执行,成员仍不可用:{diagnostic}";
57
+ readonly restartRefusedNoRestart: "会话日志被确定拒绝,修复无可用项,重启无法解决:{detail}";
43
58
  readonly restartFailed: "重启执行失败:{message}";
44
59
  readonly archiveAgent: "归档";
45
60
  readonly archiveAgentTitle: "归档 Agent:{name}";
@@ -91,12 +106,16 @@ export declare const zh: {
91
106
  readonly taskStatusInReview: "待验收";
92
107
  readonly taskStatusDone: "已完成";
93
108
  readonly taskStatusClosed: "已关闭";
94
- readonly taskMessageCount: "{count} 条消息";
95
109
  readonly taskLabel: "Task #{number}";
96
110
  readonly taskPending: "Task …";
97
111
  readonly openTask: "打开 Task #{number}";
98
112
  readonly openThread: "打开讨论";
113
+ readonly openTaskUnread: "打开 Task #{number}({count} 条新动态)";
114
+ readonly openThreadUnread: "打开讨论({count} 条新动态)";
99
115
  readonly threadLabel: "讨论";
116
+ readonly recentActivity: "最近活动 {time}";
117
+ readonly replyAction: "回复";
118
+ readonly claimers: "由 {names} 处理";
100
119
  readonly asTask: "作为任务";
101
120
  readonly promoteToTask: "转为 Task";
102
121
  readonly promotingTask: "正在转为 Task…";
@@ -175,6 +194,20 @@ export declare const en: {
175
194
  readonly statusError: "Error";
176
195
  readonly statusUnavailable: "Unavailable";
177
196
  readonly addChannel: "New Channel";
197
+ readonly inboxTitle: "Inbox";
198
+ readonly inboxTitleWithCount: "Inbox, {count} unread";
199
+ readonly loadingInbox: "Loading Inbox…";
200
+ readonly inboxEmptyTitle: "Your Inbox is empty";
201
+ readonly inboxEmptyHint: "Threads you take part in appear here when they have new activity or mention you";
202
+ readonly inboxTimeYesterday: "Yesterday";
203
+ readonly inboxHeaderThreads: "{count} Thread(s)";
204
+ readonly inboxHeaderUnread: "{count} unread";
205
+ readonly inboxHeaderMentions: "{count} mention(s)";
206
+ readonly inboxRowUnread: "{count} unread";
207
+ readonly inboxRowUnreadMentions: "{count} unread, {mentions} mention(s)";
208
+ readonly inboxRowActor: "Latest from {name}";
209
+ readonly inboxSectionNeedsMe: "Needs me";
210
+ readonly inboxSectionRecent: "Recently active";
178
211
  readonly channelName: "Name";
179
212
  readonly channelDescription: "Description";
180
213
  readonly initialMembers: "Initial members";
@@ -188,6 +221,7 @@ export declare const en: {
188
221
  readonly resumeAgent: "Resume";
189
222
  readonly restartAgent: "Restart";
190
223
  readonly restartStillUnavailable: "Restart ran, but the Member is still unavailable: {diagnostic}";
224
+ readonly restartRefusedNoRestart: "The session log is deterministically refused with nothing repairable; restarting cannot fix it: {detail}";
191
225
  readonly restartFailed: "Restart failed: {message}";
192
226
  readonly archiveAgent: "Archive";
193
227
  readonly archiveAgentTitle: "Archive Agent: {name}";
@@ -239,12 +273,16 @@ export declare const en: {
239
273
  readonly taskStatusInReview: "In review";
240
274
  readonly taskStatusDone: "Done";
241
275
  readonly taskStatusClosed: "Closed";
242
- readonly taskMessageCount: "{count} messages";
243
276
  readonly taskLabel: "Task #{number}";
244
277
  readonly taskPending: "Task …";
245
278
  readonly openTask: "Open Task #{number}";
246
279
  readonly openThread: "Open thread";
280
+ readonly openTaskUnread: "Open Task #{number} ({count} new updates)";
281
+ readonly openThreadUnread: "Open thread ({count} new updates)";
247
282
  readonly threadLabel: "Thread";
283
+ readonly recentActivity: "Last activity {time}";
284
+ readonly replyAction: "Reply";
285
+ readonly claimers: "Handled by {names}";
248
286
  readonly asTask: "As task";
249
287
  readonly promoteToTask: "Convert to Task";
250
288
  readonly promotingTask: "Converting to Task…";
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsKL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsKL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
@@ -27,6 +27,20 @@ export const zh = {
27
27
  statusError: '错误',
28
28
  statusUnavailable: '不可用',
29
29
  addChannel: '新建频道',
30
+ inboxTitle: '收件箱',
31
+ inboxTitleWithCount: '收件箱,{count} 条未读',
32
+ loadingInbox: '正在加载收件箱…',
33
+ inboxEmptyTitle: '收件箱是空的',
34
+ inboxEmptyHint: '你参与的 Thread 有新活动、或有人提到你时,会出现在这里',
35
+ inboxTimeYesterday: '昨天',
36
+ inboxHeaderThreads: '{count} 个 Thread',
37
+ inboxHeaderUnread: '{count} 条未读',
38
+ inboxHeaderMentions: '{count} 条提及',
39
+ inboxRowUnread: '{count} 条未读',
40
+ inboxRowUnreadMentions: '{count} 条未读,其中 {mentions} 条提及',
41
+ inboxRowActor: '最新来自 {name}',
42
+ inboxSectionNeedsMe: '需要我',
43
+ inboxSectionRecent: '最近活跃',
30
44
  channelName: '名称',
31
45
  channelDescription: '说明',
32
46
  initialMembers: '初始成员',
@@ -40,6 +54,7 @@ export const zh = {
40
54
  resumeAgent: '恢复',
41
55
  restartAgent: '重启',
42
56
  restartStillUnavailable: '重启已执行,成员仍不可用:{diagnostic}',
57
+ restartRefusedNoRestart: '会话日志被确定拒绝,修复无可用项,重启无法解决:{detail}',
43
58
  restartFailed: '重启执行失败:{message}',
44
59
  archiveAgent: '归档',
45
60
  archiveAgentTitle: '归档 Agent:{name}',
@@ -91,12 +106,16 @@ export const zh = {
91
106
  taskStatusInReview: '待验收',
92
107
  taskStatusDone: '已完成',
93
108
  taskStatusClosed: '已关闭',
94
- taskMessageCount: '{count} 条消息',
95
109
  taskLabel: 'Task #{number}',
96
110
  taskPending: 'Task …',
97
111
  openTask: '打开 Task #{number}',
98
112
  openThread: '打开讨论',
113
+ openTaskUnread: '打开 Task #{number}({count} 条新动态)',
114
+ openThreadUnread: '打开讨论({count} 条新动态)',
99
115
  threadLabel: '讨论',
116
+ recentActivity: '最近活动 {time}',
117
+ replyAction: '回复',
118
+ claimers: '由 {names} 处理',
100
119
  asTask: '作为任务',
101
120
  promoteToTask: '转为 Task',
102
121
  promotingTask: '正在转为 Task…',
@@ -175,6 +194,20 @@ export const en = {
175
194
  statusError: 'Error',
176
195
  statusUnavailable: 'Unavailable',
177
196
  addChannel: 'New Channel',
197
+ inboxTitle: 'Inbox',
198
+ inboxTitleWithCount: 'Inbox, {count} unread',
199
+ loadingInbox: 'Loading Inbox…',
200
+ inboxEmptyTitle: 'Your Inbox is empty',
201
+ inboxEmptyHint: 'Threads you take part in appear here when they have new activity or mention you',
202
+ inboxTimeYesterday: 'Yesterday',
203
+ inboxHeaderThreads: '{count} Thread(s)',
204
+ inboxHeaderUnread: '{count} unread',
205
+ inboxHeaderMentions: '{count} mention(s)',
206
+ inboxRowUnread: '{count} unread',
207
+ inboxRowUnreadMentions: '{count} unread, {mentions} mention(s)',
208
+ inboxRowActor: 'Latest from {name}',
209
+ inboxSectionNeedsMe: 'Needs me',
210
+ inboxSectionRecent: 'Recently active',
178
211
  channelName: 'Name',
179
212
  channelDescription: 'Description',
180
213
  initialMembers: 'Initial members',
@@ -188,6 +221,7 @@ export const en = {
188
221
  resumeAgent: 'Resume',
189
222
  restartAgent: 'Restart',
190
223
  restartStillUnavailable: 'Restart ran, but the Member is still unavailable: {diagnostic}',
224
+ restartRefusedNoRestart: 'The session log is deterministically refused with nothing repairable; restarting cannot fix it: {detail}',
191
225
  restartFailed: 'Restart failed: {message}',
192
226
  archiveAgent: 'Archive',
193
227
  archiveAgentTitle: 'Archive Agent: {name}',
@@ -239,12 +273,16 @@ export const en = {
239
273
  taskStatusInReview: 'In review',
240
274
  taskStatusDone: 'Done',
241
275
  taskStatusClosed: 'Closed',
242
- taskMessageCount: '{count} messages',
243
276
  taskLabel: 'Task #{number}',
244
277
  taskPending: 'Task …',
245
278
  openTask: 'Open Task #{number}',
246
279
  openThread: 'Open thread',
280
+ openTaskUnread: 'Open Task #{number} ({count} new updates)',
281
+ openThreadUnread: 'Open thread ({count} new updates)',
247
282
  threadLabel: 'Thread',
283
+ recentActivity: 'Last activity {time}',
284
+ replyAction: 'Reply',
285
+ claimers: 'Handled by {names}',
248
286
  asTask: 'As task',
249
287
  promoteToTask: 'Convert to Task',
250
288
  promotingTask: 'Converting to Task…',
@@ -9,6 +9,12 @@ export interface TeamNavigationSnapshot {
9
9
  taskRef?: AgentTeamTaskRef;
10
10
  threadRef?: AgentTeamThreadRef;
11
11
  taskNumber?: number;
12
+ /**
13
+ * Durable position of the Human mention-Inbox page (a navigation fact, not
14
+ * an unread fact): selecting Inbox clears the Channel/Thread faces, and a
15
+ * reload reopens the page. Read markers only move through Thread reads.
16
+ */
17
+ inbox?: boolean;
12
18
  /** Runtime-only Member Session embedded in the conversation seat; never persisted. */
13
19
  memberSessionId?: SessionId;
14
20
  /** Runtime-only session to restore when the Member view closes; never persisted. */
@@ -21,6 +27,8 @@ export interface TeamNavigationActions {
21
27
  selectWorkspace: (workspaceId: WorkspaceId) => void;
22
28
  selectChannel: (channelRef: AgentTeamChannelRef) => void;
23
29
  selectThread: (threadRef: AgentTeamThreadRef, channelRef?: AgentTeamChannelRef, taskRef?: AgentTeamTaskRef, taskNumber?: number) => void;
30
+ /** Open the Human mention-Inbox page; clears the Channel/Thread faces. */
31
+ selectInbox: () => void;
24
32
  backToWorkspace: () => void;
25
33
  /** Leave the selected Channel for the workspace Channel list; keeps mode and Workspace. */
26
34
  backToChannels: () => void;
@@ -47,6 +55,8 @@ export declare class TeamNavigation {
47
55
  private clearMemberSession;
48
56
  private setWorkspace;
49
57
  private setChannel;
58
+ /** The Inbox is a face, not workspace content: selecting a Workspace leaves it. */
59
+ private setInbox;
50
60
  private clearChannel;
51
61
  private setThread;
52
62
  private commit;
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../src/client/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AACnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAE/G,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,QAAQ,CAAA;IACd,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,UAAU,CAAC,EAAE,mBAAmB,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sFAAsF;IACtF,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,QAAA,MAAM,WAAW,8BAA8B,CAAA;AAuC/C,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,eAAe,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAA;IACnD,aAAa,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACxD,YAAY,EAAE,CAAC,SAAS,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACxI,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,2FAA2F;IAC3F,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACjF,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,4EAA4E;AAC5E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAElD,QAAQ,CAAC,WAAW,QAAO,sBAAsB,CAAiB;IAElE,QAAQ,CAAC,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAGxD;IAED,OAAO,IAAI,qBAAqB;IAchC,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,gBAAgB;IAaxB,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,MAAM;CAKf;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../src/client/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AACnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAE/G,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,QAAQ,CAAA;IACd,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,UAAU,CAAC,EAAE,mBAAmB,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,sFAAsF;IACtF,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,QAAA,MAAM,WAAW,8BAA8B,CAAA;AAyC/C,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,eAAe,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAA;IACnD,aAAa,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACxD,YAAY,EAAE,CAAC,SAAS,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACxI,0EAA0E;IAC1E,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,2FAA2F;IAC3F,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACjF,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,4EAA4E;AAC5E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAElD,QAAQ,CAAC,WAAW,QAAO,sBAAsB,CAAiB;IAElE,QAAQ,CAAC,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAGxD;IAED,OAAO,IAAI,qBAAqB;IAehC,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,gBAAgB;IAaxB,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,UAAU;IAOlB,mFAAmF;IACnF,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,MAAM;CAKf;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -9,6 +9,7 @@ function readSnapshot() {
9
9
  mode: parsed.mode === 'team' ? 'team' : 'conversation',
10
10
  ...(typeof parsed.workspaceId === 'string' ? { workspaceId: parsed.workspaceId } : {}),
11
11
  ...(typeof parsed.channelRef === 'string' ? { channelRef: parsed.channelRef } : {}),
12
+ ...(parsed.inbox === true ? { inbox: true } : {}),
12
13
  ...(hasThread ? {
13
14
  threadRef: parsed.threadRef,
14
15
  ...(typeof parsed.taskRef === 'string' ? { taskRef: parsed.taskRef } : {}),
@@ -24,11 +25,12 @@ function persistSnapshot(snapshot) {
24
25
  if (typeof localStorage === 'undefined')
25
26
  return;
26
27
  try {
27
- const { mode, workspaceId, channelRef, taskRef, threadRef, taskNumber } = snapshot;
28
+ const { mode, workspaceId, channelRef, taskRef, threadRef, taskNumber, inbox } = snapshot;
28
29
  localStorage.setItem(STORAGE_KEY, JSON.stringify({
29
30
  mode,
30
31
  ...(workspaceId === undefined ? {} : { workspaceId }),
31
32
  ...(channelRef === undefined ? {} : { channelRef }),
33
+ ...(inbox === true ? { inbox: true } : {}),
32
34
  ...(taskRef === undefined ? {} : { taskRef }),
33
35
  ...(threadRef === undefined ? {} : { threadRef }),
34
36
  ...(taskNumber === undefined ? {} : { taskNumber }),
@@ -54,6 +56,7 @@ export class TeamNavigation {
54
56
  selectWorkspace: workspaceId => { this.clearMemberSession(); this.setWorkspace(workspaceId); },
55
57
  selectChannel: channelRef => { this.clearMemberSession(); this.setChannel(channelRef); },
56
58
  selectThread: (threadRef, channelRef, taskRef, taskNumber) => { this.clearMemberSession(); this.setThread(threadRef, channelRef, taskRef, taskNumber); },
59
+ selectInbox: () => { this.clearMemberSession(); this.setInbox(); },
57
60
  backToWorkspace: () => { this.clearMemberSession(); this.setThread(undefined); },
58
61
  backToChannels: () => { this.clearMemberSession(); this.clearChannel(); },
59
62
  enterMemberSession: (sessionId, returnToSessionId) => { this.setMemberSession(sessionId, returnToSessionId); },
@@ -91,19 +94,27 @@ export class TeamNavigation {
91
94
  this.commit();
92
95
  }
93
96
  setWorkspace(workspaceId) {
94
- if (this.snapshot.workspaceId === workspaceId)
97
+ if (this.snapshot.workspaceId === workspaceId && this.snapshot.inbox !== true)
95
98
  return;
96
- const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
99
+ const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, inbox: _inbox, ...base } = this.snapshot;
97
100
  this.snapshot = { ...base, workspaceId };
98
101
  this.commit();
99
102
  }
100
103
  setChannel(channelRef) {
101
- if (this.snapshot.channelRef === channelRef && this.snapshot.threadRef === undefined)
104
+ if (this.snapshot.channelRef === channelRef && this.snapshot.threadRef === undefined && this.snapshot.inbox !== true)
102
105
  return;
103
- const { taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
106
+ const { taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, inbox: _inbox, ...base } = this.snapshot;
104
107
  this.snapshot = { ...base, channelRef };
105
108
  this.commit();
106
109
  }
110
+ /** The Inbox is a face, not workspace content: selecting a Workspace leaves it. */
111
+ setInbox() {
112
+ if (this.snapshot.inbox === true && this.snapshot.channelRef === undefined && this.snapshot.threadRef === undefined)
113
+ return;
114
+ const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
115
+ this.snapshot = { ...base, inbox: true };
116
+ this.commit();
117
+ }
107
118
  clearChannel() {
108
119
  const { channelRef: _channelRef, ...base } = this.snapshot;
109
120
  if (this.snapshot.channelRef === undefined)
@@ -112,14 +123,14 @@ export class TeamNavigation {
112
123
  this.commit();
113
124
  }
114
125
  setThread(threadRef, channelRef, taskRef, taskNumber) {
115
- if (this.snapshot.threadRef === threadRef && this.snapshot.taskRef === taskRef && this.snapshot.channelRef === channelRef && this.snapshot.taskNumber === taskNumber)
126
+ if (this.snapshot.threadRef === threadRef && this.snapshot.taskRef === taskRef && this.snapshot.channelRef === channelRef && this.snapshot.taskNumber === taskNumber && this.snapshot.inbox !== true)
116
127
  return;
117
128
  if (threadRef === undefined) {
118
129
  const { taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
119
130
  this.snapshot = base;
120
131
  }
121
132
  else {
122
- const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, ...base } = this.snapshot;
133
+ const { channelRef: _channelRef, taskRef: _taskRef, threadRef: _threadRef, taskNumber: _taskNumber, inbox: _inbox, ...base } = this.snapshot;
123
134
  this.snapshot = {
124
135
  ...base,
125
136
  threadRef,
@@ -0,0 +1,41 @@
1
+ /** The change scopes whose wakes one surface answers with a shared refresh round. */
2
+ export type SupplementalScope = 'workspace' | 'presence';
3
+ /** One change wake, carrying a version from the scope that issued it. */
4
+ export interface ScopeWake {
5
+ readonly scope: SupplementalScope;
6
+ readonly version: number;
7
+ }
8
+ /**
9
+ * Per-scope coverage of the change versions one surface has already answered.
10
+ *
11
+ * A version is only comparable inside the scope that issued it: the Host
12
+ * answers a workspace scope from the durable ledger position and a presence
13
+ * scope from its process-local wake epoch, which starts low and climbs on every
14
+ * Agent transition. One high-water mark shared by both would therefore let a
15
+ * presence epoch swallow a later, smaller ledger sequence and park the surface
16
+ * on stale data, so coverage and pending versions are kept per scope.
17
+ *
18
+ * One round answers one version per scope. The Host delivers one event to every
19
+ * matching scope, so the first version a round sees in a scope it has not
20
+ * answered yet is that event reaching a second scope, and joins the round
21
+ * already fetching instead of earning a second identical read. A newer version
22
+ * in a scope the round has answered arrived after the round issued its fetch,
23
+ * so it owes one trailing round.
24
+ */
25
+ export declare class ScopeCoverage {
26
+ private readonly covered;
27
+ private readonly pending;
28
+ /** The scopes the open round has an answer for. */
29
+ private readonly answered;
30
+ private fetching;
31
+ /** Folds one wake in and answers whether it opens a round. */
32
+ wake(scope: SupplementalScope, version: number): boolean;
33
+ /** Opens a round's fetch window; versions observed from here on are pending. */
34
+ beginRound(): void;
35
+ /** Closes a round's fetch window and answers whether a trailing round is owed. */
36
+ finishRound(): boolean;
37
+ /** Drops every observation: a new owner starts with nothing covered. */
38
+ reset(): void;
39
+ private coveredOf;
40
+ }
41
+ //# sourceMappingURL=scope-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-coverage.d.ts","sourceRoot":"","sources":["../../../src/client/scope-coverage.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IACxD,OAAO,CAAC,QAAQ,CAAQ;IAExB,8DAA8D;IAC9D,IAAI,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxD,gFAAgF;IAChF,UAAU,IAAI,IAAI;IAKlB,kFAAkF;IAClF,WAAW,IAAI,OAAO;IAYtB,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,SAAS;CAGlB"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Per-scope coverage of the change versions one surface has already answered.
3
+ *
4
+ * A version is only comparable inside the scope that issued it: the Host
5
+ * answers a workspace scope from the durable ledger position and a presence
6
+ * scope from its process-local wake epoch, which starts low and climbs on every
7
+ * Agent transition. One high-water mark shared by both would therefore let a
8
+ * presence epoch swallow a later, smaller ledger sequence and park the surface
9
+ * on stale data, so coverage and pending versions are kept per scope.
10
+ *
11
+ * One round answers one version per scope. The Host delivers one event to every
12
+ * matching scope, so the first version a round sees in a scope it has not
13
+ * answered yet is that event reaching a second scope, and joins the round
14
+ * already fetching instead of earning a second identical read. A newer version
15
+ * in a scope the round has answered arrived after the round issued its fetch,
16
+ * so it owes one trailing round.
17
+ */
18
+ export class ScopeCoverage {
19
+ covered = new Map();
20
+ pending = new Map();
21
+ /** The scopes the open round has an answer for. */
22
+ answered = new Set();
23
+ fetching = false;
24
+ /** Folds one wake in and answers whether it opens a round. */
25
+ wake(scope, version) {
26
+ if (version <= this.coveredOf(scope))
27
+ return false;
28
+ if (this.fetching && this.answered.has(scope)) {
29
+ this.pending.set(scope, Math.max(this.pending.get(scope) ?? 0, version));
30
+ return false;
31
+ }
32
+ this.answered.add(scope);
33
+ this.covered.set(scope, version);
34
+ return !this.fetching;
35
+ }
36
+ /** Opens a round's fetch window; versions observed from here on are pending. */
37
+ beginRound() {
38
+ this.pending.clear();
39
+ this.fetching = true;
40
+ }
41
+ /** Closes a round's fetch window and answers whether a trailing round is owed. */
42
+ finishRound() {
43
+ this.fetching = false;
44
+ this.answered.clear();
45
+ let owed = false;
46
+ for (const [scope, version] of this.pending) {
47
+ if (version <= this.coveredOf(scope))
48
+ continue;
49
+ this.covered.set(scope, version);
50
+ owed = true;
51
+ }
52
+ return owed;
53
+ }
54
+ /** Drops every observation: a new owner starts with nothing covered. */
55
+ reset() {
56
+ this.covered.clear();
57
+ this.pending.clear();
58
+ this.answered.clear();
59
+ this.fetching = false;
60
+ }
61
+ coveredOf(scope) {
62
+ return this.covered.get(scope) ?? 0;
63
+ }
64
+ }