@wowyuarm/dsh-agent-team 0.1.14-alpha.2 → 0.1.15

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 (106) hide show
  1. package/README.md +6 -4
  2. package/README.zh.md +6 -4
  3. package/cordis.patch.yml +3 -1
  4. package/package.json +51 -46
  5. package/packages/agent-team/README.md +2 -2
  6. package/packages/agent-team/README.zh.md +2 -2
  7. package/packages/agent-team/core-skills/member-memory-manager/SKILL.md +30 -0
  8. package/packages/agent-team/lib/context-continuity-host.js +90 -0
  9. package/packages/agent-team/lib/context-projection.js +266 -489
  10. package/packages/agent-team/lib/context-source.js +4 -72
  11. package/packages/agent-team/lib/human-avatar.js +61 -0
  12. package/packages/agent-team/lib/human-profile.js +157 -0
  13. package/packages/agent-team/lib/human-update-check.js +119 -0
  14. package/packages/agent-team/lib/index.js +464 -254
  15. package/packages/agent-team/lib/ledger.js +120 -15
  16. package/packages/agent-team/lib/member-context.js +6 -3
  17. package/packages/agent-team/lib/member-runtime.js +1 -1
  18. package/packages/agent-team/lib/pressure-policy.js +6 -1
  19. package/packages/agent-team/lib/typert.host.js +489 -33
  20. package/packages/agent-team/lib/typert.remote-client.d.ts +15 -3
  21. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  22. package/packages/agent-team/lib/typert.remote-client.js +250 -28
  23. package/packages/agent-team/lib/types/context-continuity-host.d.ts +84 -0
  24. package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -0
  25. package/packages/agent-team/lib/types/context-projection.d.ts +130 -185
  26. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  27. package/packages/agent-team/lib/types/context-source.d.ts +4 -35
  28. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  29. package/packages/agent-team/lib/types/human-avatar.d.ts +29 -0
  30. package/packages/agent-team/lib/types/human-avatar.d.ts.map +1 -0
  31. package/packages/agent-team/lib/types/human-profile.d.ts +116 -0
  32. package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -0
  33. package/packages/agent-team/lib/types/human-update-check.d.ts +73 -0
  34. package/packages/agent-team/lib/types/human-update-check.d.ts.map +1 -0
  35. package/packages/agent-team/lib/types/index.d.ts +153 -41
  36. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  37. package/packages/agent-team/lib/types/ledger.d.ts +31 -2
  38. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  39. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/pressure-policy.d.ts +6 -0
  41. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  42. package/packages/agent-team/lib/types/types/entities.d.ts +17 -0
  43. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  44. package/packages/agent-team/lib/types/types/requests-results.d.ts +81 -0
  45. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  46. package/packages/client-agent-team/README.md +1 -1
  47. package/packages/client-agent-team/README.zh.md +1 -1
  48. package/packages/client-agent-team/lib/client.js +1545 -303
  49. package/packages/client-agent-team/lib/client.js.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +15 -0
  51. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -0
  52. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +115 -0
  53. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  54. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +6 -2
  55. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +22 -1
  56. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +25 -2
  58. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +6 -1
  59. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  60. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +67 -29
  61. package/packages/client-agent-team/lib/types/client/TeamComposer.js +2 -2
  62. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  63. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/TeamConversation.js +12 -5
  65. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +4 -1
  66. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +22 -7
  68. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +11 -1
  69. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +66 -5
  71. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +18 -3
  72. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  73. package/packages/client-agent-team/lib/types/client/TeamMessage.js +213 -67
  74. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +5 -0
  75. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  76. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +52 -26
  77. package/packages/client-agent-team/lib/types/client/avatar-image.d.ts +21 -0
  78. package/packages/client-agent-team/lib/types/client/avatar-image.d.ts.map +1 -0
  79. package/packages/client-agent-team/lib/types/client/avatar-image.js +9 -0
  80. package/packages/client-agent-team/lib/types/client/human-identity.d.ts +90 -0
  81. package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -0
  82. package/packages/client-agent-team/lib/types/client/human-identity.js +96 -0
  83. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  84. package/packages/client-agent-team/lib/types/client/index.js +76 -4
  85. package/packages/client-agent-team/lib/types/client/locales.d.ts +72 -2
  86. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  87. package/packages/client-agent-team/lib/types/client/locales.js +78 -2
  88. package/packages/client-agent-team/lib/types/client/refs.d.ts +71 -0
  89. package/packages/client-agent-team/lib/types/client/refs.d.ts.map +1 -0
  90. package/packages/client-agent-team/lib/types/client/refs.js +181 -0
  91. package/packages/client-agent-team/lib/types/client/slots.d.ts +9 -3
  92. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  93. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +57 -9
  94. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  95. package/packages/client-agent-team/lib/types/client/team-formatters.js +85 -17
  96. package/packages/tool-agent-team/README.md +2 -2
  97. package/packages/tool-agent-team/README.zh.md +2 -2
  98. package/packages/tool-agent-team/lib/context-tools.js +20 -6
  99. package/packages/tool-agent-team/lib/index.js +2 -2
  100. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  101. package/packages/agent-team/lib/context-management.js +0 -371
  102. package/packages/agent-team/lib/types/context-management.d.ts +0 -154
  103. package/packages/agent-team/lib/types/context-management.d.ts.map +0 -1
  104. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +0 -31
  105. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +0 -1
  106. package/packages/client-agent-team/lib/types/client/task-refs.js +0 -99
