@wowyuarm/dsh-agent-team 0.1.12 → 0.1.13

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 (85) hide show
  1. package/README.md +2 -0
  2. package/README.zh.md +2 -0
  3. package/cordis.patch.yml +7 -3
  4. package/package.json +14 -2
  5. package/packages/agent-team/README.md +2 -2
  6. package/packages/agent-team/README.zh.md +2 -2
  7. package/packages/agent-team/lib/index.js +151 -69
  8. package/packages/agent-team/lib/ledger.js +249 -31
  9. package/packages/agent-team/lib/member-context.js +18 -1
  10. package/packages/agent-team/lib/member-runtime.js +5 -4
  11. package/packages/agent-team/lib/mentions.js +48 -24
  12. package/packages/agent-team/lib/spec.js +19 -0
  13. package/packages/agent-team/lib/typert.host.js +187 -41
  14. package/packages/agent-team/lib/typert.remote-client.d.ts +7 -3
  15. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  16. package/packages/agent-team/lib/typert.remote-client.js +132 -27
  17. package/packages/agent-team/lib/types/index.d.ts +32 -7
  18. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  19. package/packages/agent-team/lib/types/ledger.d.ts +46 -2
  20. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/member-context.d.ts +6 -0
  22. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/mentions.d.ts +20 -7
  24. package/packages/agent-team/lib/types/mentions.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/types/entities.d.ts +3 -2
  27. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/types/operations.d.ts +37 -1
  29. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/types/requests-results.d.ts +50 -10
  31. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts +87 -0
  33. package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts +59 -0
  35. package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts.map +1 -0
  36. package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts +57 -0
  37. package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts.map +1 -0
  38. package/packages/agent-team/lib/vendor/storage-sqlite/index.js +139 -0
  39. package/packages/agent-team/lib/vendor/storage-sqlite/schema.js +120 -0
  40. package/packages/agent-team/lib/vendor/storage-sqlite/unit.js +132 -0
  41. package/packages/client-agent-team/README.md +1 -1
  42. package/packages/client-agent-team/README.zh.md +1 -1
  43. package/packages/client-agent-team/lib/client.js +603 -343
  44. package/packages/client-agent-team/lib/client.js.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts +12 -0
  46. package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts.map +1 -0
  47. package/packages/client-agent-team/lib/types/client/TeamAgentImport.js +69 -0
  48. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -1
  49. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +29 -17
  51. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +19 -44
  53. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +2 -2
  54. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +15 -14
  56. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
  57. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +15 -29
  60. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  62. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  63. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/index.js +8 -1
  65. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -0
  66. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  67. package/packages/client-agent-team/lib/types/client/locales.js +18 -0
  68. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  69. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +15 -31
  71. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/team-changes.js +82 -111
  73. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +13 -11
  74. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/team-formatters.js +29 -35
  76. package/packages/tool-agent-team/README.md +1 -1
  77. package/packages/tool-agent-team/README.zh.md +1 -1
  78. package/packages/tool-agent-team/lib/host-access.js +19 -0
  79. package/packages/tool-agent-team/lib/index.js +42 -33
  80. package/packages/tool-agent-team/lib/types/host-access.d.ts +10 -0
  81. package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -1
  82. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +0 -41
  84. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +0 -1
  85. package/packages/client-agent-team/lib/types/client/scope-coverage.js +0 -64
