@wowyuarm/dsh-agent-team 0.1.2 → 0.1.3
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 +1 -0
- package/README.zh.md +2 -0
- package/package.json +12 -3
- package/packages/agent-team/README.md +1 -1
- package/packages/agent-team/README.zh.md +1 -1
- package/packages/agent-team/lib/attachments.js +11 -12
- package/packages/agent-team/lib/auto-compaction.js +163 -0
- package/packages/agent-team/lib/index.js +214 -130
- package/packages/agent-team/lib/ledger.js +77 -70
- package/packages/agent-team/lib/recovery.js +30 -35
- package/packages/agent-team/lib/typert.host.js +33 -23
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +21 -21
- package/packages/agent-team/lib/types/attachments.d.ts +2 -2
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
- package/packages/agent-team/lib/types/auto-compaction.d.ts +38 -0
- package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +40 -12
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +13 -0
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/recovery.d.ts +15 -16
- package/packages/agent-team/lib/types/recovery.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +6 -1
- 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 +942 -534
- 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 +5 -166
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +6 -20
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -2
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +0 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +34 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +169 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +17 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +120 -0
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +83 -43
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +10 -23
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +71 -1
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +22 -0
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/member-session-input.js +98 -0
- package/packages/client-agent-team/lib/types/client/requests.d.ts +16 -0
- package/packages/client-agent-team/lib/types/client/requests.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/requests.js +22 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +6 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +19 -0
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +43 -3
- 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 +61 -4
- package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-membership.js +2 -1
- package/packages/tool-agent-team/lib/index.js +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +0 -13
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.js +0 -90
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const TEAM_COMMAND_SOURCE = 'agent-team-command';
|
|
2
|
+
export const TEAM_MEMBER_SOURCE = 'agent-team-member';
|
|
3
|
+
/** Preserve the public Session command admission contract, not handler outcome. */
|
|
4
|
+
export async function admitTeamCompact(session) {
|
|
5
|
+
const result = await session.command('/compact');
|
|
6
|
+
if (!result.ok)
|
|
7
|
+
throw new Error(`session.command failed: ${result.error.message}`);
|
|
8
|
+
return result.value.matched ? { kind: 'success' } : { kind: 'error', text: 'unknown command: /compact' };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The two Team-only sources live in the shared public registry, but their
|
|
12
|
+
* candidate plane is inert for every ordinary Session. The member source owns
|
|
13
|
+
* its stable ref→label cache so serialization never falls back to guessing a
|
|
14
|
+
* handle from plain text.
|
|
15
|
+
*/
|
|
16
|
+
export function createTeamMemberSessionSources(options) {
|
|
17
|
+
const labels = new Map();
|
|
18
|
+
const command = {
|
|
19
|
+
trigger: '/',
|
|
20
|
+
name: TEAM_COMMAND_SOURCE,
|
|
21
|
+
showGroupTitle: false,
|
|
22
|
+
async candidates(session, { query }) {
|
|
23
|
+
if (!options.isEmbeddedMemberSession(session.sessionId) || !'compact'.startsWith(query.toLocaleLowerCase()))
|
|
24
|
+
return [];
|
|
25
|
+
return [{ name: '/compact', description: '压缩当前 Session 上下文', value: 'compact' }];
|
|
26
|
+
},
|
|
27
|
+
onPick({ candidate, session }) {
|
|
28
|
+
if (candidate.value !== 'compact' || !options.isEmbeddedMemberSession(session.sessionId))
|
|
29
|
+
return undefined;
|
|
30
|
+
const claim = {
|
|
31
|
+
token: '/compact',
|
|
32
|
+
submit: async () => options.executeCompact(session.sessionId),
|
|
33
|
+
};
|
|
34
|
+
return { claim };
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const member = {
|
|
38
|
+
trigger: '@',
|
|
39
|
+
name: TEAM_MEMBER_SOURCE,
|
|
40
|
+
showGroupTitle: false,
|
|
41
|
+
async candidates(session, { query, signal }) {
|
|
42
|
+
if (!options.isEmbeddedMemberSession(session.sessionId))
|
|
43
|
+
return [];
|
|
44
|
+
const normalized = query.toLocaleLowerCase();
|
|
45
|
+
const members = await options.members();
|
|
46
|
+
if (signal.aborted)
|
|
47
|
+
return [];
|
|
48
|
+
return members
|
|
49
|
+
.filter(status => status.member.sessionId !== session.sessionId && status.member.state !== 'inactive')
|
|
50
|
+
.filter(status => status.member.handle.toLocaleLowerCase().startsWith(normalized))
|
|
51
|
+
.map(status => {
|
|
52
|
+
labels.set(status.member.memberId, `@${status.member.handle}`);
|
|
53
|
+
return {
|
|
54
|
+
name: `@${status.member.handle}`,
|
|
55
|
+
description: '引用成员 · 不会通知',
|
|
56
|
+
value: String(status.member.memberId),
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
onPick({ candidate, session }) {
|
|
61
|
+
if (!options.isEmbeddedMemberSession(session.sessionId) || candidate.value === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
const ref = candidate.value;
|
|
64
|
+
const label = labels.get(ref);
|
|
65
|
+
if (label === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
const insert = {
|
|
68
|
+
source: TEAM_MEMBER_SOURCE,
|
|
69
|
+
// The input machine retains the typed trigger; label must therefore
|
|
70
|
+
// omit `@` or the draft would become `@@handle` after insertion.
|
|
71
|
+
label: label.slice(1),
|
|
72
|
+
ref,
|
|
73
|
+
clipboardText: label,
|
|
74
|
+
};
|
|
75
|
+
return { insert };
|
|
76
|
+
},
|
|
77
|
+
codec: {
|
|
78
|
+
clipboardText: ref => labels.get(ref) ?? `@${String(ref)}`,
|
|
79
|
+
serialize: async (ref) => {
|
|
80
|
+
const stable = String(ref);
|
|
81
|
+
const label = labels.get(ref) ?? '';
|
|
82
|
+
return `<team-member ref="${escapeAttribute(stable)}">${escapeText(label)}</team-member>`;
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
return [command, member];
|
|
87
|
+
}
|
|
88
|
+
/** Unknown leading slash must never fall into the shipped global palette. */
|
|
89
|
+
export function allowsMemberSessionSubmit(draft, claimToken) {
|
|
90
|
+
const trimmed = draft.trimStart();
|
|
91
|
+
return !trimmed.startsWith('/') || claimToken === '/compact';
|
|
92
|
+
}
|
|
93
|
+
function escapeText(value) {
|
|
94
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
95
|
+
}
|
|
96
|
+
function escapeAttribute(value) {
|
|
97
|
+
return escapeText(value).replaceAll('"', '"').replaceAll("'", ''');
|
|
98
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
2
|
+
import type { AgentTeamAttachmentId, AgentTeamPutAttachmentRequest, AgentTeamPutAttachmentResult, AgentTeamRequestId } from '@wowyuarm/dsh-agent-team/types';
|
|
3
|
+
/** Fresh idempotency identity for one Client-initiated durable request. */
|
|
4
|
+
export declare const mintRequestId: () => AgentTeamRequestId;
|
|
5
|
+
/**
|
|
6
|
+
* Upload composer files in order. One failure stops with the error text; the
|
|
7
|
+
* caller keeps its chips so a retry uploads only the still-pending files.
|
|
8
|
+
*/
|
|
9
|
+
export declare const uploadComposerFiles: (putAttachment: (request: AgentTeamPutAttachmentRequest) => Promise<RemoteResult<AgentTeamPutAttachmentResult>>, workspaceId: AgentTeamPutAttachmentRequest["workspaceId"], files: readonly File[]) => Promise<{
|
|
10
|
+
ok: true;
|
|
11
|
+
attachmentIds: readonly AgentTeamAttachmentId[];
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=requests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.d.ts","sourceRoot":"","sources":["../../../src/client/requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EACV,qBAAqB,EAAE,6BAA6B,EAAE,4BAA4B,EAAE,kBAAkB,EACvG,MAAM,gCAAgC,CAAA;AAGvC,2EAA2E;AAC3E,eAAO,MAAM,aAAa,QAAO,kBAA+D,CAAA;AAEhG;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,eAAe,CAAC,OAAO,EAAE,6BAA6B,KAAK,OAAO,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC,EAC9G,aAAa,6BAA6B,CAAC,aAAa,CAAC,EACzD,OAAO,SAAS,IAAI,EAAE,KACrB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,aAAa,EAAE,SAAS,qBAAqB,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAatG,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { bytesToBase64 } from "./attachment-preview.js";
|
|
2
|
+
/** Fresh idempotency identity for one Client-initiated durable request. */
|
|
3
|
+
export const mintRequestId = () => crypto.randomUUID();
|
|
4
|
+
/**
|
|
5
|
+
* Upload composer files in order. One failure stops with the error text; the
|
|
6
|
+
* caller keeps its chips so a retry uploads only the still-pending files.
|
|
7
|
+
*/
|
|
8
|
+
export const uploadComposerFiles = async (putAttachment, workspaceId, files) => {
|
|
9
|
+
const attachmentIds = [];
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
const uploaded = await putAttachment({
|
|
12
|
+
requestId: mintRequestId(), workspaceId,
|
|
13
|
+
name: file.name,
|
|
14
|
+
mediaType: file.type === '' ? undefined : file.type,
|
|
15
|
+
bytesBase64: bytesToBase64(new Uint8Array(await file.arrayBuffer())),
|
|
16
|
+
});
|
|
17
|
+
if (!uploaded.ok)
|
|
18
|
+
return { ok: false, error: uploaded.error.message };
|
|
19
|
+
attachmentIds.push(uploaded.value.attachmentId);
|
|
20
|
+
}
|
|
21
|
+
return { ok: true, attachmentIds };
|
|
22
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,KAAK,EACV,yBAAyB,EACzB,mBAAmB,EACnB,2BAA2B,EAC3B,6BAA6B,EAC7B,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,qBAAqB,EACrB,6BAA6B,EAC7B,4BAA4B,EAC5B,uBAAuB,EACvB,mCAAmC,EACnC,kCAAkC,EAClC,qBAAqB,EACrB,+BAA+B,EAC/B,8BAA8B,
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
2
|
+
import type { AgentTeamChannelRef, AgentTeamResolveTaskRefsRequest, AgentTeamResolveTaskRefsResult, AgentTeamTaskRef, AgentTeamThreadRef } from '@wowyuarm/dsh-agent-team/types';
|
|
2
3
|
/** Navigation facts for one branded Task ref, resolved once per session. */
|
|
3
4
|
export interface ResolvedTaskRef {
|
|
4
5
|
readonly taskRef: AgentTeamTaskRef;
|
|
@@ -11,6 +12,10 @@ export declare const useResolvedTaskRefVersion: () => number;
|
|
|
11
12
|
export declare const cachedResolvedTaskRef: (taskRef: AgentTeamTaskRef) => ResolvedTaskRef | undefined;
|
|
12
13
|
/** Store one resolution (click path) and wake every rendered link. */
|
|
13
14
|
export declare const rememberResolvedTaskRef: (entry: ResolvedTaskRef) => void;
|
|
15
|
+
/** Click-path Host lookup: remember every resolved entry and hand them back for immediate navigation. */
|
|
16
|
+
export declare const hostTaskRefLookup: (resolveTaskRefs: (request: AgentTeamResolveTaskRefsRequest) => Promise<RemoteResult<AgentTeamResolveTaskRefsResult>>, workspaceId: AgentTeamResolveTaskRefsRequest["workspaceId"]) => ((taskRefs: readonly AgentTeamTaskRef[]) => Promise<readonly ResolvedTaskRef[]>);
|
|
17
|
+
/** Resolve one Task ref through the Host and jump to its home Channel Thread. */
|
|
18
|
+
export declare const jumpToTaskThread: (resolveTaskRefs: (request: AgentTeamResolveTaskRefsRequest) => Promise<RemoteResult<AgentTeamResolveTaskRefsResult>>, workspaceId: AgentTeamResolveTaskRefsRequest["workspaceId"], taskRef: AgentTeamTaskRef, selectThread: (taskRef: AgentTeamTaskRef, threadRef: AgentTeamThreadRef, channelRef: AgentTeamChannelRef, taskNumber: number) => void) => void;
|
|
14
19
|
/**
|
|
15
20
|
* Batch-resolve unknown refs through the Host lookup. Concurrent callers
|
|
16
21
|
* deduplicate through the pending set; failures just clear the pending mark
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-refs.d.ts","sourceRoot":"","sources":["../../../src/client/task-refs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"task-refs.d.ts","sourceRoot":"","sources":["../../../src/client/task-refs.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EACV,mBAAmB,EAAE,+BAA+B,EAAE,8BAA8B,EAAE,gBAAgB,EAAE,kBAAkB,EAC3H,MAAM,gCAAgC,CAAA;AAEvC,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAwBD,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,QAAO,MAAmE,CAAA;AAEhH,eAAO,MAAM,qBAAqB,GAAI,SAAS,gBAAgB,KAAG,eAAe,GAAG,SAAkC,CAAA;AAEtH,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,GAAI,OAAO,eAAe,KAAG,IAIhE,CAAA;AAED,yGAAyG;AACzG,eAAO,MAAM,iBAAiB,GAC5B,iBAAiB,CAAC,OAAO,EAAE,+BAA+B,KAAK,OAAO,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC,EACpH,aAAa,+BAA+B,CAAC,aAAa,CAAC,KAC1D,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAM/E,CAAA;AAEH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,GAC3B,iBAAiB,CAAC,OAAO,EAAE,+BAA+B,KAAK,OAAO,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC,EACpH,aAAa,+BAA+B,CAAC,aAAa,CAAC,EAC3D,SAAS,gBAAgB,EACzB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,KACpI,IAMF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GACjC,MAAM,SAAS,gBAAgB,EAAE,EACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,KACrF,OAAO,CAAC,IAAI,CAmBd,CAAA"}
|
|
@@ -25,6 +25,25 @@ export const rememberResolvedTaskRef = (entry) => {
|
|
|
25
25
|
pending.delete(entry.taskRef);
|
|
26
26
|
emit();
|
|
27
27
|
};
|
|
28
|
+
/** Click-path Host lookup: remember every resolved entry and hand them back for immediate navigation. */
|
|
29
|
+
export const hostTaskRefLookup = (resolveTaskRefs, workspaceId) => async (taskRefs) => {
|
|
30
|
+
const result = await resolveTaskRefs({ workspaceId, taskRefs });
|
|
31
|
+
if (!result.ok)
|
|
32
|
+
return [];
|
|
33
|
+
for (const entry of result.value.resolved)
|
|
34
|
+
rememberResolvedTaskRef(entry);
|
|
35
|
+
return result.value.resolved;
|
|
36
|
+
};
|
|
37
|
+
/** Resolve one Task ref through the Host and jump to its home Channel Thread. */
|
|
38
|
+
export const jumpToTaskThread = (resolveTaskRefs, workspaceId, taskRef, selectThread) => {
|
|
39
|
+
void resolveTaskRefs({ workspaceId, taskRefs: [taskRef] }).then(result => {
|
|
40
|
+
if (!result.ok)
|
|
41
|
+
return;
|
|
42
|
+
const hit = result.value.resolved[0];
|
|
43
|
+
if (hit !== undefined)
|
|
44
|
+
selectThread(hit.taskRef, hit.threadRef, hit.channelRef, hit.taskNumber);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
28
47
|
/**
|
|
29
48
|
* Batch-resolve unknown refs through the Host lookup. Concurrent callers
|
|
30
49
|
* deduplicate through the pending set; failures just clear the pending mark
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentTeamActivity, AgentTeamClaim, AgentTeamMemberId, AgentTeamTask } from '@wowyuarm/dsh-agent-team/types';
|
|
1
|
+
import type { AgentTeamActivity, AgentTeamClaim, AgentTeamMemberId, AgentTeamTask, AgentTeamTaskRef } from '@wowyuarm/dsh-agent-team/types';
|
|
2
2
|
import type { TeamConversationProps } from './slots.ts';
|
|
3
3
|
export declare function formatTaskStatus(status: AgentTeamTask['status'], t: TeamConversationProps['t']): string;
|
|
4
4
|
export declare function formatClaimState(state: AgentTeamClaim['state'], t: TeamConversationProps['t']): string;
|
|
@@ -22,10 +22,18 @@ export interface RefSegment {
|
|
|
22
22
|
/**
|
|
23
23
|
* Split a literal text run into plain and branded-ref segments. The pattern
|
|
24
24
|
* anchors on the fixed ref prefixes plus the UUID shape, so ordinary prose
|
|
25
|
-
* containing a colon never linkifies
|
|
26
|
-
*
|
|
25
|
+
* containing a colon never linkifies; a doubled colon from model output is
|
|
26
|
+
* tolerated. Segment refs are always canonical, so resolution and navigation
|
|
27
|
+
* work regardless of how the ref was spelled; text without any ref comes
|
|
28
|
+
* back as one untouched segment.
|
|
27
29
|
*/
|
|
28
30
|
export declare function splitBrandedRefs(text: string): readonly RefSegment[];
|
|
31
|
+
/**
|
|
32
|
+
* Whether one string's whole content is exactly one branded ref. A code span
|
|
33
|
+
* like this is the model styling a ref as an identifier, not publishing code,
|
|
34
|
+
* so the Markdown pass may linkify it; anything larger stays literal.
|
|
35
|
+
*/
|
|
36
|
+
export declare function isSingleBrandedRef(text: string): boolean;
|
|
29
37
|
export interface MentionSegment {
|
|
30
38
|
readonly text: string;
|
|
31
39
|
readonly mention: boolean;
|
|
@@ -65,4 +73,36 @@ export declare function formatActivity(activity: AgentTeamActivity, options: {
|
|
|
65
73
|
}): string;
|
|
66
74
|
/** Remove the machine-facing `[attachment] <path>` prompt lines from a body before display. */
|
|
67
75
|
export declare function stripAttachmentLines(body: string): string;
|
|
76
|
+
/** How one Message body renders: mention-chip segments, literal text, or Markdown. */
|
|
77
|
+
export type MessageBodyRender = 'inline' | 'literal' | 'markdown';
|
|
78
|
+
/** Rendering decision for one Message body, resolved once from its stored form. */
|
|
79
|
+
export interface PlannedMessageBody {
|
|
80
|
+
/** Stored body without machine-facing attachment prompt lines; the raw body when stripping would empty it. */
|
|
81
|
+
readonly displayBody: string;
|
|
82
|
+
/** Rich Agent Markdown: only such bodies get the post-render chipify pass. */
|
|
83
|
+
readonly richAgentBody: boolean;
|
|
84
|
+
/** Which rendering branch the body takes. */
|
|
85
|
+
readonly render: MessageBodyRender;
|
|
86
|
+
/** Mention-chip segments for literal bodies; absent on the Markdown branch. */
|
|
87
|
+
readonly inline?: ReturnType<typeof splitMentionNames>;
|
|
88
|
+
/** Mention handles that did not render as chips; the trailing row shows them. */
|
|
89
|
+
readonly fallbackNames: readonly string[];
|
|
90
|
+
/** Non-Task branded refs for the trailing fallback row; rich Agent bodies keep the legacy row. */
|
|
91
|
+
readonly fallbackRefs: readonly string[];
|
|
92
|
+
/** Task refs authored in a literal body; resolved labels replace them in place. */
|
|
93
|
+
readonly taskRefs: readonly AgentTeamTaskRef[];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Decide how one Message body renders. Human input and plain-prose Agent
|
|
97
|
+
* bodies stay literal, with structured mention chips inline where possible;
|
|
98
|
+
* rich Agent Markdown keeps unmatched mentions and non-Task refs in the
|
|
99
|
+
* trailing fallback row while the post-render pass handles Task refs at their
|
|
100
|
+
* authored position. Surfaces without ref navigation render everything
|
|
101
|
+
* literally and keep the full fallback row.
|
|
102
|
+
*/
|
|
103
|
+
export declare function planMessageBody(body: string, options: {
|
|
104
|
+
readonly human: boolean;
|
|
105
|
+
readonly mentionNames?: readonly string[];
|
|
106
|
+
readonly canOpenRefs: boolean;
|
|
107
|
+
}): PlannedMessageBody;
|
|
68
108
|
//# sourceMappingURL=team-formatters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-formatters.d.ts","sourceRoot":"","sources":["../../../src/client/team-formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"team-formatters.d.ts","sourceRoot":"","sources":["../../../src/client/team-formatters.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,12 +35,22 @@ export function memberHue(memberId) {
|
|
|
35
35
|
hash = (hash * 31 + memberId.charCodeAt(index)) % 360;
|
|
36
36
|
return hash;
|
|
37
37
|
}
|
|
38
|
-
const BRANDED_REF_PATTERN = /\b(task|channel|thread):[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
|
|
38
|
+
const BRANDED_REF_PATTERN = /\b(task|channel|thread):{1,2}[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi;
|
|
39
|
+
/**
|
|
40
|
+
* Canonical form of one matched ref: models occasionally double the colon or
|
|
41
|
+
* uppercase the UUID when citing a ref in prose, while Host lookups and
|
|
42
|
+
* navigation only accept the lowercase single-colon ref the ledger mints.
|
|
43
|
+
*/
|
|
44
|
+
function canonicalBrandedRef(match) {
|
|
45
|
+
return match.replace('::', ':').toLowerCase();
|
|
46
|
+
}
|
|
39
47
|
/**
|
|
40
48
|
* Split a literal text run into plain and branded-ref segments. The pattern
|
|
41
49
|
* anchors on the fixed ref prefixes plus the UUID shape, so ordinary prose
|
|
42
|
-
* containing a colon never linkifies
|
|
43
|
-
*
|
|
50
|
+
* containing a colon never linkifies; a doubled colon from model output is
|
|
51
|
+
* tolerated. Segment refs are always canonical, so resolution and navigation
|
|
52
|
+
* work regardless of how the ref was spelled; text without any ref comes
|
|
53
|
+
* back as one untouched segment.
|
|
44
54
|
*/
|
|
45
55
|
export function splitBrandedRefs(text) {
|
|
46
56
|
const segments = [];
|
|
@@ -49,13 +59,25 @@ export function splitBrandedRefs(text) {
|
|
|
49
59
|
const start = match.index ?? 0;
|
|
50
60
|
if (start > cursor)
|
|
51
61
|
segments.push({ text: text.slice(cursor, start) });
|
|
52
|
-
segments.push({ text: match[0], ref: match[0] });
|
|
62
|
+
segments.push({ text: match[0], ref: canonicalBrandedRef(match[0]) });
|
|
53
63
|
cursor = start + match[0].length;
|
|
54
64
|
}
|
|
55
65
|
if (cursor < text.length)
|
|
56
66
|
segments.push({ text: text.slice(cursor) });
|
|
57
67
|
return segments;
|
|
58
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether one string's whole content is exactly one branded ref. A code span
|
|
71
|
+
* like this is the model styling a ref as an identifier, not publishing code,
|
|
72
|
+
* so the Markdown pass may linkify it; anything larger stays literal.
|
|
73
|
+
*/
|
|
74
|
+
export function isSingleBrandedRef(text) {
|
|
75
|
+
const trimmed = text.trim();
|
|
76
|
+
if (trimmed === '')
|
|
77
|
+
return false;
|
|
78
|
+
const matches = [...trimmed.matchAll(BRANDED_REF_PATTERN)];
|
|
79
|
+
return matches.length === 1 && matches[0][0] === trimmed;
|
|
80
|
+
}
|
|
59
81
|
function escapeRegExp(value) {
|
|
60
82
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
61
83
|
}
|
|
@@ -154,3 +176,38 @@ export function formatActivity(activity, options) {
|
|
|
154
176
|
export function stripAttachmentLines(body) {
|
|
155
177
|
return body.replaceAll(/^\[attachment\] .*$(\n)?/gm, '').replace(/\n+$/, '');
|
|
156
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Decide how one Message body renders. Human input and plain-prose Agent
|
|
181
|
+
* bodies stay literal, with structured mention chips inline where possible;
|
|
182
|
+
* rich Agent Markdown keeps unmatched mentions and non-Task refs in the
|
|
183
|
+
* trailing fallback row while the post-render pass handles Task refs at their
|
|
184
|
+
* authored position. Surfaces without ref navigation render everything
|
|
185
|
+
* literally and keep the full fallback row.
|
|
186
|
+
*/
|
|
187
|
+
export function planMessageBody(body, options) {
|
|
188
|
+
const stripped = stripAttachmentLines(body);
|
|
189
|
+
const displayBody = stripped === '' ? body : stripped;
|
|
190
|
+
const richAgentBody = !options.human && !isPlainTextBody(displayBody);
|
|
191
|
+
const inline = (options.human || isPlainTextBody(displayBody)) && options.mentionNames !== undefined && options.mentionNames.length > 0
|
|
192
|
+
? splitMentionNames(displayBody, options.mentionNames)
|
|
193
|
+
: undefined;
|
|
194
|
+
const fallbackNames = inline !== undefined ? inline.unmatched
|
|
195
|
+
: richAgentBody && options.canOpenRefs && options.mentionNames !== undefined
|
|
196
|
+
? splitMentionNames(displayBody, options.mentionNames).unmatched
|
|
197
|
+
: options.mentionNames ?? [];
|
|
198
|
+
const refs = splitBrandedRefs(displayBody).flatMap(segment => segment.ref === undefined ? [] : [segment.ref]);
|
|
199
|
+
const render = inline !== undefined ? 'inline'
|
|
200
|
+
: options.human || (options.canOpenRefs && !richAgentBody && refs.length > 0) ? 'literal'
|
|
201
|
+
: 'markdown';
|
|
202
|
+
return {
|
|
203
|
+
displayBody,
|
|
204
|
+
richAgentBody,
|
|
205
|
+
render,
|
|
206
|
+
...(inline === undefined ? {} : { inline }),
|
|
207
|
+
fallbackNames,
|
|
208
|
+
fallbackRefs: richAgentBody && options.canOpenRefs ? refs.filter(ref => !ref.startsWith('task:')) : [],
|
|
209
|
+
taskRefs: options.canOpenRefs && !richAgentBody
|
|
210
|
+
? refs.filter(ref => ref.startsWith('task:')).map(ref => ref)
|
|
211
|
+
: [],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-membership.d.ts","sourceRoot":"","sources":["../../../src/client/team-membership.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"team-membership.d.ts","sourceRoot":"","sources":["../../../src/client/team-membership.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,mCAAmC,EACnC,kCAAkC,EAEnC,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,2BAA2B,KAAK,OAAO,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC,CAAA;IACjH,QAAQ,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,OAAO,CAAC,YAAY,CAAC,kCAAkC,CAAC,CAAC,CAAA;CAC1I;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,MAAM,EACrD,WAAW,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACrE;IACD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5C,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpE,CA6BA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useRef, useState } from 'react';
|
|
2
|
+
import { mintRequestId } from "./requests.js";
|
|
2
3
|
/**
|
|
3
4
|
* Shared Channel membership mutation. One stable requestId per direction,
|
|
4
5
|
* Member, and Channel survives transport failures until the Host commits it;
|
|
@@ -15,7 +16,7 @@ export function useChannelMembership(transport, rowKeyOf, onCommitted) {
|
|
|
15
16
|
setPending(current => new Set(current).add(rowKey));
|
|
16
17
|
setErrors(current => { const next = new Map(current); next.delete(rowKey); return next; });
|
|
17
18
|
const key = `${membership.joined ? 'remove' : 'join'}:${membership.memberId}:${membership.channelRef}`;
|
|
18
|
-
const requestId = requestIds.current.get(key) ??
|
|
19
|
+
const requestId = requestIds.current.get(key) ?? mintRequestId();
|
|
19
20
|
requestIds.current.set(key, requestId);
|
|
20
21
|
const request = { requestId, workspaceId: membership.workspaceId, channelRef: membership.channelRef, memberId: membership.memberId };
|
|
21
22
|
try {
|
|
@@ -136,7 +136,7 @@ const teamMessage = markAgentTeamPreset(defineTool({
|
|
|
136
136
|
action: { type: 'string', required: true, enum: ['start', 'reply'] },
|
|
137
137
|
channelRef: { type: 'string', description: "Full branded Channel ref exactly as returned by Team tools, including the 'channel:' prefix." },
|
|
138
138
|
taskRef: { type: 'string', description: "Full branded Task ref exactly as returned by Team tools, including the 'task:' prefix." },
|
|
139
|
-
body: { type: 'string', required: true }, baseRevision: { type: 'number', description: "Positive integer; use the current Thread revision as shown by the latest team_inbox or team_thread result for this Task. The revision is an internal concurrency token, not a citable fact." },
|
|
139
|
+
body: { type: 'string', required: true, description: "Markdown body. Cite Team refs exactly as returned, as bare text with one colon (e.g. task:0f0a…) — never a double colon, never inside backticks or quotes. Spell each mentioned Member's handle in the prose so the mention renders inline." }, baseRevision: { type: 'number', description: "Positive integer; use the current Thread revision as shown by the latest team_inbox or team_thread result for this Task. The revision is an internal concurrency token, not a citable fact." },
|
|
140
140
|
mentions: { type: 'array', items: { type: 'string' }, description: 'Member refs to mention. Mentioned Agents receive the Message directly; write their handles in the body (any casing, optional @) so the mention renders inline.' },
|
|
141
141
|
attachments: { type: 'array', items: { type: 'string' }, description: 'Absolute file paths to share, e.g. screenshots or generated artifacts; images render as thumbnails for recipients. The Host validates each path and copies the file into the attachment cache, and members also receive one cached path per attachment; if any path fails validation the whole send is rejected.' },
|
|
142
142
|
},
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
-
import type { TeamConversationProps } from './slots.ts';
|
|
3
|
-
interface TeamInboxPanelProps {
|
|
4
|
-
readonly workspaceId: WorkspaceId;
|
|
5
|
-
readonly loadInbox: TeamConversationProps['loadInbox'];
|
|
6
|
-
readonly loadChannels: TeamConversationProps['loadChannels'];
|
|
7
|
-
readonly loadChanges: TeamConversationProps['loadChanges'];
|
|
8
|
-
readonly selectThread: TeamConversationProps['selectThread'];
|
|
9
|
-
readonly t: TeamConversationProps['t'];
|
|
10
|
-
}
|
|
11
|
-
export declare function TeamInboxPanel({ workspaceId, loadInbox, loadChannels, loadChanges, selectThread, t }: TeamInboxPanelProps): import("react").JSX.Element;
|
|
12
|
-
export {};
|
|
13
|
-
//# sourceMappingURL=TeamInboxPanel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TeamInboxPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAED,wBAAgB,cAAc,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,mBAAmB,+BAkGzH"}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
-
import { formatTaskStatus } from "./team-formatters.js";
|
|
5
|
-
import css from './inbox.module.css';
|
|
6
|
-
export function TeamInboxPanel({ workspaceId, loadInbox, loadChannels, loadChanges, selectThread, t }) {
|
|
7
|
-
const [inbox, setInbox] = useState();
|
|
8
|
-
const [view, setView] = useState();
|
|
9
|
-
const [error, setError] = useState();
|
|
10
|
-
const [loading, setLoading] = useState(true);
|
|
11
|
-
const active = useRef(false);
|
|
12
|
-
const refreshSequence = useRef(0);
|
|
13
|
-
const refresh = async (clearError = false) => {
|
|
14
|
-
const sequence = refreshSequence.current + 1;
|
|
15
|
-
refreshSequence.current = sequence;
|
|
16
|
-
if (clearError)
|
|
17
|
-
setError(undefined);
|
|
18
|
-
try {
|
|
19
|
-
const [nextInbox, nextView] = await Promise.all([
|
|
20
|
-
loadInbox({ workspaceId }),
|
|
21
|
-
loadChannels({ workspaceId, topLevelOnly: true, includeActivities: false, limit: 100 }),
|
|
22
|
-
]);
|
|
23
|
-
if (!active.current || sequence !== refreshSequence.current)
|
|
24
|
-
return;
|
|
25
|
-
if (!nextInbox.ok) {
|
|
26
|
-
setError(nextInbox.error.message);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (!nextView.ok) {
|
|
30
|
-
setError(nextView.error.message);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
setInbox(nextInbox.value);
|
|
34
|
-
setView(nextView.value);
|
|
35
|
-
setError(undefined);
|
|
36
|
-
}
|
|
37
|
-
catch (cause) {
|
|
38
|
-
if (active.current && sequence === refreshSequence.current)
|
|
39
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
if (active.current && sequence === refreshSequence.current)
|
|
43
|
-
setLoading(false);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
let listening = true;
|
|
48
|
-
active.current = true;
|
|
49
|
-
setInbox(undefined);
|
|
50
|
-
setView(undefined);
|
|
51
|
-
setError(undefined);
|
|
52
|
-
setLoading(true);
|
|
53
|
-
void refresh();
|
|
54
|
-
void (async () => {
|
|
55
|
-
let version = 0;
|
|
56
|
-
while (listening) {
|
|
57
|
-
try {
|
|
58
|
-
const changed = await loadChanges({ afterVersion: version });
|
|
59
|
-
if (!listening)
|
|
60
|
-
return;
|
|
61
|
-
if (!changed.ok) {
|
|
62
|
-
setError(changed.error.message);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (changed.value.version > version) {
|
|
66
|
-
version = changed.value.version;
|
|
67
|
-
await refresh();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch (cause) {
|
|
71
|
-
if (listening)
|
|
72
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
})();
|
|
77
|
-
return () => {
|
|
78
|
-
listening = false;
|
|
79
|
-
active.current = false;
|
|
80
|
-
refreshSequence.current += 1;
|
|
81
|
-
};
|
|
82
|
-
}, [workspaceId]);
|
|
83
|
-
const channels = useMemo(() => new Map(view?.channels.map(channel => [channel.channelRef, channel]) ?? []), [view]);
|
|
84
|
-
const taskNumbers = useMemo(() => new Map(view?.taskNumbers.map(item => [item.taskRef, item.taskNumber]) ?? []), [view]);
|
|
85
|
-
return _jsxs("main", { className: css.surface, "data-team-inbox": true, children: [_jsxs("header", { className: css.header, children: [_jsxs("div", { children: [_jsx("h1", { children: t('inbox') }), inbox !== undefined && _jsx("p", { children: inbox.totalUnreadCount === 0 ? t('inboxEmpty') : t('inboxUnreadCount', { count: inbox.totalUnreadCount }) })] }), inbox !== undefined && inbox.totalDirectCount > 0 && _jsx("span", { className: css.directTotal, children: t('inboxDirectCount', { count: inbox.totalDirectCount }) })] }), _jsxs("section", { className: css.list, "aria-label": t('inbox'), children: [loading && inbox === undefined && error === undefined && _jsx("p", { className: css.state, children: t('loadingInbox') }), !loading && inbox?.items.length === 0 && error === undefined && _jsx("p", { className: css.state, children: t('inboxEmpty') }), error !== undefined && _jsxs("div", { className: css.error, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void refresh(true); }, children: t('retry') })] }), inbox?.items.map(item => {
|
|
86
|
-
const channel = channels.get(item.channelRef);
|
|
87
|
-
const taskNumber = taskNumbers.get(item.task.taskRef);
|
|
88
|
-
return _jsxs("button", { type: "button", className: css.item, onClick: () => { selectThread(item.task.taskRef, item.thread.threadRef, item.channelRef, taskNumber); }, children: [_jsx("span", { className: css.priority, "data-direct": item.directCount > 0, "aria-hidden": "true" }), _jsxs("span", { className: css.copy, children: [_jsx("strong", { children: taskNumber === undefined ? t('task') : `Task #${taskNumber}` }), _jsxs("small", { children: [channel === undefined ? t('channelUnknown') : `# ${channel.name}`, " \u00B7 ", formatTaskStatus(item.task.status, t)] })] }), _jsxs("span", { className: css.count, "aria-label": `${t('inboxUnreadCount', { count: item.unreadCount })}${item.directCount > 0 ? `, ${t('inboxDirectCount', { count: item.directCount })}` : ''}`, children: [t('inboxUnreadCount', { count: item.unreadCount }), item.directCount > 0 && _jsxs(_Fragment, { children: [" \u00B7 ", t('inboxDirectCount', { count: item.directCount })] })] })] }, item.thread.threadRef);
|
|
89
|
-
})] })] });
|
|
90
|
-
}
|