@@ -0,0 +1,15 @@
1
+ import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import { type TeamHumanIdentityFace } from './human-identity.ts';
3
+ export interface HumanSettingsSectionInjected {
4
+ /** The shared Human identity projection: profile facts plus post-write refresh. */
5
+ identity: TeamHumanIdentityFace;
6
+ /** Persist one renamed display name; the failure message when the Host refuses it. */
7
+ saveName: (name: string) => Promise<string | undefined>;
8
+ /** Upload one image file, persist its reference; the failure message when it does not stick. */
9
+ uploadAvatar: (file: File) => Promise<string | undefined>;
10
+ /** Clear the avatar; the identity falls back to the initial. */
11
+ removeAvatar: () => Promise<string | undefined>;
12
+ }
13
+ export type HumanSettingsSectionProps = PropsRuntime<'settings.section'> & PropsLocale<'team'> & HumanSettingsSectionInjected;
14
+ export declare function HumanSettingsSection(props: HumanSettingsSectionProps): import("react").JSX.Element;
15
+ //# sourceMappingURL=HumanSettingsSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HumanSettingsSection.d.ts","sourceRoot":"","sources":["../../../src/client/HumanSettingsSection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAEjF,OAAO,EAAoB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAiBlF,MAAM,WAAW,4BAA4B;IAC3C,mFAAmF;IACnF,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,sFAAsF;IACtF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACvD,gGAAgG;IAChG,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACzD,gEAAgE;IAChE,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CAChD;AAED,MAAM,MAAM,yBAAyB,GACjC,YAAY,CAAC,kBAAkB,CAAC,GAChC,WAAW,CAAC,MAAM,CAAC,GACnB,4BAA4B,CAAA;AAsBhC,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,+BAiKpE"}
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { Button, Input } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { useHumanIdentity } from "./human-identity.js";
5
+ import { useAvatarImage } from "./avatar-image.js";
6
+ import css from './human-settings.module.css';
7
+ /**
8
+ * The Human's own settings page: display name, avatar, and the version
9
+ * footnote.
10
+ *
11
+ * The page owns no durable fact and no copy of one. Name, avatar, and version
12
+ * all arrive from the shared identity projection, so a save here moves the
13
+ * message rows and member refs at the same moment; a failed write keeps the
14
+ * typed name in the field and reports the Host's own reason.
15
+ */
16
+ /** Host-side avatar ceiling (`ATTACHMENT_MAX_BYTES`): the settings page refuses larger files before the round trip. */
17
+ const AVATAR_MAX_BYTES = 10 * 1024 * 1024;
18
+ /**
19
+ * Run one durable write and reduce every failure to the message the page
20
+ * reports. A rejected Remote call (a dropped carrier, a refused write) is a
21
+ * failure like any other: without this the field would sit on "saving" forever
22
+ * and report nothing.
23
+ */
24
+ async function failureOf(action) {
25
+ try {
26
+ return await action();
27
+ }
28
+ catch (error) {
29
+ return error instanceof Error ? error.message : String(error);
30
+ }
31
+ }
32
+ /** First visible character of a display name, or the neutral `H` before one is known. */
33
+ function avatarInitial(name) {
34
+ const trimmed = (name ?? '').replace(/^@/, '').trim();
35
+ return trimmed === '' ? 'H' : trimmed.slice(0, 1).toUpperCase();
36
+ }
37
+ export function HumanSettingsSection(props) {
38
+ const { t, identity } = props;
39
+ const profile = useHumanIdentity(identity);
40
+ // Undefined means "follow the Host value": the field re-syncs whenever the
41
+ // profile changes underneath, without a background read clobbering a name
42
+ // the reader is still editing.
43
+ const [draft, setDraft] = useState(undefined);
44
+ const [saving, setSaving] = useState(false);
45
+ const [uploading, setUploading] = useState(false);
46
+ const [notice, setNotice] = useState(null);
47
+ const filePicker = useRef(null);
48
+ const name = draft ?? profile.name ?? '';
49
+ const dirty = draft !== undefined && draft.trim() !== (profile.name ?? '');
50
+ const emptyName = draft !== undefined && draft.trim() === '';
51
+ const hasAvatar = profile.avatarRef !== undefined;
52
+ // The circle answers to decoded bytes, not to a stored reference: the Host
53
+ // accepts any `image/…` payload, so a file this browser cannot read has to
54
+ // fall back to the initial exactly as a removed one does.
55
+ const avatar = useAvatarImage(profile.avatarUrl);
56
+ const submitName = async () => {
57
+ if (!dirty || emptyName || saving)
58
+ return;
59
+ setSaving(true);
60
+ setNotice(null);
61
+ const failure = await failureOf(() => props.saveName(name.trim()));
62
+ setSaving(false);
63
+ if (failure !== undefined) {
64
+ setNotice(t('humanSettingsNameFailed', { message: failure }));
65
+ return;
66
+ }
67
+ setDraft(undefined);
68
+ };
69
+ const pickAvatar = async (file) => {
70
+ if (file === undefined)
71
+ return;
72
+ setNotice(null);
73
+ if (!file.type.startsWith('image/')) {
74
+ setNotice(t('humanSettingsAvatarNotImage'));
75
+ return;
76
+ }
77
+ if (file.size > AVATAR_MAX_BYTES) {
78
+ setNotice(t('humanSettingsAvatarTooLarge'));
79
+ return;
80
+ }
81
+ setUploading(true);
82
+ const failure = await failureOf(() => props.uploadAvatar(file));
83
+ setUploading(false);
84
+ if (failure !== undefined)
85
+ setNotice(t('humanSettingsAvatarFailed', { message: failure }));
86
+ };
87
+ const removeAvatar = async () => {
88
+ setNotice(null);
89
+ const failure = await failureOf(() => props.removeAvatar());
90
+ if (failure !== undefined)
91
+ setNotice(t('humanSettingsAvatarFailed', { message: failure }));
92
+ };
93
+ // The page says what it is before it says what happened: the settings nav
94
+ // lists it beside the Harness's own pages, so the title alone leaves "whose
95
+ // profile is this, and where does it apply?" unanswered — and every state,
96
+ // including a failed read, has to answer it.
97
+ const pageHeader = _jsxs(_Fragment, { children: [_jsx("h2", { className: css.heading, children: t('humanSettingsTitle') }), _jsx("p", { className: css.intro, children: t('humanSettingsIntro') })] });
98
+ if (profile.status === 'loading' && profile.name === undefined) {
99
+ return _jsxs("div", { className: css.section, children: [pageHeader, _jsx("p", { className: css.state, role: "status", children: t('humanSettingsLoading') })] });
100
+ }
101
+ if (profile.name === undefined) {
102
+ return _jsxs("div", { className: css.section, children: [pageHeader, _jsx("p", { className: css.state, role: "alert", children: t('humanSettingsUnavailable', { message: profile.error ?? '' }) }), _jsx("div", { className: css.stateAction, children: _jsx(Button, { variant: "outline", onClick: () => { void identity.refresh(); }, children: t('retry') }) })] });
103
+ }
104
+ return (_jsxs("div", { className: css.section, children: [pageHeader, _jsxs("div", { className: css.rows, children: [_jsxs("div", { className: css.row, children: [_jsxs("div", { className: css.rowText, children: [_jsx("div", { className: css.title, children: t('humanSettingsName') }), _jsx("div", { className: css.desc, children: t('humanSettingsNameHint') })] }), _jsxs("form", { className: css.controls, onSubmit: (event) => {
105
+ event.preventDefault();
106
+ void submitName();
107
+ }, children: [_jsx(Input, { className: css.nameInput, "aria-label": t('humanSettingsName'), "aria-invalid": emptyName || undefined, value: name, disabled: saving, onChange: (event) => { setDraft(event.target.value); } }), _jsx(Button, { type: "submit", variant: "primary", disabled: saving || !dirty || emptyName, children: saving ? t('humanSettingsSaving') : t('humanSettingsSave') })] })] }), _jsxs("div", { className: css.row, children: [_jsxs("div", { className: css.rowText, children: [_jsx("div", { className: css.title, children: t('humanSettingsAvatar') }), _jsx("div", { className: css.desc, children: t('humanSettingsAvatarHint') })] }), _jsxs("div", { className: css.controls, children: [avatar.src === undefined
108
+ ? _jsx("span", { className: `${css.identity} ${css.identityFallback}`, "data-avatar": "initial", "aria-hidden": "true", children: avatarInitial(profile.name) })
109
+ : _jsx("img", { className: `${css.identity} ${css.identityImage}`, "data-avatar": "image", src: avatar.src, alt: "", onError: avatar.failed }), _jsx("input", { ref: filePicker, type: "file", accept: "image/*", tabIndex: -1, "aria-hidden": "true", hidden: true, onChange: (event) => {
110
+ void pickAvatar(event.target.files?.[0]);
111
+ event.target.value = '';
112
+ } }), _jsx(Button, { variant: "outline", disabled: uploading, onClick: () => { filePicker.current?.click(); }, children: uploading ? t('humanSettingsUploading') : hasAvatar ? t('humanSettingsReplace') : t('humanSettingsUpload') }), hasAvatar && _jsx(Button, { disabled: uploading, onClick: () => { void removeAvatar(); }, children: t('humanSettingsRemoveAvatar') })] })] })] }), _jsxs("div", { className: css.footnote, children: [_jsx("span", { children: t('humanSettingsVersion', { version: profile.version ?? '' }) }), _jsx("span", { "aria-hidden": "true", children: "\u00B7" }), _jsx("a", { className: css.link, href: profile.repoUrl ?? '', target: "_blank", rel: "noreferrer", children: "GitHub" }), profile.updateAvailable && profile.latestVersion !== undefined
113
+ ? _jsx("a", { className: css.link, href: `${profile.repoUrl ?? ''}/releases`, target: "_blank", rel: "noreferrer", children: t('humanSettingsUpdateAvailable', { version: profile.latestVersion }) })
114
+ : null] }), emptyName && _jsx("p", { className: css.notice, children: t('humanSettingsNameEmpty') }), notice === null ? null : _jsx("p", { className: css.notice, role: "alert", children: notice })] }));
115
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAE5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAclD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACjD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,cAAc,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;IAC3D,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,CAAC,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAA;IAC7E,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IACjE,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1F,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA2O9P"}
1
+ {"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAE5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAclD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACjD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,cAAc,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;IAC3D,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,CAAC,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAA;IAC7E,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IACjE,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1F,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA+O9P"}
@@ -9,7 +9,7 @@ import { mintRequestId } from "./requests.js";
9
9
  import { diagnosticText, restartOffered } from "./TeamPresenceDot.js";
10
10
  import { TeamRowMenu } from "./TeamRowMenu.js";
11
11
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
12
- import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
12
+ import { AgentEditorDialog, ModelPickerField, sameModel, warmModelCatalog } from "./TeamMemberEditor.js";
13
13
  import { TeamAgentImport } from "./TeamAgentImport.js";
14
14
  import createCss from './create.module.css';
15
15
  import css from './sidebar.module.css';
@@ -112,6 +112,10 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
112
112
  setLoading(false);
113
113
  }, [loadMembers, workspaceId, followRollover]);