@@ -0,0 +1,12 @@
1
+ import type { WorkspaceId } from '@deepseek-ai/dsh-api-workspace-controller/client';
2
+ import type { TeamSidebarProps } from './slots.ts';
3
+ /** Selection is a Host join, not a new Agent or a copied Session. */
4
+ export declare function TeamAgentImport({ workspaceId, loadMembers, joinWorkspace, onJoined, onPending, t }: {
5
+ readonly workspaceId: WorkspaceId;
6
+ readonly loadMembers: TeamSidebarProps['loadMembers'];
7
+ readonly joinWorkspace: TeamSidebarProps['joinWorkspace'];
8
+ readonly onJoined: () => Promise<void>;
9
+ readonly onPending: (pending: boolean) => void;
10
+ readonly t: TeamSidebarProps['t'];
11
+ }): import("react").JSX.Element;
12
+ //# sourceMappingURL=TeamAgentImport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamAgentImport.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentImport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAKlD,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IACnG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA2DA"}
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { TeamMemberRow } from "./TeamMemberRow.js";
5
+ import { mintRequestId } from "./requests.js";
6
+ import css from './create.module.css';
7
+ /** Selection is a Host join, not a new Agent or a copied Session. */
8
+ export function TeamAgentImport({ workspaceId, loadMembers, joinWorkspace, onJoined, onPending, t }) {
9
+ const [members, setMembers] = useState([]);
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState();
12
+ const [pending, setPending] = useState();
13
+ const retry = useRef();
14
+ const busy = useRef(false);
15
+ const generation = useRef(0);
16
+ const load = useCallback(async () => {
17
+ const current = ++generation.current;
18
+ setLoading(true);
19
+ setError(undefined);
20
+ try {
21
+ const result = await loadMembers({});
22
+ if (current !== generation.current)
23
+ return;
24
+ if (!result.ok)
25
+ throw new Error(result.error.message);
26
+ setMembers(result.value.filter(status => (status.member.state === 'enabled' || status.member.state === 'suspended') && !status.workspaceIds.includes(workspaceId)));
27
+ }
28
+ catch (cause) {
29
+ if (current === generation.current)
30
+ setError(cause instanceof Error ? cause.message : String(cause));
31
+ }
32
+ finally {
33
+ if (current === generation.current)
34
+ setLoading(false);
35
+ }
36
+ }, [loadMembers, workspaceId]);
37
+ useEffect(() => { void load(); return () => { generation.current++; }; }, [load]);
38
+ const join = async (status) => {
39
+ if (busy.current)
40
+ return;
41
+ busy.current = true;
42
+ setPending(status.member.memberId);
43
+ onPending(true);
44
+ setError(undefined);
45
+ const request = retry.current?.memberId === status.member.memberId ? retry.current : {
46
+ requestId: mintRequestId(), workspaceId, memberId: status.member.memberId,
47
+ };
48
+ retry.current = request;
49
+ try {
50
+ const result = await joinWorkspace(request);
51
+ if (!result.ok)
52
+ throw new Error(result.error.message);
53
+ await onJoined();
54
+ }
55
+ catch (cause) {
56
+ setError(cause instanceof Error ? cause.message : String(cause));
57
+ }
58
+ finally {
59
+ busy.current = false;
60
+ setPending(undefined);
61
+ onPending(false);
62
+ }
63
+ };
64
+ return _jsxs("div", { className: css.form, children: [_jsx("p", { className: css.notice, children: t('importAgentNotice') }), loading && _jsx("p", { className: css.state, role: "status", children: t('loadingAgents') }), !loading && error === undefined && members.length === 0 && _jsx("p", { className: css.state, children: t('emptyImportAgents') }), !loading && members.length > 0 && _jsx("div", { className: css.roster, children: members.map(status => _jsx(TeamMemberRow, { status: status, t: t, action: {
65
+ label: pending === status.member.memberId ? t('importingAgent') : t('importAgent'),
66
+ disabled: pending !== undefined,
67
+ onSelect: () => { void join(status); },
68
+ } }, status.member.memberId)) }), error !== undefined && _jsxs("div", { className: css.failure, role: "alert", children: [_jsx("p", { className: css.error, children: error }), _jsx(Button, { disabled: pending !== undefined, variant: "outline", onClick: () => { void load(); }, children: t('retry') })] })] });
69
+ }
@@ -9,6 +9,8 @@ interface TeamAgentsPanelProps {
9
9
  readonly updateMember: TeamSidebarProps['updateMember'];
10
10
  readonly recoverMember: TeamSidebarProps['recoverMember'];
11
11
  readonly archiveMember: TeamSidebarProps['archiveMember'];
12
+ readonly joinWorkspace: TeamSidebarProps['joinWorkspace'];
13
+ readonly leaveWorkspace: TeamSidebarProps['leaveWorkspace'];
12
14
  readonly loadModels: TeamSidebarProps['loadModels'];
13
15
  /** The Member Session currently embedded in the conversation seat, if any. */
14
16
  readonly memberSessionId?: AgentTeamClientMemberStatus['member']['sessionId'];
@@ -16,6 +18,6 @@ interface TeamAgentsPanelProps {
16
18
  readonly onCreatingChange: (request: AgentTeamAddMemberRequest, creating: boolean) => void;
17
19
  readonly t: TeamSidebarProps['t'];
18
20
  }
19
- export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
21
+ export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, joinWorkspace, leaveWorkspace, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
20
22
  export {};
21
23
  //# sourceMappingURL=TeamAgentsPanel.d.ts.map
@@ -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;AAalD,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,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,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA0O/N"}
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"}
@@ -10,13 +10,15 @@ import { diagnosticText, restartOffered } from "./TeamPresenceDot.js";
10
10
  import { TeamRowMenu } from "./TeamRowMenu.js";
11
11
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
12
12
  import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
13
+ import { TeamAgentImport } from "./TeamAgentImport.js";
13
14
  import createCss from './create.module.css';
14
15
  import css from './sidebar.module.css';
15
- export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
16
+ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, joinWorkspace, leaveWorkspace, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
16
17
  const [members, setMembers] = useState([]);
17
18
  const [loading, setLoading] = useState(true);
18
19
  const [error, setError] = useState();
19
20
  const [formOpen, setFormOpen] = useState(false);
21
+ const [importing, setImporting] = useState(false);
20
22
  const [handle, setHandle] = useState('');
21
23
  const [description, setDescription] = useState('');
22
24
  const [model, setModel] = useState(undefined);
@@ -142,11 +144,10 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
142
144
  try {
143
145
  const result = await addMember(request);
144
146
  if (result.ok) {
147
+ const committed = { ...result.value.status, workspaceIds: [result.value.status.member.workspaceId] };
145
148
  setMembers(current => {
146
- const retained = current.filter(status => status.member.memberId !== result.value.status.member.memberId);
147
- return result.value.status.member.state === 'inactive' || result.value.status.member.state === 'archived'
148
- ? retained
149
- : [...retained, result.value.status];
149
+ const retained = current.filter(status => status.member.memberId !== committed.member.memberId);
150
+ return committed.member.state === 'inactive' || committed.member.state === 'archived' ? retained : [...retained, committed];
150
151
  });
151
152
  setHandle('');
152
153
  setDescription('');
@@ -195,18 +196,21 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
195
196
  ...(model === undefined ? {} : { model }),
196
197
  });
197
198
  };
