@wowyuarm/dsh-agent-team 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/README.zh.md +17 -4
- package/package.json +53 -47
- package/packages/agent-team/README.md +6 -4
- package/packages/agent-team/README.zh.md +6 -4
- package/packages/agent-team/lib/context-management.js +40 -50
- package/packages/agent-team/lib/context-projection.js +15 -1
- package/packages/agent-team/lib/index.js +244 -154
- package/packages/agent-team/lib/invariant.js +38 -4
- package/packages/agent-team/lib/ledger.js +775 -383
- package/packages/agent-team/lib/member-context.js +6 -2
- package/packages/agent-team/lib/member-runtime.js +47 -9
- package/packages/agent-team/lib/member-time-context.js +20 -5
- package/packages/agent-team/lib/mentions.js +56 -0
- package/packages/agent-team/lib/pressure-policy.js +33 -9
- package/packages/agent-team/lib/session-event-cursor.js +116 -0
- package/packages/agent-team/lib/session-remediation.js +38 -26
- package/packages/agent-team/lib/spec.js +47 -34
- package/packages/agent-team/lib/stored-session-reader.js +138 -0
- package/packages/agent-team/lib/typert.host.js +157 -41
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +129 -32
- package/packages/agent-team/lib/types/context-management.d.ts +7 -0
- package/packages/agent-team/lib/types/context-management.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-projection.d.ts +8 -0
- package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
- package/packages/agent-team/lib/types/index.d.ts +77 -7
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/invariant.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +251 -20
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts +2 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/mentions.d.ts +34 -0
- package/packages/agent-team/lib/types/mentions.d.ts.map +1 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts +18 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
- package/packages/agent-team/lib/types/session-event-cursor.d.ts +119 -0
- package/packages/agent-team/lib/types/session-event-cursor.d.ts.map +1 -0
- package/packages/agent-team/lib/types/session-remediation.d.ts +23 -0
- package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -1
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/stored-session-reader.d.ts +92 -0
- package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -0
- package/packages/agent-team/lib/types/types/entities.d.ts +28 -20
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +45 -19
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +87 -4
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +49 -7
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +2357 -1207
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +27 -7
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +18 -0
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +22 -0
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +167 -35
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +23 -27
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +11 -5
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +26 -26
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +9 -2
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts +20 -0
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.js +25 -0
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +25 -0
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +139 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +8 -24
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts +43 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.js +27 -0
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +9 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +11 -4
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +8 -0
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +21 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +108 -30
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +68 -7
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +19 -9
- package/packages/client-agent-team/lib/types/client/locales.d.ts +40 -2
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +40 -2
- package/packages/client-agent-team/lib/types/client/navigation.d.ts +10 -0
- package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/navigation.js +18 -7
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +41 -0
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/scope-coverage.js +64 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +7 -2
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts +33 -2
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.js +107 -25
- package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts +20 -0
- package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/team-dialog-save.js +36 -0
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +43 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +81 -6
- package/packages/tool-agent-team/README.md +2 -2
- package/packages/tool-agent-team/README.zh.md +2 -2
- package/packages/tool-agent-team/lib/context-tools.js +18 -14
- package/packages/tool-agent-team/lib/host-access.js +20 -0
- package/packages/tool-agent-team/lib/index.js +44 -32
- package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
- package/packages/tool-agent-team/lib/types/host-access.d.ts +14 -0
- package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -0
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/progress-nudge.js +0 -328
- package/packages/agent-team/lib/types/progress-nudge.d.ts +0 -136
- package/packages/agent-team/lib/types/progress-nudge.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +0 -3
- package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamSettings.js +0 -5
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect,
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Button, IconChevronDownOutline14, Input, Menu, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { mintRequestId } from "./requests.js";
|
|
5
|
+
import { useEditDialogSave } from "./team-dialog-save.js";
|
|
5
6
|
import createCss from './create.module.css';
|
|
6
7
|
import css from './sidebar.module.css';
|
|
7
8
|
/** Model option key inside one editor; opaque and resolved against the loaded groups. */
|
|
@@ -77,9 +78,11 @@ export function AgentEditorDialog({ status, updateMember, loadModels, onCommitte
|
|
|
77
78
|
const [handle, setHandle] = useState(status.member.handle);
|
|
78
79
|
const [description, setDescription] = useState(status.member.description);
|
|
79
80
|
const [model, setModel] = useState(status.member.model);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
const { saving, error, pendingRequest, save } = useEditDialogSave({
|
|
82
|
+
save: updateMember,
|
|
83
|
+
onCommitted,
|
|
84
|
+
onClose,
|
|
85
|
+
});
|
|
83
86
|
const dirty = handle.trim() !== status.member.handle || description.trim() !== status.member.description
|
|
84
87
|
|| !sameModel(model, status.member.model);
|
|
85
88
|
const submit = async (event) => {
|
|
@@ -104,26 +107,7 @@ export function AgentEditorDialog({ status, updateMember, loadModels, onCommitte
|
|
|
104
107
|
requestId: mintRequestId(),
|
|
105
108
|
...payload,
|
|
106
109
|
};
|
|
107
|
-
|
|
108
|
-
setSaving(true);
|
|
109
|
-
setError(undefined);
|
|
110
|
-
try {
|
|
111
|
-
const result = await updateMember(request);
|
|
112
|
-
if (result.ok) {
|
|
113
|
-
pendingRequest.current = undefined;
|
|
114
|
-
await onCommitted();
|
|
115
|
-
onClose();
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
setError(result.error.message);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (cause) {
|
|
122
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
setSaving(false);
|
|
126
|
-
}
|
|
110
|
+
await save(request);
|
|
127
111
|
};
|
|
128
112
|
return (_jsx(Modal, { open: true, onClose: onClose, title: t('editAgent'), description: `@${status.member.handle}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-edit-form", variant: "primary", disabled: saving || !dirty || handle.trim().length === 0, children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-agent-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { pendingRequest.current = undefined; setModel(choice); }, loadModels: loadModels, disabled: saving, t: t }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
|
|
129
113
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
|
|
2
|
+
import type { TeamSidebarProps } from './slots.ts';
|
|
3
|
+
/** One membership action: what it says and which Member it acts on. */
|
|
4
|
+
export interface TeamMemberAction {
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly onSelect: () => void;
|
|
7
|
+
readonly disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The one place a Member's identity is drawn: the presence-bearing avatar plus
|
|
11
|
+
* the handle over its description. Read-only rosters, the rosters with a
|
|
12
|
+
* membership action, and the sidebar Agent list all render this, so identity,
|
|
13
|
+
* tone and truncation cannot drift between surfaces that show the same person.
|
|
14
|
+
*
|
|
15
|
+
* A fragment, not a row: each surface owns its own row element, hit target and
|
|
16
|
+
* grid, and places this identity in that grid's first two tracks. The handle
|
|
17
|
+
* spelling is the surface's call — rosters address Members the way the
|
|
18
|
+
* composer does (`@handle`), the sidebar names them as the directory does.
|
|
19
|
+
*/
|
|
20
|
+
export declare function TeamMemberIdentity({ status, name, className, t }: {
|
|
21
|
+
readonly status: AgentTeamClientMemberStatus;
|
|
22
|
+
/** Handle spelling for this surface; defaults to the mention form. */
|
|
23
|
+
readonly name?: string | undefined;
|
|
24
|
+
/** Surface-local copy hook (sidebar line heights, dialog density). */
|
|
25
|
+
readonly className?: string | undefined;
|
|
26
|
+
readonly t: TeamSidebarProps['t'];
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* The one roster row: identity, an optional membership action, and the row's
|
|
30
|
+
* own failure line. The action is the row's only chrome, so the eye lands on
|
|
31
|
+
* the handle first and on the action second. A read-only roster omits it and
|
|
32
|
+
* the trailing track collapses, handing its width back to the description.
|
|
33
|
+
*/
|
|
34
|
+
export declare function TeamMemberRow({ status, action, error, className, t }: {
|
|
35
|
+
readonly status: AgentTeamClientMemberStatus;
|
|
36
|
+
readonly action?: TeamMemberAction | undefined;
|
|
37
|
+
/** Transport failure for this row's last mutation, announced in place. */
|
|
38
|
+
readonly error?: string | undefined;
|
|
39
|
+
/** Surface-local hook (dialog, sidebar, footer) for its own row rules. */
|
|
40
|
+
readonly className?: string | undefined;
|
|
41
|
+
readonly t: TeamSidebarProps['t'];
|
|
42
|
+
}): import("react").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=TeamMemberRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamMemberRow.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberRow.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IACjE,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAQA;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IACrE,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAMA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { TeamMemberAvatar } from "./TeamMemberAvatar.js";
|
|
4
|
+
import css from './member-row.module.css';
|
|
5
|
+
/**
|
|
6
|
+
* The one place a Member's identity is drawn: the presence-bearing avatar plus
|
|
7
|
+
* the handle over its description. Read-only rosters, the rosters with a
|
|
8
|
+
* membership action, and the sidebar Agent list all render this, so identity,
|
|
9
|
+
* tone and truncation cannot drift between surfaces that show the same person.
|
|
10
|
+
*
|
|
11
|
+
* A fragment, not a row: each surface owns its own row element, hit target and
|
|
12
|
+
* grid, and places this identity in that grid's first two tracks. The handle
|
|
13
|
+
* spelling is the surface's call — rosters address Members the way the
|
|
14
|
+
* composer does (`@handle`), the sidebar names them as the directory does.
|
|
15
|
+
*/
|
|
16
|
+
export function TeamMemberIdentity({ status, name, className, t }) {
|
|
17
|
+
return _jsxs(_Fragment, { children: [_jsx(TeamMemberAvatar, { status: status, t: t }), _jsxs("span", { className: className === undefined ? css.copy : `${css.copy} ${className}`, children: [_jsx("strong", { children: name ?? `@${status.member.handle.replace(/^@/, '')}` }), _jsx("small", { children: status.member.description })] })] });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The one roster row: identity, an optional membership action, and the row's
|
|
21
|
+
* own failure line. The action is the row's only chrome, so the eye lands on
|
|
22
|
+
* the handle first and on the action second. A read-only roster omits it and
|
|
23
|
+
* the trailing track collapses, handing its width back to the description.
|
|
24
|
+
*/
|
|
25
|
+
export function TeamMemberRow({ status, action, error, className, t }) {
|
|
26
|
+
return _jsxs("div", { className: className === undefined ? css.row : `${css.row} ${className}`, "data-team-member-row": true, children: [_jsx(TeamMemberIdentity, { status: status, t: t }), action !== undefined && _jsx(Button, { size: "sm", variant: "outline", className: css.action, disabled: action.disabled === true, onClick: action.onSelect, children: action.label }), error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] });
|
|
27
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type TeamMembersActionProps = Pick<
|
|
1
|
+
import type { TeamSettingsProps } from './slots.ts';
|
|
2
|
+
type TeamMembersActionProps = Pick<TeamSettingsProps, 'wide' | 'loadMemberGroups' | 't'>;
|
|
3
3
|
export declare function TeamMembersAction({ wide, loadMemberGroups, t }: TeamMembersActionProps): import("react").JSX.Element;
|
|
4
4
|
export {};
|
|
5
5
|
//# sourceMappingURL=TeamMembersAction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMembersAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMembersAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"TeamMembersAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMembersAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAKnD,KAAK,sBAAsB,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,GAAG,GAAG,CAAC,CAAA;AAExF,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,sBAAsB,+BAkDtF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { IconUserOutline16, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
-
import {
|
|
4
|
+
import { TeamMemberRow } from "./TeamMemberRow.js";
|
|
5
5
|
import membersCss from './members.module.css';
|
|
6
6
|
import css from './team.module.css';
|
|
7
7
|
export function TeamMembersAction({ wide, loadMemberGroups, t }) {
|
|
@@ -28,5 +28,5 @@ export function TeamMembersAction({ wide, loadMemberGroups, t }) {
|
|
|
28
28
|
setPanelOpen(false);
|
|
29
29
|
queueMicrotask(() => { triggerRef.current?.focus(); });
|
|
30
30
|
};
|
|
31
|
-
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('members'), delayMs: 500, disabled: wide, children: _jsxs("button", { ref: triggerRef, type: "button", className: wide ? css.settingsAction : `${css.settingsAction} ${css.rail}`, "aria-label": t('members'), "aria-haspopup": "dialog", onClick: openMembers, children: [_jsx(IconUserOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: t('members') })] }) }), _jsx(Modal, { open: panelOpen, onClose: closeMembers, title: t('members'), closeLabel: t('close'), contentClassName: membersCss.body, children: _jsxs("div", { ref: contentRef, className: membersCss.content, tabIndex: -1, children: [loading && _jsx("p", { className: membersCss.state, role: "status", children: t('loadingAgents') }), !loading && groups.length === 0 && error === undefined && _jsx("p", { className: membersCss.state, children: t('emptyAgents') }), !loading && groups.map(group => (_jsxs("section", { className: membersCss.group, "aria-labelledby": `team-members-${group.workspaceId}`, children: [_jsx("h3", { id: `team-members-${group.workspaceId}`, children: group.workspaceTitle }), group.members.map(status =>
|
|
31
|
+
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('members'), delayMs: 500, disabled: wide, children: _jsxs("button", { ref: triggerRef, type: "button", className: wide ? css.settingsAction : `${css.settingsAction} ${css.rail}`, "aria-label": t('members'), "aria-haspopup": "dialog", onClick: openMembers, children: [_jsx(IconUserOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: t('members') })] }) }), _jsx(Modal, { open: panelOpen, onClose: closeMembers, title: t('members'), closeLabel: t('close'), contentClassName: membersCss.body, children: _jsxs("div", { ref: contentRef, className: membersCss.content, tabIndex: -1, children: [loading && _jsx("p", { className: membersCss.state, role: "status", children: t('loadingAgents') }), !loading && groups.length === 0 && error === undefined && _jsx("p", { className: membersCss.state, children: t('emptyAgents') }), !loading && groups.map(group => (_jsxs("section", { className: membersCss.group, "aria-labelledby": `team-members-${group.workspaceId}`, children: [_jsx("h3", { id: `team-members-${group.workspaceId}`, children: group.workspaceTitle }), group.members.map(status => _jsx(TeamMemberRow, { status: status, className: membersCss.member, t: t }, status.member.memberId))] }, group.workspaceId))), error !== undefined && _jsx("p", { className: membersCss.error, role: "alert", children: error })] }) })] }));
|
|
32
32
|
}
|
|
@@ -27,6 +27,13 @@ export interface TeamMessageProps {
|
|
|
27
27
|
readonly onResolveTaskRefs?: ((taskRefs: readonly AgentTeamTaskRef[]) => Promise<readonly ResolvedTaskRef[]>) | undefined;
|
|
28
28
|
readonly children?: ReactNode;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* One chat message row with identity chrome and sender-appropriate rendering.
|
|
32
|
+
*
|
|
33
|
+
* Memoized because a timeline row is rendered by the page that owns the whole
|
|
34
|
+
* Thread: the Task-ref subscription below lives inside this component, so
|
|
35
|
+
* skipping a render here never detaches it. Callers must therefore keep the
|
|
36
|
+
* props they derive per render (mention names, ref callbacks) identity-stable.
|
|
37
|
+
*/
|
|
38
|
+
export declare const TeamMessage: import("react").NamedExoticComponent<TeamMessageProps>;
|
|
32
39
|
//# sourceMappingURL=TeamMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0G,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAG9I,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,wDAqHtB,CAAA"}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { MarkdownText, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { cachedAttachmentDataUrl, formatByteSize, loadAttachmentDataUrl } from "./attachment-preview.js";
|
|
5
5
|
import { cachedResolvedTaskRef, resolveUnknownTaskRefs, useResolvedTaskRefVersion } from "./task-refs.js";
|
|
6
6
|
import { formatMessageTime, isSingleBrandedRef, memberHue, planMessageBody, shouldClampMessage, splitBrandedRefs, splitMentionNames } from "./team-formatters.js";
|
|
7
7
|
import css from './conversation.module.css';
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* One chat message row with identity chrome and sender-appropriate rendering.
|
|
10
|
+
*
|
|
11
|
+
* Memoized because a timeline row is rendered by the page that owns the whole
|
|
12
|
+
* Thread: the Task-ref subscription below lives inside this component, so
|
|
13
|
+
* skipping a render here never detaches it. Callers must therefore keep the
|
|
14
|
+
* props they derive per render (mention names, ref callbacks) identity-stable.
|
|
15
|
+
*/
|
|
16
|
+
export const TeamMessage = memo(function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }) {
|
|
10
17
|
const avatarStyle = human ? undefined : { '--team-avatar-hue': memberHue(memberId) };
|
|
11
18
|
// Literal bodies carry mention chips inline — Human input always, and
|
|
12
19
|
// plain-prose Agent bodies where literal rendering loses nothing. Rich
|
|
@@ -97,7 +104,7 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
|
|
|
97
104
|
const label = resolved !== undefined && ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : ref;
|
|
98
105
|
return _jsx("button", { type: "button", className: css.refLink, title: ref, onClick: () => { onOpenRef(ref); }, children: label }, ref);
|
|
99
106
|
})] })), attachments !== undefined && attachments.length > 0 && _jsx(TeamAttachmentStrip, { attachments: attachments, ...(loadAttachment === undefined ? {} : { loadAttachment }), ...(t === undefined ? {} : { t }) }), children] })] }));
|
|
100
|
-
}
|
|
107
|
+
});
|
|
101
108
|
/** Text nodes that Markdown rendered as prose rather than code or a link. */
|
|
102
109
|
function markdownProseTextNodes(root) {
|
|
103
110
|
const nodes = [];
|
|
@@ -2,6 +2,14 @@ import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types
|
|
|
2
2
|
import type { TeamSidebarProps } from './slots.ts';
|
|
3
3
|
import type { TeamStateDotState } from './TeamStateDot.tsx';
|
|
4
4
|
export declare function presenceLabel(status: AgentTeamClientMemberStatus, t: TeamSidebarProps['t']): string;
|
|
5
|
+
/** One line of human-readable diagnostic text: the reason, plus the refused artifact path when one was reported. */
|
|
6
|
+
export declare function diagnosticText(status: AgentTeamClientMemberStatus): string;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the restart action can help an unavailable Member: it heals
|
|
9
|
+
* transient and repairable failures, but not a transient rollover window
|
|
10
|
+
* (which resolves on its own) or a refusal already proven non-remediable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function restartOffered(status: AgentTeamClientMemberStatus): boolean;
|
|
5
13
|
/** Shared presence → indicator mapping for dots and avatar badges. */
|
|
6
14
|
export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']): TeamStateDotState;
|
|
7
15
|
export declare function TeamPresenceDot({ status, t }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
|
|
1
|
+
{"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,MAAM,CAI1E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,OAAO,CAK3E;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
|
|
@@ -6,7 +6,27 @@ export function presenceLabel(status, t) {
|
|
|
6
6
|
const label = status.presence === 'available' ? t('statusAvailable')
|
|
7
7
|
: status.presence === 'working' ? t('statusWorking')
|
|
8
8
|
: status.presence === 'error' ? t('statusError') : t('statusUnavailable');
|
|
9
|
-
return status.diagnostic === undefined ? label : `${label}: ${status
|
|
9
|
+
return status.diagnostic === undefined ? label : `${label}: ${diagnosticText(status)}`;
|
|
10
|
+
}
|
|
11
|
+
/** One line of human-readable diagnostic text: the reason, plus the refused artifact path when one was reported. */
|
|
12
|
+
export function diagnosticText(status) {
|
|
13
|
+
const diagnostic = status.diagnostic;
|
|
14
|
+
if (diagnostic === undefined)
|
|
15
|
+
return '';
|
|
16
|
+
return diagnostic.location === undefined ? diagnostic.detail : `${diagnostic.detail} (${diagnostic.location.path})`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether the restart action can help an unavailable Member: it heals
|
|
20
|
+
* transient and repairable failures, but not a transient rollover window
|
|
21
|
+
* (which resolves on its own) or a refusal already proven non-remediable.
|
|
22
|
+
*/
|
|
23
|
+
export function restartOffered(status) {
|
|
24
|
+
const diagnostic = status.diagnostic;
|
|
25
|
+
if (diagnostic === undefined)
|
|
26
|
+
return true;
|
|
27
|
+
if (diagnostic.class === 'rollover')
|
|
28
|
+
return false;
|
|
29
|
+
return !(diagnostic.class === 'session-refused' && diagnostic.remediable === false);
|
|
10
30
|
}
|
|
11
31
|
/** Shared presence → indicator mapping for dots and avatar badges. */
|
|
12
32
|
export function presenceDotState(presence) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
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,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useEffect, useMemo, useRef, useState
|
|
2
|
+
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Button, DisclosureRow, IconChevronLeftOutline14, IconChecklistOutline14, Modal, Pill } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamComposer } from "./TeamComposer.js";
|
|
5
|
-
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
5
|
+
import { diagnosticText, TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
6
6
|
import { TeamMessage } from "./TeamMessage.js";
|
|
7
7
|
import { TeamRunDivider } from "./TeamRunDivider.js";
|
|
8
|
-
import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf, taskStatusDot } from "./team-formatters.js";
|
|
8
|
+
import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf, mentionedMemberIds, taskStatusDot } from "./team-formatters.js";
|
|
9
9
|
import { TeamStateDot } from "./TeamStateDot.js";
|
|
10
|
-
import { mintRequestId } from "./requests.js";
|
|
10
|
+
import { mintRequestId, uploadComposerFiles } from "./requests.js";
|
|
11
|
+
import { ScopeCoverage } from "./scope-coverage.js";
|
|
11
12
|
import { daySeparatorLabel, isRunGap, timelineDayKey } from "./team-separators.js";
|
|
12
13
|
import { useTimelineScroll } from "./timeline-scroll.js";
|
|
13
14
|
import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
|
|
14
|
-
import { bytesToBase64 } from "./attachment-preview.js";
|
|
15
15
|
import css from './conversation.module.css';
|
|
16
16
|
import threadCss from './thread.module.css';
|
|
17
17
|
function factKey(fact) {
|
|
@@ -20,6 +20,22 @@ function factKey(fact) {
|
|
|
20
20
|
function messageFact(message, mentions = []) {
|
|
21
21
|
return { kind: 'message', sequence: message.sequence, message, mentions, occurredAt: message.occurredAt };
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* A fact owns its mention array, so the rendered name list is cached against
|
|
25
|
+
* that array. Every roster refresh replaces the handles map with a fresh Map of
|
|
26
|
+
* identical content, so the cache compares the resolved names rather than the
|
|
27
|
+
* map identity: identity stays stable while the names are unchanged, which is
|
|
28
|
+
* what keeps a memoized row from re-rendering on every refresh.
|
|
29
|
+
*/
|
|
30
|
+
const mentionNamesCache = new WeakMap();
|
|
31
|
+
function stableMentionNames(mentions, handles) {
|
|
32
|
+
const names = mentionNamesOf(mentions, handles);
|
|
33
|
+
const cached = mentionNamesCache.get(mentions);
|
|
34
|
+
if (cached !== undefined && cached.length === names.length && cached.every((name, index) => name === names[index]))
|
|
35
|
+
return cached;
|
|
36
|
+
mentionNamesCache.set(mentions, names);
|
|
37
|
+
return names;
|
|
38
|
+
}
|
|
23
39
|
function mergeFacts(...groups) {
|
|
24
40
|
const byKey = new Map();
|
|
25
41
|
for (const group of groups)
|
|
@@ -57,8 +73,9 @@ export function TeamThreadPage(props) {
|
|
|
57
73
|
const [newFactsCount, setNewFactsCount] = useState(0);
|
|
58
74
|
// The reply draft lives in the keyed draft cache: view switches unmount
|
|
59
75
|
// this page, and a refresh must not cost the half-written message either.
|
|
76
|
+
// The composer owns the subscription — this page only reads a snapshot when
|
|
77
|
+
// it sends, so typing never re-renders the timeline.
|
|
60
78
|
const draftKey = `thread:${threadRef}`;
|
|
61
|
-
const { draft, recipients } = useSyncExternalStore(drafts.subscribe, () => drafts.getSnapshot(draftKey));
|
|
62
79
|
const [claimsOpen, setClaimsOpen] = useState(false);
|
|
63
80
|
// Early acceptance: the Human may accept while Claims are still open; the
|
|
64
81
|
// confirm dialog lists exactly what will be completed with the Task.
|
|
@@ -148,14 +165,15 @@ export function TeamThreadPage(props) {
|
|
|
148
165
|
setLoading(false);
|
|
149
166
|
}
|
|
150
167
|
};
|
|
151
|
-
|
|
168
|
+
/** One roster + view read. Answers false once the page is gone. */
|
|
169
|
+
const applySupplemental = async () => {
|
|
152
170
|
try {
|
|
153
171
|
const [loadedMembers, loadedView] = await Promise.all([
|
|
154
172
|
loadMembers({ workspaceId }),
|
|
155
173
|
loadChannels({ workspaceId, ...(channelRef === undefined ? {} : { channelRef }), threadRef, includeActivities: false, limit: 1 }),
|
|
156
174
|
]);
|
|
157
175
|
if (!mountedRef.current)
|
|
158
|
-
return;
|
|
176
|
+
return false;
|
|
159
177
|
if (loadedMembers.ok)
|
|
160
178
|
setMembers(loadedMembers.value);
|
|
161
179
|
if (loadedView.ok)
|
|
@@ -163,12 +181,49 @@ export function TeamThreadPage(props) {
|
|
|
163
181
|
const failure = [loadedMembers, loadedView].find(result => !result.ok);
|
|
164
182
|
if (failure !== undefined && !failure.ok)
|
|
165
183
|
setError(failure.error.message);
|
|
184
|
+
return true;
|
|
166
185
|
}
|
|
167
186
|
catch (cause) {
|
|
168
187
|
if (mountedRef.current)
|
|
169
188
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
189
|
+
return mountedRef.current;
|
|
170
190
|
}
|
|
171
191
|
};
|
|
192
|
+
// One Host change event wakes every scope it matches: here a workspace commit
|
|
193
|
+
// and a presence transition, whose separate polls both deliver that event's
|
|
194
|
+
// version. Both asks are the same roster and view read, so each round answers
|
|
195
|
+
// one version per scope, and a newer version arriving mid-round runs one
|
|
196
|
+
// trailing round instead of being dropped. The two scopes count different
|
|
197
|
+
// domains, so coverage and pending versions are tracked per scope.
|
|
198
|
+
const supplementalRef = useRef();
|
|
199
|
+
const supplementalCoverageRef = useRef(new ScopeCoverage());
|
|
200
|
+
const refreshSupplemental = (wake) => {
|
|
201
|
+
const inFlight = supplementalRef.current;
|
|
202
|
+
if (wake === undefined) {
|
|
203
|
+
if (inFlight !== undefined)
|
|
204
|
+
return inFlight;
|
|
205
|
+
}
|
|
206
|
+
else if (!supplementalCoverageRef.current.wake(wake.scope, wake.version)) {
|
|
207
|
+
return inFlight ?? Promise.resolve();
|
|
208
|
+
}
|
|
209
|
+
const coverage = supplementalCoverageRef.current;
|
|
210
|
+
const round = (async () => {
|
|
211
|
+
for (;;) {
|
|
212
|
+
coverage.beginRound();
|
|
213
|
+
const applied = await applySupplemental();
|
|
214
|
+
// A newer change landed while this round fetched: cover it too, and
|
|
215
|
+
// keep going only while the page is still the one asking.
|
|
216
|
+
if (!applied || !coverage.finishRound())
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
})().finally(() => {
|
|
220
|
+
// A remount may already own a newer round; only this one clears itself.
|
|
221
|
+
if (supplementalRef.current === round)
|
|
222
|
+
supplementalRef.current = undefined;
|
|
223
|
+
});
|
|
224
|
+
supplementalRef.current = round;
|
|
225
|
+
return round;
|
|
226
|
+
};
|
|
172
227
|
const refreshPassiveFacts = async () => {
|
|
173
228
|
try {
|
|
174
229
|
const result = await loadThreadHistory({ workspaceId, ...threadRequest, limit: 100 });
|
|
@@ -226,6 +281,9 @@ export function TeamThreadPage(props) {
|
|
|
226
281
|
};
|
|
227
282
|
useEffect(() => {
|
|
228
283
|
mountedRef.current = true;
|
|
284
|
+
// The previous mount's supplemental round must not answer for this one.
|
|
285
|
+
supplementalRef.current = undefined;
|
|
286
|
+
supplementalCoverageRef.current.reset();
|
|
229
287
|
projectionRef.current = undefined;
|
|
230
288
|
setProjection(undefined);
|
|
231
289
|
setChannelView(undefined);
|
|
@@ -298,7 +356,19 @@ export function TeamThreadPage(props) {
|
|
|
298
356
|
setError(update.message);
|
|
299
357
|
return;
|
|
300
358
|
}
|
|
301
|
-
void refreshSupplemental();
|
|
359
|
+
void refreshSupplemental({ scope: 'workspace', version: update.version });
|
|
360
|
+
}),
|
|
361
|
+
// Presence transitions commit nothing: only the member rows move, so
|
|
362
|
+
// the roster refresh rides the same supplemental fetch as workspace
|
|
363
|
+
// membership changes, leaving the timeline untouched.
|
|
364
|
+
subscribeChanges({ kind: 'presence', workspaceId }, update => {
|
|
365
|
+
if (!mountedRef.current)
|
|
366
|
+
return;
|
|
367
|
+
if (update.type === 'failed') {
|
|
368
|
+
setError(update.message);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
void refreshSupplemental({ scope: 'presence', version: update.version });
|
|
302
372
|
}),
|
|
303
373
|
];
|
|
304
374
|
return () => {
|
|
@@ -378,7 +448,8 @@ export function TeamThreadPage(props) {
|
|
|
378
448
|
// Branded-ref navigation for message bodies: channel refs hop to the
|
|
379
449
|
// Channel; the open Task's own refs are already on screen, and other Tasks
|
|
380
450
|
// are not resolvable from this surface, so they degrade to a no-op.
|
|
381
|
-
|
|
451
|
+
// Identity-stable: a fresh closure per render would defeat TeamMessage's memo.
|
|
452
|
+
const openRef = useCallback((ref) => {
|
|
382
453
|
if (ref.startsWith('channel:') && ref !== channelRef) {
|
|
383
454
|
selectChannel(ref);
|
|
384
455
|
return;
|
|
@@ -402,13 +473,13 @@ export function TeamThreadPage(props) {
|
|
|
402
473
|
// Another Task cited here: resolve its home Channel and jump.
|
|
403
474
|
jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
|
|
404
475
|
}
|
|
405
|
-
};
|
|
406
|
-
const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
|
|
476
|
+
}, [channelRef, threadRef, taskRef, workspaceId, selectChannel, selectThread, loadChannels, resolveTaskRefs]);
|
|
477
|
+
const lookupTaskRefs = useMemo(() => hostTaskRefLookup(resolveTaskRefs, workspaceId), [resolveTaskRefs, workspaceId]);
|
|
407
478
|
const renderFact = (fact, grouped = false) => {
|
|
408
479
|
if (fact.kind === 'message') {
|
|
409
480
|
const sender = memberName(fact.message.sender);
|
|
410
481
|
const senderStatus = members.find(candidate => candidate.member.memberId === fact.message.sender);
|
|
411
|
-
return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames:
|
|
482
|
+
return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames: stableMentionNames(fact.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: grouped, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }) }, factKey(fact));
|
|
412
483
|
}
|
|
413
484
|
return _jsxs("p", { className: threadCss.activityRow, children: [_jsx("span", { className: threadCss.activityMark, "aria-hidden": "true" }), _jsx("span", { className: threadCss.activityText, children: formatActivity(fact.activity, { t, actorName: memberName, claims: taskClaims }) })] }, factKey(fact));
|
|
414
485
|
};
|
|
@@ -553,7 +624,19 @@ export function TeamThreadPage(props) {
|
|
|
553
624
|
setMutating(undefined);
|
|
554
625
|
}
|
|
555
626
|
};
|
|
627
|
+
// Editing the draft invalidates one-shot send state: a confirmation token,
|
|
628
|
+
// the retained requestId, and the status line. Every setter returns the same
|
|
629
|
+
// state when there is nothing to clear, so a keystroke does not re-render the
|
|
630
|
+
// timeline now that the composer owns the draft.
|
|
631
|
+
const clearSendState = useCallback(() => {
|
|
632
|
+
setConfirmation(current => current === undefined ? current : undefined);
|
|
633
|
+
setReplyRequestId(current => current === undefined ? current : undefined);
|
|
634
|
+
setStatusMessage(current => current === undefined ? current : undefined);
|
|
635
|
+
}, []);
|
|
556
636
|
const sendReply = async () => {
|
|
637
|
+
// Read the draft at send time: this page no longer subscribes to it, so a
|
|
638
|
+
// captured render value would be stale after the composer's own edits.
|
|
639
|
+
const { draft, recipients } = drafts.getSnapshot(draftKey);
|
|
557
640
|
if (pending || thread === undefined || draft.trim() === '')
|
|
558
641
|
return;
|
|
559
642
|
const id = replyRequestId ?? mintRequestId();
|
|
@@ -563,21 +646,12 @@ export function TeamThreadPage(props) {
|
|
|
563
646
|
try {
|
|
564
647
|
// Upload chosen files first; any failure aborts the reply with the
|
|
565
648
|
// existing error surface and keeps the chips for a retry.
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
workspaceId,
|
|
571
|
-
name: file.name,
|
|
572
|
-
mediaType: file.type === '' ? undefined : file.type,
|
|
573
|
-
bytesBase64: bytesToBase64(new Uint8Array(await file.arrayBuffer())),
|
|
574
|
-
});
|
|
575
|
-
if (!uploaded.ok) {
|
|
576
|
-
setError(uploaded.error.message);
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
attachmentIds.push(uploaded.value.attachmentId);
|
|
649
|
+
const upload = await uploadComposerFiles(putAttachment, workspaceId, pendingFiles);
|
|
650
|
+
if (!upload.ok) {
|
|
651
|
+
setError(upload.error);
|
|
652
|
+
return;
|
|
580
653
|
}
|
|
654
|
+
const attachmentIds = upload.attachmentIds;
|
|
581
655
|
const result = await reply({ requestId: id, workspaceId, threadRef, ...(task === undefined ? {} : { taskRef: task.taskRef }), body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
|
|
582
656
|
if (!result.ok) {
|
|
583
657
|
setError(result.error.message);
|
|
@@ -585,8 +659,12 @@ export function TeamThreadPage(props) {
|
|
|
585
659
|
}
|
|
586
660
|
if (result.value.kind === 'committed') {
|
|
587
661
|
const committed = result.value;
|
|
662
|
+
// The optimistic row chips what the Host is about to report: a
|
|
663
|
+
// hand-typed `@Handle` delivers exactly like a picked recipient, and the
|
|
664
|
+
// committed facts replace this row as soon as they arrive.
|
|
665
|
+
const optimisticMentions = [...new Set([...recipients, ...mentionedMemberIds(draft, channelMembers)])].sort();
|
|
588
666
|
setCurrentFacts(current => {
|
|
589
|
-
const merged = mergeFacts(current, [{ kind: 'message', sequence: committed.message.sequence, message: committed.message, mentions:
|
|
667
|
+
const merged = mergeFacts(current, [{ kind: 'message', sequence: committed.message.sequence, message: committed.message, mentions: optimisticMentions, occurredAt: committed.receipt.occurredAt }]);
|
|
590
668
|
currentFactsRef.current = merged;
|
|
591
669
|
return merged;
|
|
592
670
|
});
|
|
@@ -654,7 +732,7 @@ export function TeamThreadPage(props) {
|
|
|
654
732
|
} }, children: t('acceptTask') });
|
|
655
733
|
})(), task.resolution === 'open'
|
|
656
734
|
? _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('close'); }, children: t('closeTask') })
|
|
657
|
-
: _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: status
|
|
735
|
+
: _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: diagnosticText(status) || t('statusError') }), " \u00B7 ", claim.direction] })] }, claim.claimRef))] }), task !== undefined && thread !== undefined && (() => {
|
|
658
736
|
// Recomputed here so the confirm list never shows stale rows.
|
|
659
737
|
const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
|
|
660
738
|
return _jsxs(Modal, { open: confirmingAccept, onClose: () => { if (!pending)
|
|
@@ -664,5 +742,5 @@ export function TeamThreadPage(props) {
|
|
|
664
742
|
return _jsxs("article", { className: threadCss.claimRow, children: [ownerStatus === undefined ? _jsx("span", {}) : _jsx(TeamPresenceDot, { status: ownerStatus, t: t }), _jsx("strong", { className: threadCss.claimOwner, children: memberName(claim.owner) }), _jsx("span", { className: threadCss.claimDirection, children: claim.direction }), _jsx("small", { className: threadCss.claimState, children: formatClaimState(claim.state, t) })] }, claim.claimRef);
|
|
665
743
|
})] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: handleTimelineScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), autoReadExhausted && _jsxs("div", { className: css.timelineAction, role: "alert", children: [_jsx("span", { children: t('autoReadIncomplete') }), _jsx(Button, { size: "sm", onClick: () => { setAutoReadExhausted(false); void drainUnread(); }, disabled: loading, children: t('retry') })] }), newFactsCount > 0 && _jsx("div", { className: threadCss.newUpdates, role: "status", children: _jsx("button", { type: "button", className: threadCss.newUpdatesJump, onClick: () => { setNewFactsCount(0); timeline.scrollToBottom(); }, children: t('newUpdatesJump', { count: newFactsCount }) }) })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
|
|
666
744
|
? _jsxs("div", { className: threadCss.closedBar, "data-team-closed": true, children: [error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error }), _jsxs("div", { className: threadCss.closedNotice, children: [_jsx("span", { children: t('taskClosedNotice') }), _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] })
|
|
667
|
-
: _jsx(TeamComposer, { members: channelMembers, followerMemberIds: followerIds,
|
|
745
|
+
: _jsx(TeamComposer, { members: channelMembers, followerMemberIds: followerIds, drafts: drafts, draftKey: draftKey, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onEdit: clearSendState, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
|
|
668
746
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { TeamSidebarProps } from './slots.ts';
|
|
2
|
-
export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, archiveChannel, updateMember, recoverMember, archiveMember, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
|
|
2
|
+
export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, selectInbox, t, useWorkspaces, loadMembers, loadInbox, subscribeChanges, subscribeReads, addMember, loadChannels, createChannel, updateChannel, archiveChannel, updateMember, recoverMember, archiveMember, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=TeamWorkspaceBrowser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAyBlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BAoHtY"}
|