@wowyuarm/dsh-agent-team 0.1.9 → 0.1.11

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 (119) hide show
  1. package/README.md +17 -11
  2. package/README.zh.md +17 -11
  3. package/package.json +61 -47
  4. package/packages/agent-team/README.md +6 -4
  5. package/packages/agent-team/README.zh.md +6 -3
  6. package/packages/agent-team/lib/context-management.js +48 -55
  7. package/packages/agent-team/lib/context-projection.js +22 -8
  8. package/packages/agent-team/lib/context-source.js +163 -23
  9. package/packages/agent-team/lib/index.js +349 -176
  10. package/packages/agent-team/lib/invariant.js +38 -4
  11. package/packages/agent-team/lib/ledger.js +658 -294
  12. package/packages/agent-team/lib/member-context.js +6 -2
  13. package/packages/agent-team/lib/member-time-context.js +141 -0
  14. package/packages/agent-team/lib/pressure-policy.js +34 -11
  15. package/packages/agent-team/lib/progress-nudge.js +12 -4
  16. package/packages/agent-team/lib/session-event-cursor.js +116 -0
  17. package/packages/agent-team/lib/session-remediation.js +493 -0
  18. package/packages/agent-team/lib/spec.js +49 -36
  19. package/packages/agent-team/lib/stored-session-reader.js +138 -0
  20. package/packages/agent-team/lib/time-format.js +56 -0
  21. package/packages/agent-team/lib/typert.host.js +163 -63
  22. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  23. package/packages/agent-team/lib/typert.remote-client.js +105 -32
  24. package/packages/agent-team/lib/types/context-management.d.ts +13 -1
  25. package/packages/agent-team/lib/types/context-management.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/context-projection.d.ts +8 -0
  27. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/context-source.d.ts +70 -36
  29. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/index.d.ts +110 -9
  31. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/invariant.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/ledger.d.ts +205 -7
  34. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  35. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/member-time-context.d.ts +83 -0
  37. package/packages/agent-team/lib/types/member-time-context.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/progress-nudge.d.ts.map +1 -1
  41. package/packages/agent-team/lib/types/session-event-cursor.d.ts +119 -0
  42. package/packages/agent-team/lib/types/session-event-cursor.d.ts.map +1 -0
  43. package/packages/agent-team/lib/types/session-remediation.d.ts +172 -0
  44. package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -0
  45. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  46. package/packages/agent-team/lib/types/stored-session-reader.d.ts +92 -0
  47. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -0
  48. package/packages/agent-team/lib/types/time-format.d.ts +28 -0
  49. package/packages/agent-team/lib/types/time-format.d.ts.map +1 -0
  50. package/packages/agent-team/lib/types/types/entities.d.ts +36 -2
  51. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  52. package/packages/agent-team/lib/types/types/operations.d.ts +45 -19
  53. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
  54. package/packages/agent-team/lib/types/types/requests-results.d.ts +50 -3
  55. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  56. package/packages/agent-team/preset/team-member/agent.cordis.yml +21 -4
  57. package/packages/client-agent-team/README.md +1 -1
  58. package/packages/client-agent-team/README.zh.md +1 -1
  59. package/packages/client-agent-team/lib/client.js +1121 -402
  60. package/packages/client-agent-team/lib/client.js.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  62. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +24 -4
  63. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +42 -22
  65. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +13 -24
  67. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +11 -5
  68. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  69. package/packages/client-agent-team/lib/types/client/TeamComposer.js +15 -9
  70. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  71. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/TeamConversation.js +8 -1
  73. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +21 -0
  74. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -0
  75. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +71 -0
  76. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  77. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +8 -24
  78. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +9 -2
  79. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  80. package/packages/client-agent-team/lib/types/client/TeamMessage.js +13 -6
  81. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +8 -0
  82. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +21 -1
  84. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  85. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +106 -32
  86. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  87. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  88. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +50 -7
  89. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  90. package/packages/client-agent-team/lib/types/client/index.js +25 -9
  91. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -0
  92. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  93. package/packages/client-agent-team/lib/types/client/locales.js +18 -0
  94. package/packages/client-agent-team/lib/types/client/navigation.d.ts +10 -0
  95. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  96. package/packages/client-agent-team/lib/types/client/navigation.js +18 -7
  97. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +41 -0
  98. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +1 -0
  99. package/packages/client-agent-team/lib/types/client/scope-coverage.js +64 -0
  100. package/packages/client-agent-team/lib/types/client/slots.d.ts +8 -2
  101. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  102. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +26 -1
  103. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
  104. package/packages/client-agent-team/lib/types/client/team-changes.js +66 -15
  105. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts +20 -0
  106. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts.map +1 -0
  107. package/packages/client-agent-team/lib/types/client/team-dialog-save.js +36 -0
  108. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +9 -0
  109. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  110. package/packages/client-agent-team/lib/types/client/team-formatters.js +34 -6
  111. package/packages/tool-agent-team/README.md +8 -6
  112. package/packages/tool-agent-team/README.zh.md +8 -6
  113. package/packages/tool-agent-team/lib/context-tools.js +18 -14
  114. package/packages/tool-agent-team/lib/host-access.js +20 -0
  115. package/packages/tool-agent-team/lib/index.js +314 -123
  116. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  117. package/packages/tool-agent-team/lib/types/host-access.d.ts +14 -0
  118. package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -0
  119. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Button, IconChevronDownOutline14, Input, Menu, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { mintRequestId } from "./requests.js";
