@wowyuarm/dsh-agent-team 0.1.1 → 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.
Files changed (92) hide show
  1. package/README.md +1 -0
  2. package/README.zh.md +2 -0
  3. package/package.json +42 -25
  4. package/packages/agent-team/README.md +1 -1
  5. package/packages/agent-team/README.zh.md +1 -1
  6. package/packages/agent-team/lib/attachments.js +11 -12
  7. package/packages/agent-team/lib/auto-compaction.js +163 -0
  8. package/packages/agent-team/lib/index.js +262 -189
  9. package/packages/agent-team/lib/ledger.js +140 -109
  10. package/packages/agent-team/lib/recovery.js +30 -35
  11. package/packages/agent-team/lib/spec.js +10 -4
  12. package/packages/agent-team/lib/typert.host.js +68 -71
  13. package/packages/agent-team/lib/typert.remote-client.d.ts +3 -3
  14. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  15. package/packages/agent-team/lib/typert.remote-client.js +41 -56
  16. package/packages/agent-team/lib/types/attachments.d.ts +2 -2
  17. package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
  18. package/packages/agent-team/lib/types/auto-compaction.d.ts +38 -0
  19. package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -0
  20. package/packages/agent-team/lib/types/index.d.ts +49 -24
  21. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  22. package/packages/agent-team/lib/types/ledger.d.ts +24 -11
  23. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  24. package/packages/agent-team/lib/types/recovery.d.ts +15 -16
  25. package/packages/agent-team/lib/types/recovery.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  27. package/packages/agent-team/lib/types/types.d.ts +20 -12
  28. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  29. package/packages/agent-team/preset/team-member/agent.cordis.yml +8 -3
  30. package/packages/client-agent-team/README.md +3 -2
  31. package/packages/client-agent-team/README.zh.md +3 -2
  32. package/packages/client-agent-team/lib/client.js +1507 -525
  33. package/packages/client-agent-team/lib/client.js.map +1 -1
  34. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +1 -2
  35. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  36. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +24 -182
  37. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
  38. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  39. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +27 -5
  40. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  41. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +19 -4
  42. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  43. package/packages/client-agent-team/lib/types/client/TeamComposer.js +22 -2
  44. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  46. package/packages/client-agent-team/lib/types/client/TeamConversation.js +3 -3
  47. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +34 -0
  48. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -0
  49. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +169 -0
  50. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +17 -0
  51. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -0
  52. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +120 -0
  53. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +7 -2
  54. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamMessage.js +170 -18
  56. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +4 -0
  57. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +70 -13
  59. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  60. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
  62. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  63. package/packages/client-agent-team/lib/types/client/index.js +72 -2
  64. package/packages/client-agent-team/lib/types/client/locales.d.ts +8 -2
  65. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/locales.js +8 -2
  67. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +22 -0
  68. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -0
  69. package/packages/client-agent-team/lib/types/client/member-session-input.js +98 -0
  70. package/packages/client-agent-team/lib/types/client/requests.d.ts +16 -0
  71. package/packages/client-agent-team/lib/types/client/requests.d.ts.map +1 -0
  72. package/packages/client-agent-team/lib/types/client/requests.js +22 -0
  73. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +35 -0
  74. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -0
  75. package/packages/client-agent-team/lib/types/client/sidebar-drag.js +70 -0
  76. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +27 -0
  77. package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -0
  78. package/packages/client-agent-team/lib/types/client/sidebar-order.js +170 -0
  79. package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -2
  80. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +25 -0
  82. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -0
  83. package/packages/client-agent-team/lib/types/client/task-refs.js +77 -0
  84. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +54 -1
  85. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  86. package/packages/client-agent-team/lib/types/client/team-formatters.js +83 -2
  87. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
  88. package/packages/client-agent-team/lib/types/client/team-membership.js +2 -1
  89. package/packages/tool-agent-team/lib/index.js +4 -4
  90. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +0 -13
  91. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +0 -1
  92. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.js +0 -90
@@ -1,13 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Fragment, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
3
- import { Button, DisclosureRow, IconChevronLeftOutline14, IconChecklistOutline14, Pill } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, DisclosureRow, IconChevronLeftOutline14, IconChecklistOutline14, Modal, Pill } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamComposer } from "./TeamComposer.js";
5
5
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
6
6
  import { TeamMessage } from "./TeamMessage.js";
7
7
  import { TeamRunDivider } from "./TeamRunDivider.js";
8
8
  import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf } from "./team-formatters.js";
9
+ import { mintRequestId } from "./requests.js";
9
10
  import { daySeparatorLabel, isRunGap, timelineDayKey } from "./team-separators.js";