198
- return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, 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); setRetryRequest(undefined); }, disabled: creating, 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); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'agents', open); }, actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && error === undefined && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), updateMember: updateMember, recoverMember: recoverMember, archiveMember: archiveMember, loadModels: loadModels, openMemberSession: openMemberSession, onUpdated: () => { void refresh(); }, t: t }) }, status.member.memberId))) })] }), !formOpen && error !== undefined && (_jsxs("div", { className: css.retryError, role: "alert", children: [_jsx("span", { children: error }), retryRequest !== undefined && _jsx("button", { type: "button", className: css.textButton, disabled: creating, onClick: () => { setFormOpen(true); }, children: t('retry') })] }))] }));
199
+ return (_jsxs("div", { className: css.panel, children: [_jsxs(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), !importing && _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: [_jsx(Button, { className: createCss.modeSwitch, variant: "outline", disabled: creating, "aria-expanded": importing, onClick: () => { setImporting(value => !value); setError(undefined); }, children: importing ? t('createAgent') : t('importAgentTitle') }), formOpen && importing ? _jsx(TeamAgentImport, { workspaceId: workspaceId, loadMembers: loadMembers, joinWorkspace: joinWorkspace, onPending: setCreating, onJoined: async () => { await refresh(); setFormOpen(false); queueMicrotask(() => { triggerRef.current?.focus(); }); }, t: t }) : _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, 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); setRetryRequest(undefined); }, disabled: creating, 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); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] })] }), _jsxs(TeamSidebarSection, { title: t('agents'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'agents', open); }, actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setImporting(false); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && error === undefined && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { workspaceId: workspaceId, leaveWorkspace: leaveWorkspace, status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), updateMember: updateMember, recoverMember: recoverMember, archiveMember: archiveMember, loadModels: loadModels, openMemberSession: openMemberSession, onUpdated: refresh, t: t }) }, status.member.memberId))) })] }), !formOpen && error !== undefined && (_jsxs("div", { className: css.retryError, role: "alert", children: [_jsx("span", { children: error }), retryRequest !== undefined && _jsx("button", { type: "button", className: css.textButton, disabled: creating, onClick: () => { setFormOpen(true); }, children: t('retry') })] }))] }));
199
200
  }