114
114
  useEffect(() => { void refresh(); }, [refresh]);
115
+ // Warm the Host model catalog while the roster loads, so the create and
116
+ // edit pickers open with rows instead of paying the first read on open.
117
+ // Mount-scoped (not per refresh): presence wakes must not refetch it.
118
+ useEffect(() => { warmModelCatalog(loadModels); }, [loadModels]);
115
119
  useEffect(() => subscribeChanges({ kind: 'workspace', workspaceId }, update => {
116
120
  if (update.type === 'failed') {
117
121
  setError(update.message);
@@ -144,7 +148,7 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
144
148
  try {
145
149
  const result = await addMember(request);
146
150
  if (result.ok) {
147
- const committed = { ...result.value.status, workspaceIds: [result.value.status.member.workspaceId] };
151
+ const committed = { ...result.value.status, workspaceIds: result.value.workspaceIds };
148
152
  setMembers(current => {
149
153
  const retained = current.filter(status => status.member.memberId !== committed.member.memberId);
150
154
  return committed.member.state === 'inactive' || committed.member.state === 'archived' ? retained : [...retained, committed];
@@ -5,14 +5,35 @@ export interface TeamAvatarOwner {
5
5
  /** Public handle, or the raw Member id when the roster no longer names them. */
6
6
  readonly name: string;
7
7
  }
8
+ /**
9
+ * The reader's own identity, as the Client's one Human projection holds it.
10
+ *
11
+ * The stack draws every owner from the shared Member language — hue plus
12
+ * initial — which is the whole identity an Agent has. The Human is the one
13
+ * Member with a picture, so that one chip draws the picture and falls back to
14
+ * this name's initial, exactly as the timeline already does.
15
+ */
16
+ export interface TeamAvatarHuman {
17
+ readonly memberId: AgentTeamMemberId;
18
+ readonly name: string;
19
+ readonly avatarUrl?: string | undefined;
20
+ }
21
+ /**
22
+ * Owners as a seat names them: the Client's Human identity outranks the name
23
+ * the Host projected for that actor, so a rename moves label and initial
24
+ * together in every seat that draws the stack.
25
+ */
26
+ export declare function namedAvatarOwners(owners: readonly TeamAvatarOwner[], human: TeamAvatarHuman | undefined): readonly TeamAvatarOwner[];
8
27
  /**
9
28
  * The compact "who is on this work" stack: overlapping 18px Member circles in
10
29
  * the shared identity language, capped at three plus a `+N` chip. The circles
11
30
  * are presentational, so the stack is one `role="img"` whose label carries the
12
31
  * whole roster — three anonymous initials would read as noise.
13
32
  */
14
- export declare function TeamAvatarStack({ owners, label }: {
33
+ export declare function TeamAvatarStack({ owners, label, human }: {
15
34
  readonly owners: readonly TeamAvatarOwner[];
16
35
  readonly label: string;
36
+ /** The reader's own identity; one owner matching it draws the Human's picture. */
37
+ readonly human?: TeamAvatarHuman | undefined;
17
38
  }): import("react").JSX.Element | null;
18
39
  //# sourceMappingURL=TeamAvatarStack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamAvatarStack.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAvatarStack.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAOvE,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IACjD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,sCAQA"}
1
+ {"version":3,"file":"TeamAvatarStack.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAvatarStack.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAQvE,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,EAAE,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,eAAe,EAAE,CAGpI;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IACxD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,kFAAkF;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;CAC7C,sCAQA"}
@@ -1,20 +1,43 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAvatarImage } from "./avatar-image.js";
2
3
  import { memberHue } from "./team-formatters.js";
3
4
  import css from './avatar-stack.module.css';
4
5
  /** Distinct owners past this count collapse into one `+N` chip. */
5
6
  const MAX_VISIBLE = 3;
7
+ /**
8
+ * Owners as a seat names them: the Client's Human identity outranks the name
9
+ * the Host projected for that actor, so a rename moves label and initial
10
+ * together in every seat that draws the stack.
11
+ */
12
+ export function namedAvatarOwners(owners, human) {
13
+ if (human === undefined)
14
+ return owners;
15
+ return owners.map(owner => owner.memberId === human.memberId ? { memberId: owner.memberId, name: human.name } : owner);
16
+ }
6
17
  /**
7
18
  * The compact "who is on this work" stack: overlapping 18px Member circles in
8
19
  * the shared identity language, capped at three plus a `+N` chip. The circles
9
20
  * are presentational, so the stack is one `role="img"` whose label carries the
10
21
  * whole roster — three anonymous initials would read as noise.
11
22
  */
12
- export function TeamAvatarStack({ owners, label }) {
23
+ export function TeamAvatarStack({ owners, label, human }) {
13
24
  if (owners.length === 0)
14
25
  return null;
15
26
  const shown = owners.slice(0, MAX_VISIBLE);
16
27
  const overflow = owners.length - shown.length;
17
- return _jsxs("span", { className: css.stack, role: "img", "aria-label": label, children: [shown.map(owner => _jsx("span", { className: css.avatar, style: { '--team-avatar-hue': memberHue(owner.memberId) }, children: initial(owner.name) }, owner.memberId)), overflow > 0 && _jsx("span", { className: css.overflow, children: `+${overflow}` })] });
28
+ return _jsxs("span", { className: css.stack, role: "img", "aria-label": label, children: [shown.map(owner => _jsx(OwnerAvatar, { owner: owner, human: human }, owner.memberId)), overflow > 0 && _jsx("span", { className: css.overflow, children: `+${overflow}` })] });
29
+ }
30
+ /**
31
+ * One circle. Only an owner the seat identifies as the Human consults an image
32
+ * at all, and those bytes keep the initial whenever they do not decode — the
33
+ * same promise, and the same hook, the timeline avatar makes.
34
+ */
35
+ function OwnerAvatar({ owner, human }) {
36
+ const image = useAvatarImage(human !== undefined && owner.memberId === human.memberId ? human.avatarUrl : undefined);
37
+ const hue = { '--team-avatar-hue': memberHue(owner.memberId) };
38
+ if (image.src === undefined)
39
+ return _jsx("span", { className: css.avatar, style: hue, children: initial(owner.name) });
40
+ return _jsx("img", { className: css.avatarImage, style: hue, src: image.src, alt: "", "aria-hidden": "true", onError: image.failed });
18
41
  }
19
42
  /** First visible character of a handle — and of a raw Member id when that is all there is. */
20
43
  function initial(name) {
@@ -5,6 +5,9 @@ import type { TeamDraftStore } from './drafts.ts';
5
5
  interface TeamChannelPageProps {
6
6
  readonly workspaceId: WorkspaceId;
7
7
  readonly channelRef: AgentTeamChannelRef;
8
+ /** The Human's own display name and avatar, from the shared identity projection. */
9
+ readonly humanName: string;
10
+ readonly humanAvatarUrl?: string | undefined;
8
11
  readonly loadChannels: TeamConversationProps['loadChannels'];
9
12
  readonly subscribeChanges: TeamConversationProps['subscribeChanges'];
10
13
  readonly loadMembers: TeamConversationProps['loadMembers'];
@@ -19,9 +22,11 @@ interface TeamChannelPageProps {
19
22
  readonly selectThread: TeamConversationProps['selectThread'];
20
23
  readonly selectChannel: TeamConversationProps['selectChannel'];
21
24
  readonly resolveTaskRefs: TeamConversationProps['resolveTaskRefs'];
25
+ readonly resolveThreadRefs: TeamConversationProps['resolveThreadRefs'];
26
+ readonly openMemberSession: TeamConversationProps['openMemberSession'];
22
27
  readonly backToChannels: TeamConversationProps['backToChannels'];
23
28
  readonly t: TeamConversationProps['t'];
24
29
  }
25
- export declare function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscribeChanges, loadMembers, loadInbox, drafts, putAttachment, getAttachment, sendMessage, joinChannel, removeChannelMember, selectThread, selectChannel, backToChannels, resolveTaskRefs, t }: TeamChannelPageProps): import("react").JSX.Element;
30
+ export declare function TeamChannelPage({ workspaceId, channelRef, humanName, humanAvatarUrl, loadChannels, subscribeChanges, loadMembers, loadInbox, drafts, putAttachment, getAttachment, sendMessage, joinChannel, removeChannelMember, selectThread, selectChannel, backToChannels, resolveTaskRefs, resolveThreadRefs, openMemberSession, t }: TeamChannelPageProps): import("react").JSX.Element;
26
31
  export {};
27
32
  //# sourceMappingURL=TeamChannelPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAiB/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAoCD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAgX9R"}
1
+ {"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAmB/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAkDD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA4Y/V"}
@@ -1,18 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
2
+ import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { Button, IconChevronLeftOutlineRegular, IconChevronRightOutlineRegular, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { mintRequestId, uploadComposerFiles } from "./requests.js";
5
5
  import { TeamComposer } from "./TeamComposer.js";
6
6
  import { TeamMemberRow } from "./TeamMemberRow.js";
7
7
  import { TeamMessage } from "./TeamMessage.js";
8
- import { TeamAvatarStack } from "./TeamAvatarStack.js";
8
+ import { namedAvatarOwners, TeamAvatarStack } from "./TeamAvatarStack.js";
9
9
  import { TeamCountBadge } from "./TeamCountBadge.js";
10
10
  import { TeamRunDivider } from "./TeamRunDivider.js";
11
11
  import { claimersLabel, formatAbsoluteTime, formatInboxTime, formatTaskStatus, taskStatusDot, mentionNamesOf } from "./team-formatters.js";
12
12
  import { TeamStateDot } from "./TeamStateDot.js";
13
13
  import { useChannelMembership } from "./team-membership.js";
14
14
  import { useTimelineScroll } from "./timeline-scroll.js";
15
- import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
15
+ import { hostTaskRefLookup, jumpToTaskThread } from "./refs.js";
16
+ import { hostThreadRefLookup, jumpToThread } from "./refs.js";
17
+ import { rosterChannelName, rosterMember } from "./refs.js";
16
18
  import { chunkRunsWithDays, isRunGap } from "./team-separators.js";
17
19
  import channelCss from './channel.module.css';
18
20
  import css from './conversation.module.css';
@@ -44,11 +46,24 @@ function followUpAt(item) {
44
46
  const lastActivityAt = item.lastActivityAt;
45
47
  return lastActivityAt === undefined || lastActivityAt === item.message.occurredAt ? undefined : lastActivityAt;
46
48
  }
49
+ /**
50
+ * What the door prints about follow-up activity, or nothing. A resolved Task
51
+ * stops printing it: the status word beside the door already says nothing is
52
+ * moving, and the instant it would print is the moment it resolved — repeated
53
+ * on every finished row. The precise instant stays on the control's title, one
54
+ * hover away. A taskless Thread never resolves, so a discussion keeps the
55
+ * recency that is the whole of its state line: it wears no status word, dot, or
56
+ * owner stack.
57
+ */
58
+ function printedActivityAt(item) {
59
+ const status = item.task?.status;
60
+ return status === 'done' || status === 'closed' ? undefined : followUpAt(item);
61
+ }
47
62
  /** Host unread per Thread, keyed for the feed's rows: zero unread is the absence of a badge, not a row. */
48
63
  function unreadCounts(inbox) {
49
64
  return new Map(inbox.items.filter(item => item.unreadCount > 0).map(item => [item.thread.threadRef, item.unreadCount]));
50
65
  }
51
- export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscribeChanges, loadMembers, loadInbox, drafts, putAttachment, getAttachment, sendMessage, joinChannel, removeChannelMember, selectThread, selectChannel, backToChannels, resolveTaskRefs, t }) {
66
+ export function TeamChannelPage({ workspaceId, channelRef, humanName, humanAvatarUrl, loadChannels, subscribeChanges, loadMembers, loadInbox, drafts, putAttachment, getAttachment, sendMessage, joinChannel, removeChannelMember, selectThread, selectChannel, backToChannels, resolveTaskRefs, resolveThreadRefs, openMemberSession, t }) {
52
67
  const [view, setView] = useState();
53
68
  const [members, setMembers] = useState([]);
54
69
  const [unreadByThread, setUnreadByThread] = useState(new Map());
@@ -76,8 +91,10 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
76
91
  const refreshSequenceRef = useRef(0);
77
92
  const channelLastItem = view?.items[view.items.length - 1];
78
93
  // Branded-ref navigation for message bodies: channel refs hop directly,
79
- // task refs resolve against this Channel's loaded timeline and degrade to a
80
- // no-op when the target is not reachable from here.
94
+ // task and thread refs resolve against this Channel's loaded timeline and
95
+ // fall back to the Host resolver, which also accepts abbreviated spellings.
96
+ // Unresolvable refs never become links (see TeamMessage), so this path only
97
+ // fires for refs the Host already confirmed.
81
98
  const openRef = (ref) => {
82
99
  if (ref.startsWith('channel:')) {
83
100
  if (ref !== channelRef)
@@ -90,18 +107,9 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
90
107
  selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
91
108
  return;
92
109
  }
93
- // A ref may point outside the currently loaded Channel window. Ask the
94
- // Host for the bounded Thread view so it can provide the home Channel.
95
- void loadChannels({ workspaceId, threadRef: ref, includeActivities: false, limit: 1 }).then(result => {
96
- if (!result.ok)
97
- return;
98
- const target = result.value.items[0];
99
- if (target !== undefined) {
100
- const targetChannel = result.value.channels.find(channel => channel.channelRef === target.message.channelRef);
101
- if (targetChannel !== undefined)
102
- selectThread(target.thread.threadRef, targetChannel.channelRef, target.task?.taskRef, target.taskNumber);
103
- }
104
- });
110
+ // Not in the loaded timeline (or cited by abbreviation): resolve through
111
+ // the Host and jump to the Thread's home Channel.
112
+ jumpToThread(resolveThreadRefs, workspaceId, ref, selectThread);
105
113
  return;
106
114
  }
107
115
  if (ref.startsWith('task:')) {
@@ -116,6 +124,33 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
116
124
  }
117
125
  };
118
126
  const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
127
+ const lookupThreadRefs = hostThreadRefLookup(resolveThreadRefs, workspaceId);
128
+ // Roster chips resolve synchronously from loaded data: channel names from
129
+ // the loaded feed windows, member facts from the member list. Anything
130
+ // outside stays plain text — the same rule unresolvable refs follow. The
131
+ // lookups key on roster content, not array identity: every refresh hands
132
+ // over fresh arrays, and the memoized rows must survive change bursts that
133
+ // leave the roster itself untouched.
134
+ const rosterKey = useMemo(() => [
135
+ (view?.channels ?? []).map(channel => `${channel.channelRef}=${channel.name}=${channel.state}`).join(','),
136
+ members.map(status => `${status.member.memberId}=${status.member.handle}=${status.member.sessionId}=${status.availability}`).join(','),
137
+ view?.humanMemberId ?? '',
138
+ ].join(';'), [view, members]);
139
+ const channelNameOf = useMemo(() => {
140
+ const channels = view?.channels ?? [];
141
+ return (ref) => rosterChannelName(channels, ref);
142
+ }, [rosterKey]);
143
+ const memberOf = useMemo(() => {
144
+ const humanMemberId = view?.humanMemberId;
145
+ return (ref) => rosterMember(members, humanMemberId, humanName, ref);
146
+ }, [rosterKey, humanName]);
147
+ // The Thread entry row's owner stack speaks the same grammar as the Inbox
148
+ // row's, Human included: the Host names which actor is the reader, the
149
+ // Client's identity projection says what that actor looks like.
150
+ const humanMemberId = view?.humanMemberId;
151
+ const humanIdentity = humanMemberId === undefined
152
+ ? undefined
153
+ : { memberId: humanMemberId, name: humanName, ...(humanAvatarUrl === undefined ? {} : { avatarUrl: humanAvatarUrl }) };
119
154
  const timeline = useTimelineScroll(`${view?.items.length ?? 0}:${channelLastItem?.message.messageRef ?? ''}`);
120
155
  const channel = view?.channels.find(item => item.channelRef === channelRef);
121
156
  const channelMemberIds = new Set(view?.members.filter(item => item.channelRef === channelRef).map(item => item.memberId) ?? []);
@@ -364,7 +399,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
364
399
  : _jsx("div", { className: css.messageRun, children: block.items.map((item, index) => {
365
400
  const senderStatus = members.find(member => member.member.memberId === item.message.sender);
366
401
  const human = item.message.sender === view.humanMemberId;
367
- const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
402
+ const sender = human ? humanName : senderStatus?.member.handle ?? item.message.sender;
368
403
  const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
369
404
  const task = item.task;
370
405
  // The entry's own line is the gate under the body, and its state
@@ -373,7 +408,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
373
408
  // of it — the Thread page owns that surface.
374
409
  const unread = unreadByThread.get(item.thread.threadRef) ?? 0;
375
410
  // A turn divider already labels the gapped entry; its row stays chrome-free.
376
- return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsx(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, handleByMember), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: item.message.topLevel && _jsx(ThreadEntryRow, { item: item, owners: item.claimOwners, unread: unread, t: t, onOpen: () => { selectThread(item.thread.threadRef, channelRef, task?.taskRef, item.taskNumber); } }) })] }, item.message.messageRef);
411
+ return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsx(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, ...(humanAvatarUrl === undefined ? {} : { avatarUrl: humanAvatarUrl }), body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, handleByMember, humanName), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, onResolveThreadRefs: lookupThreadRefs, channelNameOf: channelNameOf, memberOf: memberOf, onOpenMemberSession: openMemberSession, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: item.message.topLevel && _jsx(ThreadEntryRow, { item: item, owners: item.claimOwners, unread: unread, human: humanIdentity, t: t, onOpen: () => { selectThread(item.thread.threadRef, channelRef, task?.taskRef, item.taskNumber); } }) })] }, item.message.messageRef);
377
412
  }) }, `run-${block.items[0].message.messageRef}`))] }) }), channel !== undefined ? _jsx(TeamComposer, { members: channelMembers, drafts: drafts, draftKey: draftKey, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onEdit: clearSendState, onSubmit: () => { void send(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, asTask: asTask, onAsTaskChange: setAsTask, t: t }, draftKey) : _jsx("div", {})] });