10
11
  import { useTimelineScroll } from "./timeline-scroll.js";
12
+ import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
13
+ import { bytesToBase64 } from "./attachment-preview.js";
11
14
  import css from './conversation.module.css';
12
15
  import threadCss from './thread.module.css';
13
16
  function factKey(fact) {
@@ -30,7 +33,7 @@ function readMeta(facts) {
30
33
  return new Map(facts.map(fact => [factKey(fact.fact), fact]));
31
34
  }
32
35
  export function TeamThreadPage(props) {
33
- const { workspaceId, channelRef, taskRef, threadRef, taskNumber, backToWorkspace, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, drafts, getAttachment, reply, changeTask, t, } = props;
36
+ const { workspaceId, channelRef, taskRef, threadRef, taskNumber, backToWorkspace, selectChannel, selectThread, resolveTaskRefs, putAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, drafts, getAttachment, reply, changeTask, t, } = props;
34
37
  const [projection, setProjection] = useState();
35
38
  const [channelView, setChannelView] = useState();
36
39
  const [members, setMembers] = useState([]);
@@ -46,6 +49,13 @@ export function TeamThreadPage(props) {
46
49
  const draftKey = `thread:${threadRef}`;
47
50
  const { draft, recipients } = useSyncExternalStore(drafts.subscribe, () => drafts.getSnapshot(draftKey));
48
51
  const [claimsOpen, setClaimsOpen] = useState(false);
52
+ // Early acceptance: the Human may accept while Claims are still open; the
53
+ // confirm dialog lists exactly what will be completed with the Task.
54
+ const [confirmingAccept, setConfirmingAccept] = useState(false);
55
+ useEffect(() => {
56
+ if (confirmingAccept && projection?.task.resolution === 'accepted')
57
+ setConfirmingAccept(false);
58
+ }, [confirmingAccept, projection?.task.resolution]);
49
59
  const [replyRequestId, setReplyRequestId] = useState();
50
60
  const [confirmation, setConfirmation] = useState();
51
61
  const [statusMessage, setStatusMessage] = useState();
@@ -55,7 +65,7 @@ export function TeamThreadPage(props) {
55
65
  const mountedRef = useRef(false);
56
66
  const currentFactsRef = useRef([]);
57
67
  const sequenceRef = useRef(0);
58
- const readRequestIdRef = useRef(crypto.randomUUID());
68
+ const readRequestIdRef = useRef(mintRequestId());
59
69
  const mutationRequests = useRef(new Map());
60
70
  const threadLastFact = currentFacts[currentFacts.length - 1];
61
71
  const timeline = useTimelineScroll(`${currentFacts.length}:${olderFacts.length}:${threadLastFact === undefined ? '' : factKey(threadLastFact)}`);
@@ -76,7 +86,7 @@ export function TeamThreadPage(props) {
76
86
  if (!mountedRef.current)
77
87
  return false;
78
88
  if (newRequest)
79
- readRequestIdRef.current = crypto.randomUUID();
89
+ readRequestIdRef.current = mintRequestId();
80
90
  const sequence = sequenceRef.current + 1;
81
91
  sequenceRef.current = sequence;
82
92
  setLoading(true);
@@ -284,7 +294,7 @@ export function TeamThreadPage(props) {
284
294
  const mentionHandlesMap = useMemo(() => new Map(members.map(status => [status.member.memberId, status.member.handle.replace(/^@/, '')])), [members]);
285
295
  const metadata = useMemo(() => readMeta(readFacts), [readFacts]);
286
296
  const unreadIndex = useMemo(() => {
287
- const all = mergeFacts([messageFact(activeProjection?.anchor ?? { messageRef: 'missing', sequence: 0 }, activeProjection?.anchorMentions), ...readFacts.map(fact => fact.fact), ...currentFacts]);
297
+ const all = mergeFacts(...(activeProjection === undefined ? [] : [[messageFact(activeProjection.anchor, activeProjection.anchorMentions)]]), readFacts.map(fact => fact.fact), currentFacts);
288
298
  return all.findIndex(fact => metadata.get(factKey(fact))?.unread === true);
289
299
  }, [activeProjection?.anchor, readFacts, currentFacts, metadata]);
290
300
  const memberName = (memberId) => {
@@ -294,11 +304,24 @@ export function TeamThreadPage(props) {
294
304
  return status === undefined ? t('memberUnknown') : `@${status.member.handle}`;
295
305
  };
296
306
  const messageSender = (fact) => fact.kind === 'message' ? fact.message.sender : undefined;
307
+ const [pendingFiles, setPendingFiles] = useState([]);
308
+ // Branded-ref navigation for message bodies: channel refs hop to the
309
+ // Channel; the open Task's own refs are already on screen, and other Tasks
310
+ // are not resolvable from this surface, so they degrade to a no-op.
311
+ const openRef = (ref) => {
312
+ if (ref.startsWith('channel:') && ref !== channelRef)
313
+ selectChannel(ref);
314
+ if (ref.startsWith('task:') && ref !== taskRef) {
315
+ // Another Task cited here: resolve its home Channel and jump.
316
+ jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
317
+ }
318
+ };
319
+ const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
297
320
  const renderFact = (fact, grouped = false) => {
298
321
  if (fact.kind === 'message') {
299
322
  const sender = memberName(fact.message.sender);
300
323
  const senderStatus = members.find(candidate => candidate.member.memberId === fact.message.sender);
301
- return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames: mentionNamesOf(fact.mentions, mentionHandlesMap), grouped: grouped, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }) }, factKey(fact));
324
+ return _jsx(TeamMessage, { senderName: sender, memberId: fact.message.sender, human: fact.message.sender === channelView?.humanMemberId, body: fact.message.body, attachments: fact.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: fact.message.occurredAt, mentionNames: mentionNamesOf(fact.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: grouped, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }) }, factKey(fact));
302
325
  }
303
326
  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));
304
327
  };