200
201
  /**
201
202
  * One sidebar Agent row: the select button opens the Member's own Session
202
203
  * conversation page, the avatar carries identity plus the presence badge, and
203
204
  * the row menu opens the editor.
204
205
  */
205
- function AgentRow({ status, current, updateMember, recoverMember, archiveMember, loadModels, openMemberSession, onUpdated, t }) {
206
+ function AgentRow({ workspaceId, leaveWorkspace, status, current, updateMember, recoverMember, archiveMember, loadModels, openMemberSession, onUpdated, t }) {
206
207
  const [menuOpen, setMenuOpen] = useState(false);
207
208
  const [editing, setEditing] = useState(false);
208
209
  const [archiving, setArchiving] = useState(false);
209
210
  const [rowAlert, setRowAlert] = useState();
211
+ const [archivePending, setArchivePending] = useState(false);
212
+ const archiveRequest = useRef();
213
+ const withdrawing = workspaceId !== status.member.workspaceId;
210
214
  // Both row actions ride the same runtime remote: the Host steers a live
211
215
  // session, rebuilds an orphaned composition, or re-runs a failed activation.
212
216
  const recover = async () => {
@@ -241,25 +245,32 @@ function AgentRow({ status, current, updateMember, recoverMember, archiveMember,
241
245
  }
242
246
  };
243
247
  const archive = async () => {
248
+ if (archivePending)
249
+ return;
250
+ setArchivePending(true);
251
+ setRowAlert(undefined);
252
+ archiveRequest.current ??= mintRequestId();
253
+ const request = { requestId: archiveRequest.current, memberId: status.member.memberId };
244
254
  try {
245
- const result = await archiveMember({
246
- requestId: mintRequestId(),
247
- memberId: status.member.memberId,
248
- });
255
+ const result = withdrawing ? await leaveWorkspace({ ...request, workspaceId }) : await archiveMember(request);
256
+ if (!result.ok)
257
+ throw new Error(result.error.message);
258
+ setArchiving(false);
259
+ archiveRequest.current = undefined;
249
260
  await onUpdated();
250
- if (!result.ok) {
251
- setRowAlert(t('archiveAgentFailed', { message: result.error.message }));
252
- }
253
261
  }
254
262
  catch (cause) {
255
- setRowAlert(t('archiveAgentFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
263
+ setRowAlert(cause instanceof Error ? cause.message : String(cause));
264
+ }
265
+ finally {
266
+ setArchivePending(false);
256
267
  }
257
268
  };
258
269
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.agentRow, "data-agent-row": true, "data-menu-open": menuOpen || undefined, children: [_jsx("button", { type: "button", className: css.agentSelect, "aria-label": t('openAgentSession', { name: status.member.handle }), "aria-current": current ? 'page' : undefined, disabled: status.availability !== 'active', onClick: () => { openMemberSession(status.member.sessionId); }, children: _jsx(TeamMemberIdentity, { status: status, name: status.member.handle.replace(/^@/, ''), className: css.agentCopy, t: t }) }), _jsx("span", { className: css.rowMenu, children: _jsx(TeamRowMenu, { label: t('actionsAgent', { name: status.member.handle }), items: [
259
270
  { id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
260
271
  ...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
261
272
  ...(status.availability === 'unavailable' && restartOffered(status) ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
262
- { id: 'archive', label: t('archiveAgent'), icon: _jsx(IconArchiveOutline20, { size: 16 }), danger: true },
273
+ { id: 'archive', label: t(withdrawing ? 'withdrawAgent' : 'archiveAgent'), icon: _jsx(IconArchiveOutline20, { size: 16 }), danger: true },
263
274
  ], onSelect: (id) => {
264
275
  if (id === 'edit')
265
276
  setEditing(true);
@@ -267,5 +278,6 @@ function AgentRow({ status, current, updateMember, recoverMember, archiveMember,
267
278
  setArchiving(true);
268
279
  else
269
280
  void recover();
270
- }, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), archiving && (_jsx(Modal, { open: true, onClose: () => { setArchiving(false); }, title: t('archiveAgentTitle', { name: status.member.handle }), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: () => { setArchiving(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", onClick: () => { setArchiving(false); void archive(); }, children: t('archiveAgentConfirm') })] }), children: _jsx("p", { className: createCss.error, children: t('archiveAgentNotice', { name: status.member.handle }) }) })), editing && (_jsx(AgentEditorDialog, { status: status, updateMember: updateMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
281
+ }, onOpenChange: setMenuOpen }) })] }), !archiving && rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), archiving && (_jsxs(Modal, { open: true, onClose: () => { if (!archivePending)
282
+ setArchiving(false); }, title: t(withdrawing ? 'withdrawAgentTitle' : 'archiveAgentTitle', { name: status.member.handle }), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: archivePending, onClick: () => { setArchiving(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: archivePending, onClick: () => { void archive(); }, children: t(withdrawing ? 'withdrawAgent' : 'archiveAgentConfirm') })] }), children: [_jsx("p", { className: createCss.error, children: t(withdrawing ? 'withdrawAgentNotice' : 'archiveAgentNotice', { name: status.member.handle }) }), !withdrawing && status.workspaceIds.length > 1 && _jsx("p", { children: t('archiveOtherWorkspaces', { count: status.workspaceIds.length - 1 }) }), rowAlert !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: rowAlert })] })), editing && (_jsx(AgentEditorDialog, { status: status, updateMember: updateMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
271
283
  }
@@ -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;AA0DD,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,+BAkX9R"}
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"}
@@ -8,7 +8,7 @@ import { TeamMessage } from "./TeamMessage.js";
8
8
  import { TeamAvatarStack } from "./TeamAvatarStack.js";
9
9
  import { TeamCountBadge } from "./TeamCountBadge.js";
10
10
  import { TeamRunDivider } from "./TeamRunDivider.js";
11
- import { formatAbsoluteTime, formatInboxTime, formatTaskStatus, taskStatusDot, mentionNamesOf } from "./team-formatters.js";
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";
@@ -35,30 +35,6 @@ function mergeChannelView(current, fresh) {
35
35
  hasMore: fresh.hasMore || current.cursor < fresh.cursor,
36
36
  };
37
37
  }
38
- /**
39
- * The people the Channel feed shows on one Task: owners of its live Claims, in
40
- * claim order, deduped. Claim state is a Host fact, not decoration — a released
41
- * Claim is no longer work — and only a Task still in progress or review has
42
- * any, because a done or closed Task keeps its Claims as history that the state
43
- * word already tells the reader.
44
- */
45
- function liveOwnersByTask(items, claims) {
46
- const live = new Set(items
47
- .flatMap(item => item.task === undefined ? [] : [item.task])
48
- .filter(task => task.status === 'in_progress' || task.status === 'in_review')
49
- .map(task => task.taskRef));
50
- const owners = new Map();
51
- for (const claim of claims) {
52
- if (!live.has(claim.taskRef) || claim.state === 'released')
53
- continue;
54
- const known = owners.get(claim.taskRef);
55
- if (known === undefined)
56
- owners.set(claim.taskRef, [claim.owner]);
57
- else if (!known.includes(claim.owner))
58
- known.push(claim.owner);
59
- }
60
- return owners;
61
- }
62
38
  /**
63
39
  * The newest fact instant, or nothing when the entry's own Message is still the
64
40
  * newest fact on its Thread. That difference is exactly what "has follow-up
@@ -76,7 +52,9 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
76
52
  const [view, setView] = useState();
77
53
  const [members, setMembers] = useState([]);
78
54
  const [unreadByThread, setUnreadByThread] = useState(new Map());
79
- const [error, setError] = useState();
55
+ const [actionError, setError] = useState();
56
+ const [loadError, setLoadError] = useState();
57
+ const error = actionError ?? loadError;
80
58
  const [pendingFiles, setPendingFiles] = useState([]);
81
59
  const [statusMessage, setStatusMessage] = useState();
82
60
  const [loading, setLoading] = useState(true);
@@ -146,10 +124,6 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
146
124
  const onlineCount = channelMembers.filter(status => status.presence === 'available' || status.presence === 'working').length;
147
125
  const messageSender = (item) => item.message.sender;
148
126
  const handleByMember = new Map(members.map(status => [status.member.memberId, status.member.handle.replace(/^@/, '')]));
149
- // Claim owners ride the same Host projection as everything else; the map is
150
- // rebuilt per render because the window is small and identity must never lag
151
- // a change wake.
152
- const ownersByTask = liveOwnersByTask(view?.items ?? [], view?.claims ?? []);
153
127
  const refresh = async (clearError = false) => {
154
128
  if (!mountedRef.current)
155
129
  return false;
@@ -160,7 +134,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
160
134
  if (!loadedRef.current)
161
135
  setLoading(true);
162
136
  if (clearError) {
163
- setError(undefined);
137
+ setLoadError(undefined);
164
138
  setStatusMessage(undefined);
165
139
  }
166
140
  try {
@@ -180,24 +154,26 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
180
154
  loadedRef.current = true;
181
155
  }
182
156
  else
183
- setError(loaded.error.message);
157
+ setLoadError(loaded.error.message);
184
158
  if (loadedMembers.ok)
185
159
  setMembers(loadedMembers.value);
186
160
  else
187
- setError(loadedMembers.error.message);
161
+ setLoadError(loadedMembers.error.message);
188
162
  // A failed unread read drops the badges instead of leaving counts the
189
163
  // reader can no longer trust; the failure surfaces like any other read.
190
164
  if (loadedInbox.ok)
191
165
  setUnreadByThread(unreadCounts(loadedInbox.value));
192
166
  else {
193
167
  setUnreadByThread(new Map());
194
- setError(loadedInbox.error.message);
168
+ setLoadError(loadedInbox.error.message);
195
169
  }
170
+ if (loaded.ok && loadedMembers.ok && loadedInbox.ok)
171
+ setLoadError(undefined);
196
172
  return loaded.ok && loadedMembers.ok && loadedInbox.ok;
197
173
  }
198
174
  catch (cause) {
199
175
  if (mountedRef.current && sequence === refreshSequenceRef.current)
200
- setError(cause instanceof Error ? cause.message : String(cause));
176
+ setLoadError(cause instanceof Error ? cause.message : String(cause));
201
177
  return false;
202
178
  }
203
179
  finally {
@@ -217,18 +193,18 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
217
193
  if (loaded.ok)
218
194
  setMembers(loaded.value);
219
195
  else
220
- setError(loaded.error.message);
196
+ setLoadError(loaded.error.message);
221
197
  }
222
198
  catch (cause) {
223
199
  if (mountedRef.current)
224
- setError(cause instanceof Error ? cause.message : String(cause));
200
+ setLoadError(cause instanceof Error ? cause.message : String(cause));
225
201
  }
226
202
  };
227
203
  useEffect(() => {
228
204
  mountedRef.current = true;
229
205
  loadedRef.current = false;
230
206
  setView(undefined);
231
- setError(undefined);
207
+ setLoadError(undefined);
232
208
  setLoading(true);
233
209
  setManagingMembers(false);
234
210
  void refresh();
@@ -237,7 +213,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
237
213
  if (!mountedRef.current)
238
214
  return;
239
215
  if (update.type === 'failed') {
240
- setError(update.message);
216
+ setLoadError(update.message);
241
217
  return;
242
218
  }
243
219
  void refresh();
@@ -246,7 +222,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
246
222
  if (!mountedRef.current)
247
223
  return;
248
224
  if (update.type === 'failed') {
249
- setError(update.message);
225
+ setLoadError(update.message);
250
226
  return;
251
227
  }
252
228
  void refreshMembers();
@@ -257,7 +233,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
257
233
  if (!mountedRef.current)
258
234
  return;
259
235
  if (update.type === 'failed') {
260
- setError(update.message);
236
+ setLoadError(update.message);
261
237
  return;
262
238
  }
263
239
  void refreshMembers();
@@ -395,10 +371,9 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
395
371
  // leads that line: ownership, status, and unread answer the reader
396
372
  // where they are already reading. A reply inside a Thread has none
397
373
  // of it — the Thread page owns that surface.
398
- const owners = task === undefined ? [] : ownersByTask.get(task.taskRef) ?? [];
399
374
  const unread = unreadByThread.get(item.thread.threadRef) ?? 0;
400
375
  // A turn divider already labels the gapped entry; its row stays chrome-free.
401
- 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: owners.map(memberId => ({ memberId, name: handleByMember.get(memberId) ?? memberId })), unread: unread, t: t, onOpen: () => { selectThread(item.thread.threadRef, channelRef, task?.taskRef, item.taskNumber); } }) })] }, item.message.messageRef);
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);
402
377
  }) }, `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", {})] });
403
378
  }
404
379
  /**
@@ -413,7 +388,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
413
388
  function ThreadStateCluster({ task, owners, unread, t }) {
414
389
  if (task === undefined && unread === 0)
415
390
  return null;
416
- return _jsxs("span", { className: css.stateCluster, children: [task !== undefined && _jsx(TeamAvatarStack, { owners: owners, label: t('claimers', { names: owners.map(owner => `@${owner.name}`).join(', ') }) }), 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 })] });
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 })] });
417
392
  }
418
393
  /**
419
394
  * The gate under one top-level Message: the single row that opens its Thread,
@@ -12,8 +12,8 @@ interface TeamInboxPageProps {
12
12
  * The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
13
13
  * two slices — the unread queue (「需要我」, mentions counted inside it rather
14
14
  * than alone) and the 「最近活跃」 tail of Threads the reader took part in.
15
- * Both merge across every Workspace into one recency-ordered list, newest
16
- * first, since a queue is read by recency rather than by Workspace.
15
+ * Both merge across every Workspace into one list in the Host's own order,
16
+ * mentions first and then newest, rather than by Workspace.
17
17
  * Opening the page never acknowledges anything — only a durable Thread read
18
18
  * advances the watermark and consumes a mention marker, so rows and the badge
19
19
  * drop after the Thread is opened through the existing auto-ack path. A Thread
@@ -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;AAyBD;;;;;;;;;;;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,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,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
4
- import { formatAbsoluteTime, formatInboxTime } from "./team-formatters.js";
4
+ import { claimersLabel, formatAbsoluteTime, formatInboxTime } from "./team-formatters.js";
5
5
  import { TeamAvatarStack } from "./TeamAvatarStack.js";
6
6
  import { TeamCountBadge } from "./TeamCountBadge.js";
7
7
  import css from './conversation.module.css';
@@ -15,24 +15,25 @@ import inboxCss from './inbox.module.css';
15
15
  */
16
16
  const RECENT_ROWS_LIMIT = 5;
17
17
  /**
18
- * Queue order: newest unread first, one total order across Workspaces. Each
19
- * Workspace slice arrives in the Host's own order (that order is the Host's
20
- * truncation policy); the merged display order is decided here, where the
21
- * merge happens, and the ledger sequence breaks ties so the queue never
22
- * reshuffles two rows that share an instant.
18
+ * Queue order, one total order across Workspaces: the Host's own keys —
19
+ * mentions first, then the ledger sequence, then the Thread ref. Each Workspace
20
+ * slice already arrives in this order, and it is also the Host's truncation
21
+ * policy, so the merge re-applies it instead of inventing a second one: a row
22
+ * that survived the cut on mentions must not sink below a merely newer row
23
+ * after the merge. `newestSequence` is a global ledger position, so it stays
24
+ * comparable across Workspaces.
23
25
  */
24
26
  function compareInboxRows(left, right) {
25
- const leftAt = Date.parse(left.item.newestOccurredAt);
26
- const rightAt = Date.parse(right.item.newestOccurredAt);
27
- const byTime = (Number.isNaN(rightAt) ? 0 : rightAt) - (Number.isNaN(leftAt) ? 0 : leftAt);
28
- return byTime !== 0 ? byTime : right.item.newestSequence - left.item.newestSequence;
27
+ return right.item.directCount - left.item.directCount
28
+ || right.item.newestSequence - left.item.newestSequence
29
+ || left.item.thread.threadRef.localeCompare(right.item.thread.threadRef);
29
30
  }
30
31
  /**
31
32
  * The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
32
33
  * two slices — the unread queue (「需要我」, mentions counted inside it rather
33
34
  * than alone) and the 「最近活跃」 tail of Threads the reader took part in.
34
- * Both merge across every Workspace into one recency-ordered list, newest
35
- * first, since a queue is read by recency rather than by Workspace.
35
+ * Both merge across every Workspace into one list in the Host's own order,
36
+ * mentions first and then newest, rather than by Workspace.
36
37
  * Opening the page never acknowledges anything — only a durable Thread read
37
38
  * advances the watermark and consumes a mention marker, so rows and the badge
38
39
  * drop after the Thread is opened through the existing auto-ack path. A Thread
@@ -61,7 +62,7 @@ export function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, sele
61
62
  const asRows = (items, workspaceId, workspaceTitle) => items.map(item => ({ workspaceId, workspaceTitle, item }));
62
63
  setRows(results.flatMap(result => result.ok ? asRows(result.items, result.workspaceId, result.workspaceTitle) : []).sort(compareInboxRows));
63
64
  // The tail is one global bound rather than one per Workspace: the reader was
64
- // promised ten Threads to step back into, and every Workspace's slice is
65
+ // promised five Threads to step back into, and every Workspace's slice is
65
66
  // already capped on its own, so the merged list is trimmed here.
66
67
  setRecentRows(results.flatMap(result => result.ok ? asRows(result.recent, result.workspaceId, result.workspaceTitle) : [])
67
68
  .sort(compareInboxRows).slice(0, RECENT_ROWS_LIMIT));
@@ -134,6 +135,6 @@ function InboxQueueRow({ row, t, showWorkspace, onOpen }) {
134
135
  ? t('inboxRowUnreadMentions', { count: item.unreadCount, mentions: item.directCount })
135
136
  : t('inboxRowUnread', { count: item.unreadCount });
136
137
  return _jsxs("button", { type: "button", className: inboxCss.row, "data-named": named || undefined, onClick: onOpen, children: [_jsx("span", { className: inboxCss.rowActor, children: owners.length > 0
137
- ? _jsx(TeamAvatarStack, { owners: owners, label: t('claimers', { names: owners.map(owner => `@${owner.name}`).join(', ') }) })
138
+ ? _jsx(TeamAvatarStack, { owners: owners, label: claimersLabel(owners, t) })
138
139
  : _jsx(TeamAvatarStack, { owners: [actor], label: t('inboxRowActor', { name: `@${actor.name}` }) }) }), _jsxs("span", { className: inboxCss.rowLine, children: [_jsxs("span", { className: inboxCss.rowCrumb, children: [showWorkspace && _jsx("span", { className: inboxCss.rowWorkspace, children: row.workspaceTitle }), showWorkspace && ' / ', _jsxs("span", { className: inboxCss.rowChannel, children: ["#", item.channelName] }), ' ', item.taskNumber !== undefined && _jsx("span", { className: inboxCss.rowTask, children: t('taskLabel', { number: item.taskNumber }) })] }), _jsx(TeamCountBadge, { count: item.unreadCount, tone: named ? 'solid' : 'hairline', label: countLabel }), _jsx("time", { className: inboxCss.rowTime, dateTime: item.newestOccurredAt, title: formatAbsoluteTime(item.newestOccurredAt), children: formatInboxTime(item.newestOccurredAt, t) })] }), _jsx("span", { className: inboxCss.rowPreview, children: item.previewText })] });
139
140
  }
@@ -3,7 +3,7 @@ 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
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;
6
+ export declare function diagnosticText(status: Pick<AgentTeamClientMemberStatus, 'diagnostic'>): string;
7
7
  /**
8
8
  * Whether the restart action can help an unavailable Member: it heals
9
9
  * transient and repairable failures, but not a transient rollover window
@@ -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,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"}
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,IAAI,CAAC,2BAA2B,EAAE,YAAY,CAAC,GAAG,MAAM,CAI9F;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"}
@@ -1 +1 @@
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,+BAuvBxD"}
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;AAc/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,+BA2uBxD"}