378
413
  }
379
414
  /**
@@ -385,27 +420,30 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
385
420
  * carried nothing else. The unread capsule is the only member a taskless
386
421
  * discussion can carry: it needs no Task.
387
422
  */
388
- function ThreadStateCluster({ task, owners, unread, t }) {
423
+ function ThreadStateCluster({ task, owners, unread, human, t }) {
389
424
  if (task === undefined && unread === 0)
390
425
  return null;
391
- return _jsxs("span", { className: css.stateCluster, children: [task !== undefined && _jsx(TeamAvatarStack, { owners: owners, label: claimersLabel(owners, t) }), task !== undefined && _jsx(TeamStateDot, { size: 8, state: taskStatusDot(task.status) }), task !== undefined && _jsx("span", { className: css.statusWord, children: formatTaskStatus(task.status, t) }), _jsx(TeamCountBadge, { count: unread })] });
426
+ const namedOwners = namedAvatarOwners(owners, human);
427
+ return _jsxs("span", { className: css.stateCluster, children: [task !== undefined && _jsx(TeamAvatarStack, { owners: namedOwners, label: claimersLabel(namedOwners, t), human: human }), task !== undefined && _jsx(TeamStateDot, { size: 8, state: taskStatusDot(task.status) }), task !== undefined && _jsx("span", { className: css.statusWord, children: formatTaskStatus(task.status, t) }), _jsx(TeamCountBadge, { count: unread })] });
392
428
  }
393
429
  /**
394
430
  * The gate under one top-level Message: the single row that opens its Thread,
395
431
  * and the one line that says what stands there. State leads it, then what the
396
432
  * entry is: a Task entry keeps its number so the status word never floats free
397
- * of the Task it describes, follow-up activity adds when the work last moved,
398
- * and an unanswered Thread says only 回复. The instant uses the Inbox's own
399
- * 今天/昨天 form with the precise local time on the control's title, and an
400
- * unread Thread says so in the control's label rather than only in pixels.
433
+ * of the Task it describes, follow-up activity adds when the work last moved —
434
+ * while the work is still unfinished — and an unanswered Thread says only 回复.
435
+ * The instant uses the Inbox's own 今天/昨天 form with the precise local time on
436
+ * the control's title, and an unread Thread says so in the control's label
437
+ * rather than only in pixels.
401
438
  */
402
- function ThreadEntryRow({ item, owners, unread, t, onOpen }) {
439
+ function ThreadEntryRow({ item, owners, unread, human, t, onOpen }) {
403
440
  const taskNumber = item.taskNumber;
404
441
  const followUp = followUpAt(item);
442
+ const printedActivity = printedActivityAt(item);
405
443
  const label = taskNumber !== undefined
406
444
  ? t('taskLabel', { number: taskNumber })
407
445
  : followUp === undefined ? t('replyAction') : t('threadLabel');
408
- const text = followUp === undefined ? label : `${label} · ${t('recentActivity', { time: formatInboxTime(followUp, t) })}`;
446
+ const text = printedActivity === undefined ? label : `${label} · ${t('recentActivity', { time: formatInboxTime(printedActivity, t) })}`;
409
447
  const openLabel = taskNumber === undefined
410
448
  ? unread > 0 ? t('openThreadUnread', { count: unread }) : t('openThread')
411
449
  : unread > 0 ? t('openTaskUnread', { number: taskNumber, count: unread }) : t('openTask', { number: taskNumber });
@@ -413,5 +451,5 @@ function ThreadEntryRow({ item, owners, unread, t, onOpen }) {
413
451
  // owner stack keeps its own accessible name — a labeled button prunes its
414
452
  // descendants from the accessibility tree — and so the control's name says
415
453
  // exactly what clicking it does.
416
- return _jsxs("span", { className: css.entryLine, "data-thread-entry": "", children: [_jsx(ThreadStateCluster, { task: item.task, owners: owners, unread: unread, t: t }), _jsxs("button", { type: "button", className: css.entryRow, "aria-label": openLabel, ...(followUp === undefined ? {} : { title: formatAbsoluteTime(followUp) }), onClick: onOpen, children: [_jsx("span", { children: text }), _jsx("span", { className: css.entryArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutlineRegular, { size: 12 }) })] })] });
454
+ return _jsxs("span", { className: css.entryLine, "data-thread-entry": "", children: [_jsx(ThreadStateCluster, { task: item.task, owners: owners, unread: unread, human: human, t: t }), _jsxs("button", { type: "button", className: css.entryRow, "aria-label": openLabel, ...(followUp === undefined ? {} : { title: formatAbsoluteTime(followUp) }), onClick: onOpen, children: [_jsx("span", { children: text }), _jsx("span", { className: css.entryArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutlineRegular, { size: 12 }) })] })] });
417
455
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';
3
- import { IconChecklistOutlineRegular, IconPaperclipOutlineRegular, IconSendOutlineRegular, Tooltip, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { IconChecklistOutlineMedium, IconPaperclipOutlineMedium, IconSendOutlineRegular, Tooltip, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
5
  import { allMentionMembers, containsAllMention, containsMention, mentionedMemberIds } from "./team-formatters.js";
6
6
  import css from './composer.module.css';
@@ -259,7 +259,7 @@ export function TeamComposer({ members, followerMemberIds, drafts, draftKey, pen
259
259
  if (chosen.length > 0 && pendingFiles !== undefined)
260
260
  onFilesChange([...pendingFiles, ...chosen]);
261
261
  event.target.value = '';
262
- } }), _jsx(Tooltip, { label: t('attachFiles'), side: "top", delayMs: 500, children: _jsx("button", { type: "button", className: css.attachButton, "aria-label": t('attachFiles'), disabled: pending, onClick: () => { fileInputRef.current?.click(); }, children: _jsx(IconPaperclipOutlineRegular, { size: 14 }) }) })] })), onAsTaskChange !== undefined && (_jsxs("button", { type: "button", className: asTask === true ? `${css.asTaskPill} ${css.asTaskPillOn}` : css.asTaskPill, "aria-label": t('asTask'), "aria-pressed": asTask === true, title: t('asTask'), disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: [_jsx(IconChecklistOutlineRegular, { size: 14 }), _jsx("span", { className: css.asTaskLabel, children: t('asTask') })] })), _jsx("button", { type: "submit", className: css.sendButton, "aria-label": pending ? t('sendingMessage') : t('sendMessage'), disabled: pending || draft.trim() === '', onMouseDown: event => {
262
+ } }), _jsx(Tooltip, { label: t('attachFiles'), side: "top", delayMs: 500, children: _jsx("button", { type: "button", className: css.attachButton, "aria-label": t('attachFiles'), disabled: pending, onClick: () => { fileInputRef.current?.click(); }, children: _jsx(IconPaperclipOutlineMedium, { size: 14 }) }) })] })), onAsTaskChange !== undefined && (_jsxs("button", { type: "button", className: asTask === true ? `${css.asTaskPill} ${css.asTaskPillOn}` : css.asTaskPill, "aria-label": t('asTask'), "aria-pressed": asTask === true, title: t('asTask'), disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: [_jsx(IconChecklistOutlineMedium, { size: 14 }), _jsx("span", { className: css.asTaskLabel, children: t('asTask') })] })), _jsx("button", { type: "submit", className: css.sendButton, "aria-label": pending ? t('sendingMessage') : t('sendMessage'), disabled: pending || draft.trim() === '', onMouseDown: event => {
263
263
  // Keep the composer focused when the send control is clicked.
264
264
  event.preventDefault();
265
265
  inputRef.current?.focus({ preventScroll: true });
@@ -1,3 +1,3 @@
1
1
  import type { TeamConversationProps } from './slots.ts';
2
- export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
2
+ export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, humanIdentity, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, backToWorkspace, backToChannels, resolveTaskRefs, resolveThreadRefs, openMemberSession }: TeamConversationProps): import("react").JSX.Element;
3
3
  //# sourceMappingURL=TeamConversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAMvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BA0BzZ"}
1
+ {"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAOvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,EAAE,qBAAqB,+BAgC9c"}
@@ -1,24 +1,31 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSyncExternalStore } from 'react';
3
+ import { useHumanIdentity } from "./human-identity.js";
3
4
  import { TeamChannelPage } from "./TeamChannelPage.js";