@@ -354,7 +377,7 @@ export function TeamThreadPage(props) {
354
377
  setPending(true);
355
378
  setError(undefined);
356
379
  const key = `task:${action}`;
357
- const requestId = mutationRequests.current.get(key) ?? crypto.randomUUID();
380
+ const requestId = mutationRequests.current.get(key) ?? mintRequestId();
358
381
  mutationRequests.current.set(key, requestId);
359
382
  try {
360
383
  const result = await changeTask({ requestId, workspaceId, taskRef: task.taskRef, action, baseRevision: thread.revision });
@@ -395,12 +418,29 @@ export function TeamThreadPage(props) {
395
418
  const sendReply = async () => {
396
419
  if (pending || task === undefined || thread === undefined || draft.trim() === '')
397
420
  return;
398
- const id = replyRequestId ?? crypto.randomUUID();
421
+ const id = replyRequestId ?? mintRequestId();
399
422
  setReplyRequestId(id);
400
423
  setPending(true);
401
424
  setError(undefined);
402
425
  try {
403
- const result = await reply({ requestId: id, workspaceId, taskRef: task.taskRef, body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
426
+ // Upload chosen files first; any failure aborts the reply with the
427
+ // existing error surface and keeps the chips for a retry.
428
+ const attachmentIds = [];
429
+ for (const file of pendingFiles) {
430
+ const uploaded = await putAttachment({
431
+ requestId: mintRequestId(),
432
+ workspaceId,
433
+ name: file.name,
434
+ mediaType: file.type === '' ? undefined : file.type,
435
+ bytesBase64: bytesToBase64(new Uint8Array(await file.arrayBuffer())),
436
+ });
437
+ if (!uploaded.ok) {
438
+ setError(uploaded.error.message);
439
+ return;
440
+ }
441
+ attachmentIds.push(uploaded.value.attachmentId);
442
+ }
443
+ const result = await reply({ requestId: id, workspaceId, taskRef: task.taskRef, body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
404
444
  if (!result.ok) {
405
445
  setError(result.error.message);
406
446
  return;
@@ -414,6 +454,7 @@ export function TeamThreadPage(props) {
414
454
  });
415
455
  setProjection(current => current === undefined ? current : { ...current, task: committed.task, thread: committed.thread });
416
456
  drafts.clear(draftKey);
457
+ setPendingFiles([]);
417
458
  setReplyRequestId(undefined);
418
459
  setConfirmation(undefined);
419
460
  setStatusMessage(undefined);
@@ -460,12 +501,28 @@ export function TeamThreadPage(props) {
460
501
  // Threads are always entered through a Channel page, so a Channel origin
461
502
  // returns to its timeline; a Thread restored without one returns further.
462
503
  const backLabel = channelRef === undefined ? t('backToWorkspace') : t('backToChannel');
463
- return _jsxs("main", { className: css.surface, "data-team-thread": threadRef, children: [_jsxs("div", { className: css.surfaceHeader, children: [_jsx("div", { className: css.backRow, children: _jsx(Button, { size: "sm", icon: _jsx(IconChevronLeftOutline14, {}), onClick: backToWorkspace, children: backLabel }) }), _jsxs("header", { className: css.headerRow, children: [_jsxs("div", { className: css.headerCopy, children: [_jsxs("div", { className: threadCss.titleLine, children: [_jsx("h1", { children: task === undefined ? t('taskPending') : t('taskLabel', { number: taskNumber ?? '…' }) }), task !== undefined && _jsx(Pill, { children: formatTaskStatus(task.status, t) })] }), taskTitle !== undefined && taskTitle !== '' && _jsx("p", { className: threadCss.taskTitle, children: taskTitle })] }), task !== undefined && thread !== undefined && task.resolution !== 'closed' && _jsxs("div", { className: css.headerActions, children: [task.status === 'in_review' && task.resolution === 'open' && _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('accept'); }, children: t('acceptTask') }), task.resolution === 'open'
504
+ return _jsxs("main", { className: css.surface, "data-team-thread": threadRef, children: [_jsxs("div", { className: css.surfaceHeader, children: [_jsx("div", { className: css.backRow, children: _jsx(Button, { size: "sm", icon: _jsx(IconChevronLeftOutline14, {}), onClick: backToWorkspace, children: backLabel }) }), _jsxs("header", { className: css.headerRow, children: [_jsxs("div", { className: css.headerCopy, children: [_jsxs("div", { className: threadCss.titleLine, children: [_jsx("h1", { children: task === undefined ? t('taskPending') : t('taskLabel', { number: taskNumber ?? '…' }) }), task !== undefined && _jsx(Pill, { children: formatTaskStatus(task.status, t) })] }), taskTitle !== undefined && taskTitle !== '' && _jsx("p", { className: threadCss.taskTitle, children: taskTitle })] }), task !== undefined && thread !== undefined && task.resolution !== 'closed' && _jsxs("div", { className: css.headerActions, children: [(() => {
505
+ const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
506
+ const earlyAccept = task.resolution === 'open' && task.status === 'in_progress' && activeClaims.length > 0;
507
+ if (!(task.status === 'in_review' || earlyAccept) || task.resolution !== 'open')
508
+ return null;
509
+ return _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { if (earlyAccept) {
510
+ setConfirmingAccept(true);
511
+ }
512
+ else {
513
+ void mutateTask('accept');
514
+ } }, children: t('acceptTask') });
515
+ })(), task.resolution === 'open'
464
516
  ? _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('close'); }, children: t('closeTask') })
465
- : _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: status.diagnostic ?? t('statusError') }), " \u00B7 ", claim.direction] })] }, claim.claimRef))] }), task !== undefined && thread !== undefined && _jsx("section", { className: threadCss.workSection, "aria-label": t('claims'), children: _jsx(DisclosureRow, { expandOnRowClick: true, expandable: true, open: claimsOpen, onToggle: () => { setClaimsOpen(current => !current); }, icon: _jsx(IconChecklistOutline14, { size: 14 }), title: `${t('claims')} · ${taskClaims.length}`, children: _jsxs("div", { className: threadCss.claimList, children: [taskClaims.length === 0 && _jsx("p", { className: threadCss.emptyClaims, children: t('noClaims') }), taskClaims.map(claim => {
517
+ : _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] }), risks.length > 0 && _jsxs("section", { className: threadCss.riskSection, "aria-label": t('runtimeRisk'), children: [_jsx("h2", { children: t('runtimeRisk') }), risks.map(({ claim, status }) => _jsxs("p", { className: threadCss.riskRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { children: [t('runtimeRiskDetail', { member: status.member.handle, diagnostic: status.diagnostic ?? t('statusError') }), " \u00B7 ", claim.direction] })] }, claim.claimRef))] }), task !== undefined && thread !== undefined && (() => {
518
+ // Recomputed here so the confirm list never shows stale rows.
519
+ const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
520
+ return _jsxs(Modal, { open: confirmingAccept, onClose: () => { if (!pending)
521
+ setConfirmingAccept(false); }, title: t('acceptEarlyTitle'), closeLabel: t('cancel'), footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: pending, onClick: () => { setConfirmingAccept(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: pending, onClick: () => { void mutateTask('accept'); }, children: pending ? t('acceptingTask') : t('acceptTask') })] }), children: [_jsx("p", { className: css.confirmBody, children: t('acceptEarlyBody', { count: activeClaims.length }) }), _jsx("ul", { className: css.confirmList, children: activeClaims.map(claim => (_jsxs("li", { children: [memberName(claim.owner), " \u00B7 ", claim.direction] }, claim.claimRef))) })] });
522
+ })(), task !== undefined && thread !== undefined && _jsx("section", { className: threadCss.workSection, "aria-label": t('claims'), children: _jsx(DisclosureRow, { expandOnRowClick: true, expandable: true, open: claimsOpen, onToggle: () => { setClaimsOpen(current => !current); }, icon: _jsx(IconChecklistOutline14, { size: 14 }), title: `${t('claims')} · ${taskClaims.length}`, children: _jsxs("div", { className: threadCss.claimList, children: [taskClaims.length === 0 && _jsx("p", { className: threadCss.emptyClaims, children: t('noClaims') }), taskClaims.map(claim => {
466
523
  const ownerStatus = members.find(status => status.member.memberId === claim.owner);
467
524
  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);
468
525
  })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: timeline.onScroll, 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) }), newFactsCount > 0 && _jsxs("div", { className: threadCss.newUpdates, role: "status", children: [_jsx("span", { children: t('readNewUpdates', { count: newFactsCount }) }), _jsx(Button, { size: "sm", variant: "outline", disabled: loading, onClick: () => { timeline.jumpToLatest(); void readCurrent(true); }, children: t('markRead') })] }), remainingUnreadCount > 0 && _jsxs("div", { className: css.timelineAction, role: "status", children: [_jsx("span", { children: t('remainingUnread', { count: remainingUnreadCount }) }), _jsx(Button, { size: "sm", onClick: () => { timeline.jumpToBoundary(); void readCurrent(true); }, disabled: loading, children: t('continueReading') })] })] }) }), projection !== undefined && task !== undefined && thread !== undefined ? (task.resolution === 'closed'
469
526
  ? _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') })] })] })
470
- : _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, t: t }, draftKey)) : _jsx("div", {})] });
527
+ : _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
471
528
  }
@@ -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, updateMember, recoverMember, restartMember, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
2
+ export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, 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;AAQlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA4D9U"}
1
+ {"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAQlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA4D/T"}
@@ -5,7 +5,7 @@ import { TeamWorkspaceRow } from "./TeamWorkspaceRow.js";
5
5
  import { TeamAgentsPanel } from "./TeamAgentsPanel.js";
6
6
  import { TeamChannelsPanel } from "./TeamChannelsPanel.js";
7
7
  import css from './sidebar.module.css';
8
- export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, restartMember, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
8
+ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
9
9
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
10
10
  const workspaces = useWorkspaces(state => state.items);
11
11
  const selected = navigationState.workspaceId;
@@ -36,5 +36,5 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
36
36
  if (!wide) {
37
37
  return _jsxs("nav", { className: css.railWorkspace, "aria-label": t('workspaceSections'), children: [_jsx(Tooltip, { label: t('channels'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('channels'), onClick: () => { setPendingSection('channels'); expandSidebar(); }, children: _jsx(IconListPenOutline16, { size: 16 }) }) }), _jsx(Tooltip, { label: t('agents'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('agents'), onClick: () => { setPendingSection('agents'); expandSidebar(); }, children: _jsx(IconAgentPresetOutline16, { size: 16 }) }) })] });
38
38
  }
39
- return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx("div", { className: css.workspaceHeader, children: t('workspaces') }), _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, loadChannels: loadChannels, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, restartMember: restartMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
39
+ return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx("div", { className: css.workspaceHeader, children: t('workspaces') }), _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, loadChannels: loadChannels, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
40
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AA8BtF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAK5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAoJD,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AA+BtF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAO5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAuND,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
@@ -6,11 +6,13 @@ import { TeamFooterAction } from "./TeamFooterAction.js";
6
6
  import { TeamSettings } from "./TeamSettings.js";
7
7
  import { TeamConversation } from "./TeamConversation.js";
8
8
  import { TeamWorkspaceBrowser } from "./TeamWorkspaceBrowser.js";
9
+ import { TeamMemberSessionComposer } from "./TeamMemberSessionComposer.js";
10
+ import { admitTeamCompact, createTeamMemberSessionSources } from "./member-session-input.js";
9
11
  import { en, zh } from "./locales.js";
10
12
  export { TeamNavigation } from "./navigation.js";
11
13
  const NS = 'team';
12
14
  export const inject = [
13
- 'slots', 'workspaces', 'locale', 'remote', 'sessions', 'connection',
15
+ 'slots', 'workspaces', 'locale', 'remote', 'sessions', 'connection', 'conversation', 'inputTriggers',
14
16
  ];
15
17
  function registerModeShadow(ctx, navigation, changes, drafts, name, component, extraInject) {
16
18
  // Remote bindings shared by every Team slot; surface-specific entries extend it below.
@@ -24,7 +26,6 @@ function registerModeShadow(ctx, navigation, changes, drafts, name, component, e
24
26
  updateChannel: (request) => ctx.remote.agentTeam.updateChannel(request),
25
27
  updateMember: (request) => ctx.remote.agentTeam.updateMember(request),
26
28
  recoverMember: (request) => ctx.remote.agentTeam.recoverMember(request),
27
- restartMember: (request) => ctx.remote.agentTeam.restartMember(request),
28
29
  // The Host-scoped catalog needs no live Member, so suspended ones stay editable too.
29
30
  loadModels: async () => {
30
31
  const connection = ctx.get('connection');
@@ -71,6 +72,7 @@ function registerModeShadow(ctx, navigation, changes, drafts, name, component, e
71
72
  getAttachment: (request) => ctx.remote.agentTeam.getAttachment(request),
72
73
  reply: (request) => ctx.remote.agentTeam.reply(request),
73
74
  changeTask: (request) => ctx.remote.agentTeam.changeTask(request),
75
+ resolveTaskRefs: (request) => ctx.remote.agentTeam.resolveTaskRefs(request),
74
76
  } : {}),
75
77
  ...(name === 'sidebar.workspaces' ? {
76
78
  addMember: (request) => ctx.remote.agentTeam.addMember(request),
@@ -93,6 +95,49 @@ function registerModeShadow(ctx, navigation, changes, drafts, name, component, e
93
95
  };
94
96
  });
95
97
  }
98
+ function registerMemberSessionComposer(ctx, navigation) {
99
+ ctx.slots.inject('conversation.composer.bar', () => {
100
+ let dispose;
101
+ let registeredSessionId;
102
+ const reconcile = () => {
103
+ const memberSessionId = navigation.getSnapshot().memberSessionId;
104
+ const active = navigation.getSnapshot().mode === 'team' && memberSessionId !== undefined;
105
+ if (dispose !== undefined && (!active || registeredSessionId !== memberSessionId)) {
106
+ dispose();
107
+ dispose = undefined;
108
+ registeredSessionId = undefined;
109
+ }
110
+ if (active && dispose === undefined) {
111
+ registeredSessionId = memberSessionId;
112
+ dispose = ctx.slots.register({
113
+ name: 'conversation.composer.bar',
114
+ priority: -100,
115
+ // The registration may remain live during a session transition, but
116
+ // it elects only when the shell's current Session is the embedded
117
+ // Member. This avoids taking an ordinary Session if selection races.
118
+ select: () => ctx.sessions.list.getSnapshot().current === memberSessionId ? {} : null,
119
+ inject: (sessionId) => {
120
+ const actx = ctx.sessions.scope(sessionId);
121
+ if (actx === undefined)
122
+ throw new Error(`agent-team: Member session ${String(sessionId)} has no client scope`);
123
+ const sessions = ctx.sessions;
124
+ return {
125
+ controller: ctx.get('inputTriggers').sessionOf(actx),
126
+ stop: () => { void sessions.sessionOf(actx)?.cancel(); },
127
+ submitInput: (mode) => ctx.conversation.input.for(actx).submit(mode),
128
+ };
129
+ },
130
+ }, TeamMemberSessionComposer);
131
+ }
132
+ };
133
+ const offNavigation = navigation.subscribe(reconcile);
134
+ reconcile();
135
+ return () => {
136
+ offNavigation();
137
+ dispose?.();
138
+ };
139
+ });
140
+ }
96
141
  function applyUi(ctx) {
97
142
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'agent-team: dictionaries');
98
143
  const navigation = new TeamNavigation();
@@ -106,6 +151,30 @@ function applyUi(ctx) {
106
151
  void disposeDrafts();
107
152
  }, 'agent-team: navigation service');
108
153
  const changes = new TeamChangeStream((request, signal) => ctx.remote.agentTeam.changes(request, signal));
154
+ const triggerSources = createTeamMemberSessionSources({
155
+ isEmbeddedMemberSession: sessionId => {
156
+ const snapshot = navigation.getSnapshot();
157
+ return snapshot.mode === 'team' && snapshot.memberSessionId === sessionId && snapshot.workspaceId !== undefined;
158
+ },
159
+ members: async () => {
160
+ const workspaceId = navigation.getSnapshot().workspaceId;
161
+ if (workspaceId === undefined)
162
+ return [];
163
+ const result = await ctx.remote.agentTeam.members({ workspaceId });
164
+ return result.ok ? result.value : [];
165
+ },
166
+ executeCompact: async (sessionId) => {
167
+ const actx = ctx.sessions.scope(sessionId);
168
+ const session = actx === undefined ? undefined : ctx.sessions.sessionOf(actx);
169
+ return session === undefined ? { kind: 'error', text: 'current Member Session is unavailable' } : admitTeamCompact(session);
170
+ },
171
+ });
172
+ const inputTriggers = ctx.get('inputTriggers');
173
+ ctx.effect(() => {
174
+ const disposers = triggerSources.map(source => inputTriggers.registerSource(source));
175
+ return () => { for (const dispose of disposers)
176
+ dispose(); };
177
+ }, 'agent-team: Member session trigger sources');
109
178
  const loadMemberGroups = async () => {
110
179
  const workspaces = ctx.workspaces.list.getSnapshot().items;
111
180
  const groups = await Promise.all(workspaces.map(async (workspace) => {
@@ -144,6 +213,7 @@ function applyUi(ctx) {
144
213
  }, TeamFooterAction));
145
214
  registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.workspaces', TeamWorkspaceBrowser);
146
215
  registerModeShadow(ctx, navigation, changes, drafts, 'conversation', TeamConversation);
216
+ registerMemberSessionComposer(ctx, navigation);
147
217
  registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.settings', TeamSettings, () => ({ loadMemberGroups }));
148
218
  }
149
219
  export async function apply(ctx) {
@@ -37,7 +37,6 @@ export declare const zh: {
37
37
  readonly editChannel: "编辑频道";
38
38
  readonly editAgent: "编辑 Agent";
39
39
  readonly resumeAgent: "恢复";
40
- readonly restartAgent: "重启会话";
41
40
  readonly optionalSuffix: "(可选)";
42
41
  readonly agentDescriptionPlaceholder: "留空则暂无描述";
43
42
  readonly channelsPickerEmpty: "选择初始频道";
@@ -115,6 +114,10 @@ export declare const zh: {
115
114
  readonly activityClaimReleased: "{actor} 释放了「{direction}」";
116
115
  readonly activityClaimsReleased: "{actor} 因成员权限变化释放了 {count} 个 Claim";
117
116
  readonly activityAccepted: "{actor} 验收了此 Task";
117
+ readonly activityAcceptedWithClaims: "{actor} 验收了此 Task,并一并完成了 {count} 个未完成 Claim";
118
+ readonly acceptEarlyTitle: "提前验收任务";
119
+ readonly acceptEarlyBody: "将验收本 Task,并把以下 {count} 个未完成的 Claim 一并标记为完成:";
120
+ readonly acceptingTask: "验收中…";
118
121
  readonly activityClosed: "{actor} 关闭了此 Task";
119
122
  readonly activityReopened: "{actor} 重新打开了此 Task";
120
123
  readonly mentionConfirmation: "提及未关注成员需要确认,再次发送以继续";
@@ -162,7 +165,6 @@ export declare const en: {
162
165
  readonly editChannel: "Edit Channel";
163
166
  readonly editAgent: "Edit Agent";
164
167
  readonly resumeAgent: "Resume";
165
- readonly restartAgent: "Restart session";
166
168
  readonly optionalSuffix: " (optional)";
167
169
  readonly agentDescriptionPlaceholder: "Leave empty if not needed";
168
170
  readonly channelsPickerEmpty: "Choose initial channels";
@@ -240,6 +242,10 @@ export declare const en: {
240
242
  readonly activityClaimReleased: "{actor} released “{direction}”";
241
243
  readonly activityClaimsReleased: "{actor} released {count} Claim(s) after a membership change";
242
244
  readonly activityAccepted: "{actor} accepted this Task";
245
+ readonly activityAcceptedWithClaims: "{actor} accepted this Task and completed {count} unfinished Claims with it";
246
+ readonly acceptEarlyTitle: "Accept Task early";
247
+ readonly acceptEarlyBody: "This accepts the Task and marks the following {count} unfinished Claims as done:";
248
+ readonly acceptingTask: "Accepting…";
243
249
  readonly activityClosed: "{actor} closed this Task";
244
250
  readonly activityReopened: "{actor} reopened this Task";
245
251
  readonly mentionConfirmation: "Mentioning an unfollowed member requires confirmation; send again to continue";
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4HL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4HL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+HL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+HL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
@@ -37,7 +37,6 @@ export const zh = {
37
37
  editChannel: '编辑频道',
38
38
  editAgent: '编辑 Agent',
39
39
  resumeAgent: '恢复',
40
- restartAgent: '重启会话',
41
40
  optionalSuffix: '(可选)',
42
41
  agentDescriptionPlaceholder: '留空则暂无描述',
43
42
  channelsPickerEmpty: '选择初始频道',
@@ -115,6 +114,10 @@ export const zh = {
115
114
  activityClaimReleased: '{actor} 释放了「{direction}」',
116
115
  activityClaimsReleased: '{actor} 因成员权限变化释放了 {count} 个 Claim',
117
116
  activityAccepted: '{actor} 验收了此 Task',
117
+ activityAcceptedWithClaims: '{actor} 验收了此 Task,并一并完成了 {count} 个未完成 Claim',
118
+ acceptEarlyTitle: '提前验收任务',
119
+ acceptEarlyBody: '将验收本 Task,并把以下 {count} 个未完成的 Claim 一并标记为完成:',
120
+ acceptingTask: '验收中…',
118
121
  activityClosed: '{actor} 关闭了此 Task',
119
122
  activityReopened: '{actor} 重新打开了此 Task',
120
123
  mentionConfirmation: '提及未关注成员需要确认,再次发送以继续',
@@ -162,7 +165,6 @@ export const en = {
162
165
  editChannel: 'Edit Channel',
163
166
  editAgent: 'Edit Agent',
164
167
  resumeAgent: 'Resume',
165
- restartAgent: 'Restart session',
166
168
  optionalSuffix: ' (optional)',
167
169
  agentDescriptionPlaceholder: 'Leave empty if not needed',
168
170
  channelsPickerEmpty: 'Choose initial channels',
@@ -240,6 +242,10 @@ export const en = {
240
242
  activityClaimReleased: '{actor} released “{direction}”',
241
243
  activityClaimsReleased: '{actor} released {count} Claim(s) after a membership change',
242
244
  activityAccepted: '{actor} accepted this Task',
245
+ activityAcceptedWithClaims: '{actor} accepted this Task and completed {count} unfinished Claims with it',
246
+ acceptEarlyTitle: 'Accept Task early',
247
+ acceptEarlyBody: 'This accepts the Task and marks the following {count} unfinished Claims as done:',
248
+ acceptingTask: 'Accepting…',
243
249
  activityClosed: '{actor} closed this Task',
244
250
  activityReopened: '{actor} reopened this Task',
245
251
  mentionConfirmation: 'Mentioning an unfollowed member requires confirmation; send again to continue',
@@ -0,0 +1,22 @@
1
+ import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
2
+ import type { ISession } from '@deepseek-ai/dsh-client-runtime/client';
3
+ import type { ClientSessionContext, InputTriggerSource, SubmitOutcome } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
4
+ export declare const TEAM_COMMAND_SOURCE = "agent-team-command";
5
+ export declare const TEAM_MEMBER_SOURCE = "agent-team-member";
6
+ export interface TeamMemberSessionInputOptions {
7
+ readonly isEmbeddedMemberSession: (sessionId: ClientSessionContext['sessionId']) => boolean;
8
+ readonly members: () => Promise<readonly AgentTeamClientMemberStatus[]>;
9
+ readonly executeCompact: (sessionId: ClientSessionContext['sessionId']) => Promise<SubmitOutcome>;
10
+ }
11
+ /** Preserve the public Session command admission contract, not handler outcome. */
12
+ export declare function admitTeamCompact(session: Pick<ISession, 'command'>): Promise<SubmitOutcome>;
13
+ /**
14
+ * The two Team-only sources live in the shared public registry, but their
15
+ * candidate plane is inert for every ordinary Session. The member source owns
16
+ * its stable ref→label cache so serialization never falls back to guessing a
17
+ * handle from plain text.
18
+ */
19
+ export declare function createTeamMemberSessionSources(options: TeamMemberSessionInputOptions): readonly InputTriggerSource[];
20
+ /** Unknown leading slash must never fall into the shipped global palette. */
21
+ export declare function allowsMemberSessionSubmit(draft: string, claimToken: string | undefined): boolean;
22
+ //# sourceMappingURL=member-session-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"member-session-input.d.ts","sourceRoot":"","sources":["../../../src/client/member-session-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAqB,MAAM,gCAAgC,CAAA;AACpG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAA;AACtE,OAAO,KAAK,EACV,oBAAoB,EAAgB,kBAAkB,EAAmB,aAAa,EACvF,MAAM,iDAAiD,CAAA;AAExD,eAAO,MAAM,mBAAmB,uBAAuB,CAAA;AACvD,eAAO,MAAM,kBAAkB,sBAAsB,CAAA;AAErD,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,uBAAuB,EAAE,CAAC,SAAS,EAAE,oBAAoB,CAAC,WAAW,CAAC,KAAK,OAAO,CAAA;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,SAAS,2BAA2B,EAAE,CAAC,CAAA;IACvE,QAAQ,CAAC,cAAc,EAAE,CAAC,SAAS,EAAE,oBAAoB,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;CAClG;AAED,mFAAmF;AACnF,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAIjG;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,6BAA6B,GAAG,SAAS,kBAAkB,EAAE,CAiEpH;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGhG"}
@@ -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('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
95
+ }
96
+ function escapeAttribute(value) {
97
+ return escapeText(value).replaceAll('"', '&quot;').replaceAll("'", '&#39;');
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
+ };