5
+ import { useEditDialogSave } from "./team-dialog-save.js";
5
6
  import createCss from './create.module.css';
6
7
  import css from './sidebar.module.css';
7
8
  /** Model option key inside one editor; opaque and resolved against the loaded groups. */
@@ -77,9 +78,11 @@ export function AgentEditorDialog({ status, updateMember, loadModels, onCommitte
77
78
  const [handle, setHandle] = useState(status.member.handle);
78
79
  const [description, setDescription] = useState(status.member.description);
79
80
  const [model, setModel] = useState(status.member.model);
80
- const [saving, setSaving] = useState(false);
81
- const [error, setError] = useState();
82
- const pendingRequest = useRef();
81
+ const { saving, error, pendingRequest, save } = useEditDialogSave({
82
+ save: updateMember,
83
+ onCommitted,
84
+ onClose,
85
+ });
83
86
  const dirty = handle.trim() !== status.member.handle || description.trim() !== status.member.description
84
87
  || !sameModel(model, status.member.model);
85
88
  const submit = async (event) => {
@@ -104,26 +107,7 @@ export function AgentEditorDialog({ status, updateMember, loadModels, onCommitte
104
107
  requestId: mintRequestId(),
105
108
  ...payload,
106
109
  };
107
- pendingRequest.current = request;
108
- setSaving(true);
109
- setError(undefined);
110
- try {
111
- const result = await updateMember(request);
112
- if (result.ok) {
113
- pendingRequest.current = undefined;
114
- await onCommitted();
115
- onClose();
116
- }
117
- else {
118
- setError(result.error.message);
119
- }
120
- }
121
- catch (cause) {
122
- setError(cause instanceof Error ? cause.message : String(cause));
123
- }
124
- finally {
125
- setSaving(false);
126
- }
110
+ await save(request);
127
111
  };
128
112
  return (_jsx(Modal, { open: true, onClose: onClose, title: t('editAgent'), description: `@${status.member.handle}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-edit-form", variant: "primary", disabled: saving || !dirty || handle.trim().length === 0, children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-agent-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { pendingRequest.current = undefined; setModel(choice); }, loadModels: loadModels, disabled: saving, t: t }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
129
113
  }
@@ -27,6 +27,13 @@ export interface TeamMessageProps {
27
27
  readonly onResolveTaskRefs?: ((taskRefs: readonly AgentTeamTaskRef[]) => Promise<readonly ResolvedTaskRef[]>) | undefined;
28
28
  readonly children?: ReactNode;
29
29
  }
30
- /** One chat message row with identity chrome and sender-appropriate rendering. */
31
- export declare function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }: TeamMessageProps): import("react").JSX.Element;
30
+ /**
31
+ * One chat message row with identity chrome and sender-appropriate rendering.
32
+ *
33
+ * Memoized because a timeline row is rendered by the page that owns the whole
34
+ * Thread: the Task-ref subscription below lives inside this component, so
35
+ * skipping a render here never detaches it. Callers must therefore keep the
36
+ * props they derive per render (mention names, ref callbacks) identity-stable.
37
+ */
38
+ export declare const TeamMessage: import("react").NamedExoticComponent<TeamMessageProps>;
32
39
  //# sourceMappingURL=TeamMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoG,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGxI,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BAqH3N"}
1
+ {"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0G,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAG9I,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,wDAqHtB,CAAA"}
@@ -1,12 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { MarkdownText, MessageText, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
+ import { MarkdownText, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { cachedAttachmentDataUrl, formatByteSize, loadAttachmentDataUrl } from "./attachment-preview.js";
5
5
  import { cachedResolvedTaskRef, resolveUnknownTaskRefs, useResolvedTaskRefVersion } from "./task-refs.js";
6
6
  import { formatMessageTime, isSingleBrandedRef, memberHue, planMessageBody, shouldClampMessage, splitBrandedRefs, splitMentionNames } from "./team-formatters.js";
7
7
  import css from './conversation.module.css';
8
- /** One chat message row with identity chrome and sender-appropriate rendering. */
9
- export function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }) {
8
+ /**
9
+ * One chat message row with identity chrome and sender-appropriate rendering.
10
+ *
11
+ * Memoized because a timeline row is rendered by the page that owns the whole
12
+ * Thread: the Task-ref subscription below lives inside this component, so
13
+ * skipping a render here never detaches it. Callers must therefore keep the
14
+ * props they derive per render (mention names, ref callbacks) identity-stable.
15
+ */
16
+ export const TeamMessage = memo(function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }) {
10
17
  const avatarStyle = human ? undefined : { '--team-avatar-hue': memberHue(memberId) };
11
18
  // Literal bodies carry mention chips inline — Human input always, and
12
19
  // plain-prose Agent bodies where literal rendering loses nothing. Rich
@@ -90,14 +97,14 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
90
97
  ? _jsx("span", { className: css.mention, children: segment.text }, index)
91
98
  : _jsx(Fragment, { children: renderRefs(segment.text, onOpenRef, taskLabel, refLabel) }, index)) })
92
99
  : plan.render === 'literal'
93
- ? _jsx("div", { className: css.messageText, children: onOpenRef === undefined ? _jsx(MessageText, { text: displayBody }) : renderRefs(displayBody, onOpenRef, taskLabel, refLabel) })
100
+ ? _jsx("div", { className: css.messageText, children: onOpenRef === undefined ? displayBody : renderRefs(displayBody, onOpenRef, taskLabel, refLabel) })
94
101
  : _jsx("div", { ref: markdownRef, className: css.messageMarkdown, children: _jsx(MarkdownText, { text: displayBody, labels: markdownLabels }, `${displayBody}:${onOpenRef === undefined ? 'literal' : 'refs'}`) });
95
102
  return (_jsxs("article", { className: css.messageRow, "data-human": human || undefined, "data-grouped": grouped || undefined, children: [_jsx("div", { className: css.messageIdentity, style: avatarStyle, "aria-hidden": "true", children: senderName.replace('@', '').slice(0, 1).toUpperCase() }), _jsxs("div", { className: css.messageBody, children: [(!grouped || showGroupedTime === true) && (_jsxs("div", { className: css.nameRow, children: [!grouped && _jsx("strong", { ...(senderTitle === undefined ? {} : { title: senderTitle }), children: senderName }), occurredAt !== undefined && _jsx("span", { className: css.messageTime, children: formatMessageTime(occurredAt) })] })), clampable ? _jsx("div", { className: clamped ? css.messageClamp : undefined, children: bodyNode }) : bodyNode, clampable && (_jsx("button", { type: "button", className: css.messageExpand, "data-message-expand": "true", "aria-expanded": expanded, onClick: () => { setExpanded(value => !value); }, children: expanded ? (t?.('collapseMessage') ?? 'Show less') : (t?.('expandMessage') ?? 'Show more') })), (plan.fallbackNames.length > 0 || plan.fallbackRefs.length > 0) && (_jsxs("div", { className: css.mentionsRow, children: [plan.fallbackNames.map(name => _jsxs("span", { className: css.mention, children: ["@", name] }, name)), plan.fallbackRefs.map(ref => {
96
103
  const resolved = cachedResolvedTaskRef(ref);
97
104
  const label = resolved !== undefined && ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : ref;
98
105
  return _jsx("button", { type: "button", className: css.refLink, title: ref, onClick: () => { onOpenRef(ref); }, children: label }, ref);
99
106
  })] })), attachments !== undefined && attachments.length > 0 && _jsx(TeamAttachmentStrip, { attachments: attachments, ...(loadAttachment === undefined ? {} : { loadAttachment }), ...(t === undefined ? {} : { t }) }), children] })] }));
100
- }
107
+ });
101
108
  /** Text nodes that Markdown rendered as prose rather than code or a link. */
102
109
  function markdownProseTextNodes(root) {
103
110
  const nodes = [];
@@ -2,6 +2,14 @@ import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types
2
2
  import type { TeamSidebarProps } from './slots.ts';
3
3
  import type { TeamStateDotState } from './TeamStateDot.tsx';
4
4
  export declare function presenceLabel(status: AgentTeamClientMemberStatus, t: TeamSidebarProps['t']): string;
5
+ /** One line of human-readable diagnostic text: the reason, plus the refused artifact path when one was reported. */
6
+ export declare function diagnosticText(status: AgentTeamClientMemberStatus): string;
7
+ /**
8
+ * Whether the restart action can help an unavailable Member: it heals
9
+ * transient and repairable failures, but not a transient rollover window
10
+ * (which resolves on its own) or a refusal already proven non-remediable.
11
+ */
12
+ export declare function restartOffered(status: AgentTeamClientMemberStatus): boolean;
5
13
  /** Shared presence → indicator mapping for dots and avatar badges. */
6
14
  export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']): TeamStateDotState;
7
15
  export declare function TeamPresenceDot({ status, t }: {
@@ -1 +1 @@
1
- {"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
1
+ {"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,MAAM,CAI1E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,OAAO,CAK3E;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
@@ -6,7 +6,27 @@ export function presenceLabel(status, t) {
6
6
  const label = status.presence === 'available' ? t('statusAvailable')
7
7
  : status.presence === 'working' ? t('statusWorking')
8
8
  : status.presence === 'error' ? t('statusError') : t('statusUnavailable');
9
- return status.diagnostic === undefined ? label : `${label}: ${status.diagnostic}`;
9
+ return status.diagnostic === undefined ? label : `${label}: ${diagnosticText(status)}`;
10
+ }
11
+ /** One line of human-readable diagnostic text: the reason, plus the refused artifact path when one was reported. */
12
+ export function diagnosticText(status) {
13
+ const diagnostic = status.diagnostic;
14
+ if (diagnostic === undefined)
15
+ return '';
16
+ return diagnostic.location === undefined ? diagnostic.detail : `${diagnostic.detail} (${diagnostic.location.path})`;
17
+ }
18
+ /**
19
+ * Whether the restart action can help an unavailable Member: it heals
20
+ * transient and repairable failures, but not a transient rollover window
21
+ * (which resolves on its own) or a refusal already proven non-remediable.
22
+ */
23
+ export function restartOffered(status) {
24
+ const diagnostic = status.diagnostic;
25
+ if (diagnostic === undefined)
26
+ return true;
27
+ if (diagnostic.class === 'rollover')
28
+ return false;
29
+ return !(diagnostic.class === 'session-refused' && diagnostic.remediable === false);
10
30
  }
11
31
  /** Shared presence → indicator mapping for dots and avatar badges. */
12
32
  export function presenceDotState(presence) {
@@ -1 +1 @@
1
- {"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IACxE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAgsBxD"}
1
+ {"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IACxE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA+CD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAmvBxD"}
@@ -1,24 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Fragment, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
2
+ import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { Button, DisclosureRow, IconChevronLeftOutline14, IconChecklistOutline14, Modal, Pill } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamComposer } from "./TeamComposer.js";
5
- import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import { diagnosticText, TeamPresenceDot } from "./TeamPresenceDot.js";
6
6
  import { TeamMessage } from "./TeamMessage.js";
7
7
  import { TeamRunDivider } from "./TeamRunDivider.js";
8
8
  import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf, taskStatusDot } from "./team-formatters.js";
9
9
  import { TeamStateDot } from "./TeamStateDot.js";
10
- import { mintRequestId } from "./requests.js";
10
+ import { mintRequestId, uploadComposerFiles } from "./requests.js";
11
+ import { ScopeCoverage } from "./scope-coverage.js";
11
12
  import { daySeparatorLabel, isRunGap, timelineDayKey } from "./team-separators.js";
12
13
  import { useTimelineScroll } from "./timeline-scroll.js";
13
14
  import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
14
- import { bytesToBase64 } from "./attachment-preview.js";
15
15
  import css from './conversation.module.css';
16
16
  import threadCss from './thread.module.css';
17
17
  function factKey(fact) {
18
18
  return fact.kind === 'message' ? `message:${fact.message.messageRef}` : `activity:${fact.activity.activityRef}`;
19
19
  }
20
20
  function messageFact(message, mentions = []) {
21
- return { kind: 'message', sequence: message.sequence, message, mentions };
21
+ return { kind: 'message', sequence: message.sequence, message, mentions, occurredAt: message.occurredAt };
22
+ }
23
+ /**
24
+ * A fact owns its mention array, so the rendered name list is cached against
25
+ * that array. Every roster refresh replaces the handles map with a fresh Map of
26
+ * identical content, so the cache compares the resolved names rather than the
27
+ * map identity: identity stays stable while the names are unchanged, which is
28
+ * what keeps a memoized row from re-rendering on every refresh.
29
+ */
30
+ const mentionNamesCache = new WeakMap();
31
+ function stableMentionNames(mentions, handles) {
32
+ const names = mentionNamesOf(mentions, handles);
33
+ const cached = mentionNamesCache.get(mentions);
34
+ if (cached !== undefined && cached.length === names.length && cached.every((name, index) => name === names[index]))
35
+ return cached;
36
+ mentionNamesCache.set(mentions, names);
37
+ return names;
22
38
  }
23
39
  function mergeFacts(...groups) {
24
40
  const byKey = new Map();
@@ -57,8 +73,9 @@ export function TeamThreadPage(props) {
57
73
  const [newFactsCount, setNewFactsCount] = useState(0);
58
74
  // The reply draft lives in the keyed draft cache: view switches unmount
59
75
  // this page, and a refresh must not cost the half-written message either.
76
+ // The composer owns the subscription — this page only reads a snapshot when
77
+ // it sends, so typing never re-renders the timeline.
60
78
  const draftKey = `thread:${threadRef}`;
61
- const { draft, recipients } = useSyncExternalStore(drafts.subscribe, () => drafts.getSnapshot(draftKey));
62
79
  const [claimsOpen, setClaimsOpen] = useState(false);
63
80
  // Early acceptance: the Human may accept while Claims are still open; the
64
81
  // confirm dialog lists exactly what will be completed with the Task.
@@ -148,14 +165,15 @@ export function TeamThreadPage(props) {
148
165
  setLoading(false);
149
166
  }
150
167
  };
151
- const refreshSupplemental = async () => {
168
+ /** One roster + view read. Answers false once the page is gone. */
169
+ const applySupplemental = async () => {
152
170
  try {
153
171
  const [loadedMembers, loadedView] = await Promise.all([
154
172
  loadMembers({ workspaceId }),
155
173
  loadChannels({ workspaceId, ...(channelRef === undefined ? {} : { channelRef }), threadRef, includeActivities: false, limit: 1 }),
156
174
  ]);
157
175
  if (!mountedRef.current)
158
- return;
176
+ return false;
159
177
  if (loadedMembers.ok)
160
178
  setMembers(loadedMembers.value);
161
179
  if (loadedView.ok)
@@ -163,12 +181,49 @@ export function TeamThreadPage(props) {
163
181
  const failure = [loadedMembers, loadedView].find(result => !result.ok);
164
182
  if (failure !== undefined && !failure.ok)
165
183
  setError(failure.error.message);
184
+ return true;
166
185
  }
167
186
  catch (cause) {
168
187
  if (mountedRef.current)
169
188
  setError(cause instanceof Error ? cause.message : String(cause));
189
+ return mountedRef.current;
170
190
  }
171
191
  };
192
+ // One Host change event wakes every scope it matches: here a workspace commit
193
+ // and a presence transition, whose separate polls both deliver that event's
194
+ // version. Both asks are the same roster and view read, so each round answers
195
+ // one version per scope, and a newer version arriving mid-round runs one
196
+ // trailing round instead of being dropped. The two scopes count different
197
+ // domains, so coverage and pending versions are tracked per scope.
198
+ const supplementalRef = useRef();
199
+ const supplementalCoverageRef = useRef(new ScopeCoverage());
200
+ const refreshSupplemental = (wake) => {
201
+ const inFlight = supplementalRef.current;
202
+ if (wake === undefined) {
203
+ if (inFlight !== undefined)
204
+ return inFlight;
205
+ }
206
+ else if (!supplementalCoverageRef.current.wake(wake.scope, wake.version)) {
207
+ return inFlight ?? Promise.resolve();
208
+ }
209
+ const coverage = supplementalCoverageRef.current;
210
+ const round = (async () => {
211
+ for (;;) {
212
+ coverage.beginRound();
213
+ const applied = await applySupplemental();
214
+ // A newer change landed while this round fetched: cover it too, and
215
+ // keep going only while the page is still the one asking.
216
+ if (!applied || !coverage.finishRound())
217
+ return;
218
+ }
219
+ })().finally(() => {
220
+ // A remount may already own a newer round; only this one clears itself.
221
+ if (supplementalRef.current === round)
222
+ supplementalRef.current = undefined;
223
+ });
224
+ supplementalRef.current = round;
225
+ return round;
226
+ };
172
227
  const refreshPassiveFacts = async () => {
173
228
  try {
174
229
  const result = await loadThreadHistory({ workspaceId, ...threadRequest, limit: 100 });
@@ -226,6 +281,9 @@ export function TeamThreadPage(props) {
226
281
  };
227
282
  useEffect(() => {
228
283
  mountedRef.current = true;
284
+ // The previous mount's supplemental round must not answer for this one.
285
+ supplementalRef.current = undefined;
286
+ supplementalCoverageRef.current.reset();
229
287
  projectionRef.current = undefined;
230
288
  setProjection(undefined);
231
289
  setChannelView(undefined);
@@ -298,7 +356,19 @@ export function TeamThreadPage(props) {
298
356
  setError(update.message);
299
357
  return;
300
358
  }
301
- void refreshSupplemental();
359
+ void refreshSupplemental({ scope: 'workspace', version: update.version });
360
+ }),
361
+ // Presence transitions commit nothing: only the member rows move, so
362
+ // the roster refresh rides the same supplemental fetch as workspace
363
+ // membership changes, leaving the timeline untouched.
364
+ subscribeChanges({ kind: 'presence', workspaceId }, update => {
365
+ if (!mountedRef.current)
366
+ return;
367
+ if (update.type === 'failed') {
368
+ setError(update.message);
369
+ return;
370
+ }
371
+ void refreshSupplemental({ scope: 'presence', version: update.version });
302
372
  }),
303
373
  ];
304
374
  return () => {
@@ -378,7 +448,8 @@ export function TeamThreadPage(props) {
378
448
  // Branded-ref navigation for message bodies: channel refs hop to the
379
449
  // Channel; the open Task's own refs are already on screen, and other Tasks
380
450
  // are not resolvable from this surface, so they degrade to a no-op.
381
- const openRef = (ref) => {
451
+ // Identity-stable: a fresh closure per render would defeat TeamMessage's memo.
452
+ const openRef = useCallback((ref) => {
382
453
  if (ref.startsWith('channel:') && ref !== channelRef) {
383
454
  selectChannel(ref);
384
455
  return;
@@ -402,13 +473,13 @@ export function TeamThreadPage(props) {
402
473
  // Another Task cited here: resolve its home Channel and jump.
403
474
  jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
404
475
  }
405
- };
406
- const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
476
+ }, [channelRef, threadRef, taskRef, workspaceId, selectChannel, selectThread, loadChannels, resolveTaskRefs]);
477
+ const lookupTaskRefs = useMemo(() => hostTaskRefLookup(resolveTaskRefs, workspaceId), [resolveTaskRefs, workspaceId]);
407
478
  const renderFact = (fact, grouped = false) => {
408
479
  if (fact.kind === 'message') {
409
480
  const sender = memberName(fact.message.sender);
410
481
  const senderStatus = members.find(candidate => candidate.member.memberId === fact.message.sender);
411
- return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames: mentionNamesOf(fact.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: grouped, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }) }, factKey(fact));
482
+ return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames: stableMentionNames(fact.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: grouped, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }) }, factKey(fact));
412
483
  }
413
484
  return _jsxs("p", { className: threadCss.activityRow, children: [_jsx("span", { className: threadCss.activityMark, "aria-hidden": "true" }), _jsx("span", { className: threadCss.activityText, children: formatActivity(fact.activity, { t, actorName: memberName, claims: taskClaims }) })] }, factKey(fact));
414
485
  };
@@ -480,7 +551,7 @@ export function TeamThreadPage(props) {
480
551
  setProjection(current => current === undefined ? current
481
552
  : { ...current, task: committed.task, thread: committed.thread });
482
553
  setCurrentFacts(current => {
483
- const merged = mergeFacts(current, [{ kind: 'activity', sequence: committed.activity.sequence, activity: committed.activity }]);
554
+ const merged = mergeFacts(current, [{ kind: 'activity', sequence: committed.activity.sequence, activity: committed.activity, occurredAt: committed.receipt.occurredAt }]);
484
555
  currentFactsRef.current = merged;
485
556
  return merged;
486
557
  });
@@ -527,7 +598,7 @@ export function TeamThreadPage(props) {
527
598
  const committed = result.value;
528
599
  setProjection(current => current === undefined ? current : { ...current, task: committed.task, thread: committed.thread, claims: committed.claims });
529
600
  setCurrentFacts(current => {
530
- const merged = mergeFacts(current, [{ kind: 'activity', sequence: committed.activity.sequence, activity: committed.activity }]);
601
+ const merged = mergeFacts(current, [{ kind: 'activity', sequence: committed.activity.sequence, activity: committed.activity, occurredAt: committed.receipt.occurredAt }]);
531
602
  currentFactsRef.current = merged;
532
603
  return merged;
533
604
  });
@@ -553,7 +624,19 @@ export function TeamThreadPage(props) {
553
624
  setMutating(undefined);
554
625
  }
555
626
  };
627
+ // Editing the draft invalidates one-shot send state: a confirmation token,
628
+ // the retained requestId, and the status line. Every setter returns the same
629
+ // state when there is nothing to clear, so a keystroke does not re-render the
630
+ // timeline now that the composer owns the draft.
631
+ const clearSendState = useCallback(() => {
632
+ setConfirmation(current => current === undefined ? current : undefined);
633
+ setReplyRequestId(current => current === undefined ? current : undefined);
634
+ setStatusMessage(current => current === undefined ? current : undefined);
635
+ }, []);
556
636
  const sendReply = async () => {
637
+ // Read the draft at send time: this page no longer subscribes to it, so a
638
+ // captured render value would be stale after the composer's own edits.
639
+ const { draft, recipients } = drafts.getSnapshot(draftKey);
557
640
  if (pending || thread === undefined || draft.trim() === '')
558
641
  return;
559
642
  const id = replyRequestId ?? mintRequestId();
@@ -563,21 +646,12 @@ export function TeamThreadPage(props) {
563
646
  try {
564
647
  // Upload chosen files first; any failure aborts the reply with the
565
648
  // existing error surface and keeps the chips for a retry.
566
- const attachmentIds = [];
567
- for (const file of pendingFiles) {
568
- const uploaded = await putAttachment({
569
- requestId: mintRequestId(),
570
- workspaceId,
571
- name: file.name,
572
- mediaType: file.type === '' ? undefined : file.type,
573
- bytesBase64: bytesToBase64(new Uint8Array(await file.arrayBuffer())),
574
- });
575
- if (!uploaded.ok) {
576
- setError(uploaded.error.message);
577
- return;
578
- }
579
- attachmentIds.push(uploaded.value.attachmentId);
649
+ const upload = await uploadComposerFiles(putAttachment, workspaceId, pendingFiles);
650
+ if (!upload.ok) {
651
+ setError(upload.error);
652
+ return;
580
653
  }
654
+ const attachmentIds = upload.attachmentIds;
581
655
  const result = await reply({ requestId: id, workspaceId, threadRef, ...(task === undefined ? {} : { taskRef: task.taskRef }), body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
582
656
  if (!result.ok) {
583
657
  setError(result.error.message);
@@ -586,7 +660,7 @@ export function TeamThreadPage(props) {
586
660
  if (result.value.kind === 'committed') {
587
661
  const committed = result.value;
588
662
  setCurrentFacts(current => {
589
- const merged = mergeFacts(current, [{ kind: 'message', sequence: committed.message.sequence, message: committed.message, mentions: [...recipients].sort() }]);
663
+ const merged = mergeFacts(current, [{ kind: 'message', sequence: committed.message.sequence, message: committed.message, mentions: [...recipients].sort(), occurredAt: committed.receipt.occurredAt }]);
590
664
  currentFactsRef.current = merged;
591
665
  return merged;
592
666
  });
@@ -654,7 +728,7 @@ export function TeamThreadPage(props) {
654
728
  } }, children: t('acceptTask') });
655
729
  })(), task.resolution === 'open'
656
730
  ? _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('close'); }, children: t('closeTask') })
657
- : _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: status.diagnostic ?? t('statusError') }), " \u00B7 ", claim.direction] })] }, claim.claimRef))] }), task !== undefined && thread !== undefined && (() => {
731
+ : _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: diagnosticText(status) || t('statusError') }), " \u00B7 ", claim.direction] })] }, claim.claimRef))] }), task !== undefined && thread !== undefined && (() => {
658
732
  // Recomputed here so the confirm list never shows stale rows.
659
733
  const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
660
734
  return _jsxs(Modal, { open: confirmingAccept, onClose: () => { if (!pending)
@@ -664,5 +738,5 @@ export function TeamThreadPage(props) {
664
738
  return _jsxs("article", { className: threadCss.claimRow, children: [ownerStatus === undefined ? _jsx("span", {}) : _jsx(TeamPresenceDot, { status: ownerStatus, t: t }), _jsx("strong", { className: threadCss.claimOwner, children: memberName(claim.owner) }), _jsx("span", { className: threadCss.claimDirection, children: claim.direction }), _jsx("small", { className: threadCss.claimState, children: formatClaimState(claim.state, t) })] }, claim.claimRef);
665
739
  })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: handleTimelineScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), autoReadExhausted && _jsxs("div", { className: css.timelineAction, role: "alert", children: [_jsx("span", { children: t('autoReadIncomplete') }), _jsx(Button, { size: "sm", onClick: () => { setAutoReadExhausted(false); void drainUnread(); }, disabled: loading, children: t('retry') })] }), newFactsCount > 0 && _jsx("div", { className: threadCss.newUpdates, role: "status", children: _jsx("button", { type: "button", className: threadCss.newUpdatesJump, onClick: () => { setNewFactsCount(0); timeline.scrollToBottom(); }, children: t('newUpdatesJump', { count: newFactsCount }) }) })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
666
740
  ? _jsxs("div", { className: threadCss.closedBar, "data-team-closed": true, children: [error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error }), _jsxs("div", { className: threadCss.closedNotice, children: [_jsx("span", { children: t('taskClosedNotice') }), _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] })
667
- : _jsx(TeamComposer, { members: channelMembers, followerMemberIds: followerIds, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
741
+ : _jsx(TeamComposer, { members: channelMembers, followerMemberIds: followerIds, drafts: drafts, draftKey: draftKey, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onEdit: clearSendState, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
668
742
  }
@@ -1,3 +1,3 @@
1
1
  import type { TeamSidebarProps } from './slots.ts';
2
- export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, archiveChannel, updateMember, recoverMember, archiveMember, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
2
+ export declare 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 }: TeamSidebarProps): import("react").JSX.Element;
3
3
  //# sourceMappingURL=TeamWorkspaceBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA8D9V"}
1
+ {"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA+GtY"}
@@ -1,13 +1,13 @@
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
+ 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
11
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
12
12
  const workspaces = useWorkspaces(state => state.items);
13
13
  const selected = navigationState.workspaceId;
@@ -16,19 +16,62 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
16
16
  // Exactly one sidebar row carries aria-current='page': the open Channel while
17
17
  // one is set, the selected Agent card while a Member Session view is open,
18
18
  // 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;
19
+ // quiet folder tint (data-selected) in every case. The mention-Inbox card
20
+ // takes the marker while the Inbox page stands.
21
+ const overviewIsCurrent = navigationState.channelRef === undefined && navigationState.memberSessionId === undefined && navigationState.inbox !== true;
22
+ const inboxIsCurrent = navigationState.inbox === true;
21
23
  const [creatingAgents, setCreatingAgents] = useState([]);
22
24
  // Rail icons request expansion and name the section to reveal once wide.
23
25
  const [pendingSection, setPendingSection] = useState();
24
26
  const channelsRef = useRef(null);
25
27
  const agentsRef = useRef(null);
26
28
  const workspacesOpen = useSidebarSectionOpen(undefined, 'workspaces');
29
+ // The cross-Workspace mention badge: one scope-less subscription while the
30
+ // Team sidebar stands; every wake re-pulls each Workspace's direct-only
31
+ // total (limit 1 — totals cover every row, never the list). Same-burst
32
+ // wakes coalesce behind a short debounce; the total is still whatever the
33
+ // Host's direct-only projection returns.
34
+ const [inboxTotal, setInboxTotal] = useState(0);
27
35
  useEffect(() => {
36
+ let disposed = false;
37
+ let scheduled;
38
+ const refresh = async () => {
39
+ const results = await Promise.all(workspaces.map(workspace => loadInbox({ workspaceId: workspace.workspaceId, directOnly: true, limit: 1 })));
40
+ if (disposed)
41
+ return;
42
+ setInboxTotal(results.reduce((sum, result) => result.ok ? sum + result.value.totalUnreadCount : sum, 0));
43
+ };
44
+ const schedule = () => {
45
+ if (scheduled !== undefined)
46
+ return;
47
+ scheduled = setTimeout(() => { scheduled = undefined; void refresh(); }, 200);
48
+ };
49
+ void refresh();
50
+ const unsubscribe = subscribeChanges(undefined, update => {
51
+ if (update.type === 'changed')
52
+ schedule();
53
+ });
54
+ // A durable Thread read consumes this reader's mention markers without a
55
+ // changes wake (reads change no shared projection), so the badge also
56
+ // refreshes from every completed read.
57
+ const unsubscribeReads = subscribeReads(schedule);
58
+ return () => {
59
+ disposed = true;
60
+ if (scheduled !== undefined)
61
+ clearTimeout(scheduled);
62
+ unsubscribe();
63
+ unsubscribeReads();
64
+ };
65
+ }, [loadInbox, subscribeChanges, subscribeReads, workspaces]);
66
+ useEffect(() => {
67
+ // The Inbox page is global and needs no selected Workspace, so the
68
+ // auto-select must not yank the seat back to a Workspace overview.
69
+ if (navigationState.inbox === true)
70
+ return;
28
71
  if (navigationState.mode === 'team' && selectedId !== undefined && selectedId !== selected) {
29
72
  selectWorkspace(selectedId);
30
73
  }
31
- }, [navigationState.mode, selected, selectedId, selectWorkspace]);
74
+ }, [navigationState.inbox, navigationState.mode, selected, selectedId, selectWorkspace]);
32
75
  useEffect(() => {
33
76
  if (!wide || pendingSection === undefined)
34
77
  return;
@@ -37,7 +80,7 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
37
80
  queueMicrotask(() => { node?.querySelector('button')?.focus(); });
38
81
  }, [wide, pendingSection]);
39
82
  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 }) }) })] });
83
+ return _jsxs("nav", { className: css.railWorkspace, "aria-label": t('workspaceSections'), children: [_jsx(Tooltip, { label: t('mentionsOfMe'), side: "right", children: _jsxs("button", { type: "button", className: css.railButton, "aria-label": inboxTotal > 0 ? t('mentionsOfMeWithCount', { count: inboxTotal }) : t('mentionsOfMe'), "aria-current": inboxIsCurrent ? 'page' : undefined, onClick: () => { selectInbox(); expandSidebar(); }, children: [_jsx(IconQueueOutline14, { size: 16 }), inboxTotal > 0 && _jsx("span", { className: css.inboxBadge, "aria-hidden": "true", children: inboxTotal > 99 ? '99+' : 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
84
  }
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) })] })] });
85
+ return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsxs("button", { type: "button", className: css.inboxCard, "aria-label": inboxTotal > 0 ? t('mentionsOfMeWithCount', { count: inboxTotal }) : t('mentionsOfMe'), "aria-current": inboxIsCurrent ? 'page' : undefined, onClick: selectInbox, children: [_jsx(IconQueueOutline14, { size: 16 }), _jsx("span", { className: css.inboxCardLabel, children: t('mentionsOfMe') }), inboxTotal > 0 && _jsx("span", { className: css.inboxBadge, "aria-hidden": "true", children: inboxTotal > 99 ? '99+' : inboxTotal })] }), _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
86
  }
@@ -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;AA8KD,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;AAqCnE,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;AA8LD,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}