4
5
  import { TeamInboxPage } from "./TeamInboxPage.js";
5
6
  import { TeamThreadPage } from "./TeamThreadPage.js";
6
7
  import css from './conversation.module.css';
7
- export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, backToWorkspace, backToChannels, resolveTaskRefs }) {
8
+ export function TeamConversation({ t, useWorkspaces, navigation, drafts, humanIdentity, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, backToWorkspace, backToChannels, resolveTaskRefs, resolveThreadRefs, openMemberSession }) {
8
9
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
10
+ // Every seat names and draws the Human from this one projection: the
11
+ // localized fallback stands only until the first profile read lands.
12
+ const identity = useHumanIdentity(humanIdentity);
13
+ const humanName = identity.name ?? t('human');
9
14
  const workspaces = useWorkspaces(state => state.items);
10
15
  const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
11
16
  if (navigationState.inbox === true) {
12
17
  // The Inbox page is global: it merges every visible Workspace and needs no
13
18
  // selected Workspace. Selecting Inbox clears the Channel/Thread faces, so
14
- // this face owns the seat while the flag stands.
15
- return _jsx(TeamInboxPage, { useWorkspaces: useWorkspaces, loadInbox: loadInbox, subscribeChanges: subscribeChanges, selectWorkspace: selectWorkspace, selectThread: selectThread, t: t }, "inbox");
19
+ // this face owns the seat while the flag stands. The Human identity rides
20
+ // along for the same reason it rides on the other two faces: a row that
21
+ // names or draws the reader must draw the one identity every seat draws.
22
+ return _jsx(TeamInboxPage, { useWorkspaces: useWorkspaces, loadInbox: loadInbox, subscribeChanges: subscribeChanges, selectWorkspace: selectWorkspace, selectThread: selectThread, humanName: humanName, ...(identity.avatarUrl === undefined ? {} : { humanAvatarUrl: identity.avatarUrl }), t: t }, "inbox");
16
23
  }
17
24
  if (current !== undefined && navigationState.threadRef !== undefined) {
18
- return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
25
+ return _jsx(TeamThreadPage, { humanName: humanName, ...(identity.avatarUrl === undefined ? {} : { humanAvatarUrl: identity.avatarUrl }), workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, resolveThreadRefs: resolveThreadRefs, openMemberSession: openMemberSession, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
19
26
  }
20
27
  if (current !== undefined && navigationState.channelRef !== undefined) {
21
- return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, loadInbox: loadInbox, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
28
+ return _jsx(TeamChannelPage, { humanName: humanName, ...(identity.avatarUrl === undefined ? {} : { humanAvatarUrl: identity.avatarUrl }), workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, loadInbox: loadInbox, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, resolveThreadRefs: resolveThreadRefs, openMemberSession: openMemberSession, t: t }, navigationState.channelRef);
22
29
  }
23
30
  const welcome = current === undefined
24
31
  ? { eyebrow: t('teamMode'), title: t('team'), body: t('empty') }
@@ -6,6 +6,9 @@ interface TeamInboxPageProps {
6
6
  readonly subscribeChanges: TeamConversationProps['subscribeChanges'];
7
7
  readonly selectWorkspace: TeamConversationProps['selectWorkspace'];
8
8
  readonly selectThread: TeamConversationProps['selectThread'];
9
+ /** The Human's display name, from the Client's one identity projection. */
10
+ readonly humanName: string;
11
+ readonly humanAvatarUrl?: string | undefined;
9
12
  readonly t: TeamConversationProps['t'];
10
13
  }
11
14
  /**
@@ -20,6 +23,6 @@ interface TeamInboxPageProps {
20
23
  * holding unread is only ever in the queue: the Host already excludes it from
21
24
  * the tail, and this page never re-derives that judgement.
22
25
  */
23
- export declare function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, t }: TeamInboxPageProps): import("react").JSX.Element;
26
+ export declare function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, humanName, humanAvatarUrl, t }: TeamInboxPageProps): import("react").JSX.Element;
24
27
  export {};
25
28
  //# sourceMappingURL=TeamInboxPage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamInboxPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAavD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,mGAAmG;IACnG,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,kBAAkB,+BA0GjI"}
1
+ {"version":3,"file":"TeamInboxPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAavD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,mGAAmG;IACnG,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE,kBAAkB,+BAqH5J"}