dsh-agent-crew-handoff-ui 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tushar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/experimental/crew-handoff-ui/README.md
5
+ /:
6
+ en: 71ebec9903cd789c
7
+ zh: 23b9c2f296b21aa3
8
+ /deepseek-ai-dsh-crew-handoff-ui:
9
+ en: 17d7e994d0d66e07
10
+ zh: 5c701b160b2f2f01
11
+ /deepseek-ai-dsh-crew-handoff-ui/summary:
12
+ en: 860a27dcdde71b12
13
+ zh: c172bf5ee864f658
14
+ /deepseek-ai-dsh-crew-handoff-ui/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: b00dddf4c88f0a45
17
+ /deepseek-ai-dsh-crew-handoff-ui/use-this-package:
18
+ en: a3b690da06f69ddb
19
+ zh: 653b52eb0002219c
20
+ /deepseek-ai-dsh-crew-handoff-ui/understand-the-implementation:
21
+ en: 09a93104ea754441
22
+ zh: bdea19ac1f46ee97
23
+ /deepseek-ai-dsh-crew-handoff-ui/further-exploration:
24
+ en: 768ecd5a100dfffb
25
+ zh: 838d273c2220b278
26
+ /deepseek-ai-dsh-crew-handoff-ui/model-experience:
27
+ en: 42ee187cf83f453a
28
+ zh: c92aa8c2e5489b54
29
+ /deepseek-ai-dsh-crew-handoff-ui/model-experience/kv-cache-effect:
30
+ en: ca75c51c89c2c9b0
31
+ zh: 477b768cc9dcda1a
32
+ /deepseek-ai-dsh-crew-handoff-ui/known-limitations-and-deferred-work:
33
+ en: f259831648ca9f9c
34
+ zh: 1a521734c3f706c1
35
+ /deepseek-ai-dsh-crew-handoff-ui/known-limitations-and-deferred-work/dev-note:
36
+ en: e54c807bb0679a66
37
+ zh: e3bdb70977c87bf4
package/README.md CHANGED
@@ -1,3 +1,84 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Handoffs tab: agent cards with live state and lanes of the tasks a Session handed to each subagent, shared with other crew pages."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # dsh-agent-crew-handoff-ui
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ When a Session delegates, the work moves out of sight. This client plugin adds a **Handoffs** tab to the right panel: one card for the Session and one for each subagent it created, with the crew member's face and name, its preset, whether it is working, waiting on you or idle, and the task it is on. Below the cards, one lane per agent lists every task handed to it, newest first, with the short task label, the first line of the brief, when it was handed off and through which tool. Other crew pages reuse the view through `ctx.crewHandoffs`.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ The [crew-agents](../crew-agents/README.md) bundle mounts this plugin. Open **Handoffs** from the right panel's guide in any Session; a card opens that agent's Session. The newest item in a lane reads **With** the agent while it works or waits on you, and **Handed back** once it is idle.
29
+
30
+ -----
31
+
32
+ <a id="understand-the-implementation"></a>
33
+ ## Understand the implementation
34
+
35
+ <details>
36
+ <summary>Implementation internals — click to expand</summary>
37
+
38
+ A handoff is a successful tool result whose presentation metadata is `{ kind: 'activation', name }`, which every subagent delegation tool returns; its `tool/call` gives the `description`, `prompt` and `agent_preset` arguments. The tab reads the Session's loaded event window, its `subagentCatalog` projection for the children, and the Session list and status for each card's preset and state.
39
+
40
+ | Source | Contents |
41
+ |---|---|
42
+ | [`src/client/handoffs.ts`](src/client/handoffs.ts) | Reading handoffs from events |
43
+ | [`src/client/view.ts`](src/client/view.ts) | Cards and lanes |
44
+ | [`src/client/HandoffView.tsx`](src/client/HandoffView.tsx) | View |
45
+ | [`src/client/HandoffTab.tsx`](src/client/HandoffTab.tsx) | Tab body |
46
+ | [`src/client/index.ts`](src/client/index.ts) | Registrations and `ctx.crewHandoffs` |
47
+ | [`tests/`](tests/) | Reading, building and drawing |
48
+
49
+ </details>
50
+
51
+ -----
52
+
53
+ <a id="further-exploration"></a>
54
+ ## Further Exploration
55
+
56
+ - [tool-subagent](../../subagent/tool-subagent/README.md) — the delegation tools.
57
+ - [ui-subagent](../../client/ui-subagent/README.md) — the subagent tree in the Session header.
58
+
59
+ <a id="model-experience"></a>
60
+ ## Model Experience
61
+
62
+ None, as the package is a browser view that registers nothing model-facing.
63
+
64
+ #### KV Cache effect
65
+
66
+ None; this package neither assembles nor sends a provider request.
67
+
68
+ ## Known Limitations and Deferred Work
69
+
70
+ <a id="known-limitations-and-deferred-work"></a>
71
+
72
+ - Cost per agent is not shown; dsh records token usage, not prices.
73
+ - Only handoffs in the loaded part of the Session log appear.
74
+ - Handoffs between subagents appear in the delegating subagent's own Session, not the lead's.
75
+
76
+ <a id="dev-note"></a>
77
+ ### Dev Note
78
+
79
+ <details>
80
+ <summary>Working context for maintainers — click to expand</summary>
81
+
82
+ Tests read synthetic events with the real event shapes and render the view from a built model.
83
+
84
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,84 @@
1
+ ---
2
+ description: "交接标签页:显示智能体卡片的实时状态,以及 Session 交给每个子智能体的任务列表,并与其他 crew 页面共享。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # dsh-agent-crew-handoff-ui
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ Session 委派工作后,工作就离开了视线。本客户端插件在右侧面板添加 **交接** 标签页:Session 本身和它创建的每个子智能体各一张卡片,显示 crew 成员的头像和名称、预设、是在工作、等待你还是空闲,以及当前任务。卡片下方每个智能体一列,按从新到旧列出交给它的每项任务,包括简短的任务标签、简要说明的第一行、交接时间和所用工具。其他 crew 页面通过 `ctx.crewHandoffs` 复用该视图。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [实现原理](#understand-the-implementation)
18
+ - [延伸阅读](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与后续工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ [crew-agents](../crew-agents/README.zh.md) bundle 挂载本插件。在任意 Session 中从右侧面板的指南打开 **交接**;点击卡片会打开该智能体的 Session。列中最新的一项在智能体工作或等待你时显示 **在…处**,空闲后显示 **已交回**。
29
+
30
+ -----
31
+
32
+ <a id="understand-the-implementation"></a>
33
+ ## 实现原理
34
+
35
+ <details>
36
+ <summary>实现细节——点击展开</summary>
37
+
38
+ 交接是指展示元数据为 `{ kind: 'activation', name }` 的成功工具结果,每个子智能体委派工具都会返回它;对应的 `tool/call` 提供 `description`、`prompt` 和 `agent_preset` 参数。标签页读取 Session 已加载的事件窗口、用于子智能体的 `subagentCatalog` 投影,以及用于每张卡片预设和状态的 Session 列表与状态。
39
+
40
+ | 源码 | 内容 |
41
+ |---|---|
42
+ | [`src/client/handoffs.ts`](src/client/handoffs.ts) | 从事件读取交接 |
43
+ | [`src/client/view.ts`](src/client/view.ts) | 卡片与列 |
44
+ | [`src/client/HandoffView.tsx`](src/client/HandoffView.tsx) | 视图 |
45
+ | [`src/client/HandoffTab.tsx`](src/client/HandoffTab.tsx) | 标签页主体 |
46
+ | [`src/client/index.ts`](src/client/index.ts) | 注册与 `ctx.crewHandoffs` |
47
+ | [`tests/`](tests/) | 读取、构建与绘制 |
48
+
49
+ </details>
50
+
51
+ -----
52
+
53
+ <a id="further-exploration"></a>
54
+ ## 延伸阅读
55
+
56
+ - [tool-subagent](../../subagent/tool-subagent/README.zh.md)——委派工具。
57
+ - [ui-subagent](../../client/ui-subagent/README.zh.md)——Session 标题中的子智能体树。
58
+
59
+ <a id="model-experience"></a>
60
+ ## 模型体验
61
+
62
+ 无,本包是浏览器视图,不注册任何面向模型的内容。
63
+
64
+ #### KV Cache 影响
65
+
66
+ 无;本包既不组装也不发送模型提供方请求。
67
+
68
+ ## 已知限制与后续工作
69
+
70
+ <a id="known-limitations-and-deferred-work"></a>
71
+
72
+ - 不显示每个智能体的费用;dsh 记录的是 token 用量而不是价格。
73
+ - 只显示 Session 日志中已加载部分的交接。
74
+ - 子智能体之间的交接显示在委派方子智能体自己的 Session 中,而不是负责人的 Session 中。
75
+
76
+ <a id="dev-note"></a>
77
+ ### 开发备注
78
+
79
+ <details>
80
+ <summary>维护者工作背景——点击展开</summary>
81
+
82
+ 测试用真实事件结构读取合成事件,并用构建出的模型渲染视图。
83
+
84
+ </details>
package/lib/client.js ADDED
@@ -0,0 +1,551 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-agent-crew-handoff-ui",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
+ //#region ../../util/brand/src/index.ts
11
+ /**
12
+ * Apply a compile-time string brand without changing the value.
13
+ * @param value - string admitted by the domain that owns the target brand.
14
+ * @returns the same string with the requested compile-time brand.
15
+ */
16
+ function brandString(value) {
17
+ return value;
18
+ }
19
+ //#endregion
20
+ //#region ../../core/session/src/types.ts
21
+ /**
22
+ * Brand a string as a {@link SessionId}.
23
+ * @param id - the raw session id string.
24
+ * @returns the same string with the session-id brand.
25
+ */
26
+ function SessionId(id) {
27
+ return brandString(id);
28
+ }
29
+ //#endregion
30
+ //#region lib/types/client/handoffs.js
31
+ const MAX_BRIEF = 160;
32
+ function record(raw) {
33
+ try {
34
+ const value = JSON.parse(raw);
35
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
36
+ } catch {
37
+ return;
38
+ }
39
+ }
40
+ function text(value) {
41
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
42
+ }
43
+ /**
44
+ * Every successful subagent start in the window, oldest first. A start is a tool result whose presentation metadata is
45
+ * `{ kind: 'activation', name }`, which every subagent delegation tool returns.
46
+ * @param entries - the Session's loaded event window.
47
+ * @returns the handoffs.
48
+ */
49
+ function handoffsOf(entries) {
50
+ const calls = /* @__PURE__ */ new Map();
51
+ const handoffs = [];
52
+ for (const entry of entries) {
53
+ if (entry.type !== "event") continue;
54
+ const { event } = entry;
55
+ if (event.type === "tool/call") {
56
+ calls.set(String(event.data.callId), {
57
+ tool: event.data.name,
58
+ args: record(event.data.arguments)
59
+ });
60
+ continue;
61
+ }
62
+ if (event.type !== "tool/result" || event.data.message.isError === true) continue;
63
+ const meta = event.data.meta;
64
+ if (typeof meta !== "object" || meta === null || Array.isArray(meta)) continue;
65
+ const child = text(meta["name"]);
66
+ if (meta["kind"] !== "activation" || child === void 0) continue;
67
+ const callId = String(event.data.message.source.callId);
68
+ const call = calls.get(callId);
69
+ const args = call?.args;
70
+ const line = text(args?.["prompt"])?.split("\n")[0];
71
+ const brief = line === void 0 ? void 0 : line.length > MAX_BRIEF ? `${line.slice(0, MAX_BRIEF - 1)}…` : line;
72
+ const preset = text(args?.["agent_preset"]);
73
+ handoffs.push({
74
+ callId,
75
+ child,
76
+ task: text(args?.["description"]) ?? child,
77
+ tool: call?.tool ?? "subagent",
78
+ time: event.time,
79
+ ...brief === void 0 ? {} : { brief },
80
+ ...preset === void 0 ? {} : { preset }
81
+ });
82
+ }
83
+ return handoffs;
84
+ }
85
+ //#endregion
86
+ //#region \0dsh-css:/Users/tushar/Developer/deepseek-harness-fork/packages/experimental/crew-handoff-ui/src/client/HandoffView.module.css.mjs
87
+ const css = "[data-state]{--c:var(--dsw-alias-state-idle-primary)}[data-state=working]{--c:var(--dsw-alias-state-business-primary)}[data-state=needs-you]{--c:var(--dsw-alias-state-warn-primary)}[data-state=done]{--c:var(--dsw-alias-state-success-primary)}.dINUDq_root{background:var(--dsw-alias-bg-base);height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;padding:16px;font-size:13px;display:flex;overflow:auto}.dINUDq_head{align-items:flex-start;gap:12px;display:flex}.dINUDq_headText{flex:1;min-width:0}.dINUDq_title{letter-spacing:-.01em;margin:0;font-size:17px;font-weight:650;line-height:1.2}.dINUDq_summary{color:var(--dsw-alias-label-tertiary);margin:2px 0 0;font-size:12px}.dINUDq_pill{background:color-mix(in srgb, var(--c) 14%, transparent);color:var(--dsw-alias-label-primary);white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 8px;font-size:11px;font-weight:550;display:inline-flex}.dINUDq_pill:before{content:\"\";background:var(--c);border-radius:50%;width:6px;height:6px}.dINUDq_pill[data-state=working]:before{animation:1.6s ease-in-out infinite dINUDq_pulse}@keyframes dINUDq_pulse{50%{opacity:.35}}.dINUDq_hub,.dINUDq_main,.dINUDq_toggle{color:inherit;font:inherit;text-align:left;cursor:pointer;border:0}.dINUDq_hub{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);align-items:center;gap:12px;width:100%;padding:12px;display:flex}.dINUDq_hub:hover{background:var(--dsw-alias-interactive-bg-hover)}.dINUDq_who{flex-direction:column;flex:1;min-width:0;display:flex}.dINUDq_name{text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.dINUDq_hub .dINUDq_name{font-size:14px}.dINUDq_nameLine{align-items:baseline;gap:6px;min-width:0;display:flex}.dINUDq_preset{color:var(--dsw-alias-label-tertiary);font-family:var(--dsw-font-mono,ui-monospace, monospace);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.dINUDq_muted{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.dINUDq_stat{flex-direction:column;align-items:flex-end;display:flex}.dINUDq_stat b{font-family:var(--dsw-font-mono,ui-monospace, monospace);font-size:18px;font-weight:600;line-height:1}.dINUDq_stat span{color:var(--dsw-alias-label-tertiary);font-size:11px}.dINUDq_agents{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.dINUDq_agent{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-base);box-shadow:inset 3px 0 0 var(--c);overflow:hidden}.dINUDq_agent[data-state=idle]{box-shadow:none}.dINUDq_agent[data-state=needs-you]{border-color:var(--c)}.dINUDq_row{align-items:stretch;display:flex}.dINUDq_main{background:0 0;flex:1;align-items:center;gap:10px;min-width:0;padding:10px 8px 10px 12px;display:flex}.dINUDq_main:hover,.dINUDq_toggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.dINUDq_now{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.dINUDq_toggle{color:var(--dsw-alias-label-tertiary);background:0 0;flex:none;align-items:center;padding:0 10px;display:flex}.dINUDq_toggle>*{transition:transform .15s}.dINUDq_toggle[aria-expanded=true]>*{transform:rotate(90deg)}.dINUDq_hub:focus-visible,.dINUDq_main:focus-visible,.dINUDq_toggle:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}.dINUDq_lane{border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1)}.dINUDq_items{margin:0;padding:4px 12px 12px 22px;list-style:none}.dINUDq_item{flex-direction:column;gap:2px;padding:10px 0 2px 18px;animation:.25s ease-out dINUDq_slide;display:flex;position:relative}.dINUDq_item:before{content:\"\";background:var(--dsw-alias-border-l3);width:1.5px;position:absolute;top:0;bottom:-2px;left:3px}.dINUDq_item:last-child:before{height:16px;bottom:auto}@keyframes dINUDq_slide{0%{opacity:0;transform:translateY(-4px)}}.dINUDq_mark{border:2px solid var(--dsw-alias-bg-layer-1);background:var(--c);border-radius:50%;width:12px;height:12px;position:absolute;top:13px;left:-2px}.dINUDq_mark[data-state=working],.dINUDq_mark[data-state=needs-you]{box-shadow:0 0 0 3px color-mix(in srgb, var(--c) 22%, transparent)}.dINUDq_itemTask{overflow-wrap:anywhere;font-weight:550}.dINUDq_brief{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;display:-webkit-box;overflow:hidden}.dINUDq_itemMeta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;gap:4px 8px;margin-top:2px;font-size:11px;display:flex}.dINUDq_itemState{color:var(--dsw-alias-label-secondary);font-weight:550}.dINUDq_time{font-variant-numeric:tabular-nums}@media (prefers-reduced-motion:reduce){.dINUDq_item{animation:none}.dINUDq_pill:before{animation:none!important}.dINUDq_toggle>*{transition:none}}";
88
+ const tagId = "dsh-agent-crew-handoff-ui/HandoffView.module.css";
89
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
90
+ const tag = document.createElement("style");
91
+ tag.dataset.plugin = "dsh-agent-crew-handoff-ui";
92
+ tag.dataset.pluginCss = tagId;
93
+ tag.textContent = css;
94
+ document.head.appendChild(tag);
95
+ }
96
+ var HandoffView_module_css_default = {
97
+ "agent": "dINUDq_agent",
98
+ "agents": "dINUDq_agents",
99
+ "brief": "dINUDq_brief",
100
+ "head": "dINUDq_head",
101
+ "headText": "dINUDq_headText",
102
+ "hub": "dINUDq_hub",
103
+ "item": "dINUDq_item",
104
+ "itemMeta": "dINUDq_itemMeta",
105
+ "itemState": "dINUDq_itemState",
106
+ "itemTask": "dINUDq_itemTask",
107
+ "items": "dINUDq_items",
108
+ "lane": "dINUDq_lane",
109
+ "main": "dINUDq_main",
110
+ "mark": "dINUDq_mark",
111
+ "muted": "dINUDq_muted",
112
+ "name": "dINUDq_name",
113
+ "nameLine": "dINUDq_nameLine",
114
+ "now": "dINUDq_now",
115
+ "pill": "dINUDq_pill",
116
+ "preset": "dINUDq_preset",
117
+ "pulse": "dINUDq_pulse",
118
+ "root": "dINUDq_root",
119
+ "row": "dINUDq_row",
120
+ "slide": "dINUDq_slide",
121
+ "stat": "dINUDq_stat",
122
+ "summary": "dINUDq_summary",
123
+ "time": "dINUDq_time",
124
+ "title": "dINUDq_title",
125
+ "toggle": "dINUDq_toggle",
126
+ "who": "dINUDq_who"
127
+ };
128
+ //#endregion
129
+ //#region lib/types/client/HandoffView.js
130
+ /** The lead as a hub card, then one row per agent, most urgent first, each opening into a timeline of what it was handed. */
131
+ const URGENCY = {
132
+ "needs-you": 0,
133
+ working: 1,
134
+ idle: 2
135
+ };
136
+ function time(value) {
137
+ return new Intl.DateTimeFormat(void 0, {
138
+ hour: "numeric",
139
+ minute: "2-digit"
140
+ }).format(new Date(value));
141
+ }
142
+ function Face({ card, props, size }) {
143
+ const design = props.designOf(card);
144
+ const { Face: Drawn } = props;
145
+ return (0, react_jsx_runtime.jsx)(Drawn, {
146
+ agentId: card.preset ?? card.name,
147
+ size,
148
+ mood: card.state === "working" ? "working" : "idle",
149
+ ...design === void 0 ? {} : { design }
150
+ });
151
+ }
152
+ /**
153
+ * Render the hub, agent rows and their timelines.
154
+ * @param props - view data, faces, names, navigation and copy.
155
+ * @returns the view.
156
+ */
157
+ function HandoffView(props) {
158
+ const { data, t } = props;
159
+ const [expanded, setExpanded] = (0, react.useState)({});
160
+ const leadName = props.nameOf(data.lead);
161
+ const lanes = new Map(data.lanes.map((lane) => [lane.to.sessionId, lane.items]));
162
+ const agents = [...data.agents].sort((a, b) => URGENCY[a.state] - URGENCY[b.state]);
163
+ const needsYou = agents.filter((card) => card.state === "needs-you").length;
164
+ const working = agents.filter((card) => card.state === "working").length;
165
+ const leadState = t(`state.${data.lead.state}`);
166
+ return (0, react_jsx_runtime.jsxs)("div", {
167
+ className: HandoffView_module_css_default.root,
168
+ children: [
169
+ (0, react_jsx_runtime.jsxs)("header", {
170
+ className: HandoffView_module_css_default.head,
171
+ children: [(0, react_jsx_runtime.jsxs)("div", {
172
+ className: HandoffView_module_css_default.headText,
173
+ children: [(0, react_jsx_runtime.jsx)("h2", {
174
+ className: HandoffView_module_css_default.title,
175
+ children: t("tab.title")
176
+ }), (0, react_jsx_runtime.jsx)("p", {
177
+ className: HandoffView_module_css_default.summary,
178
+ children: t("summary", {
179
+ agents: agents.length,
180
+ handoffs: data.lead.handoffs
181
+ })
182
+ })]
183
+ }), needsYou > 0 ? (0, react_jsx_runtime.jsx)("span", {
184
+ className: HandoffView_module_css_default.pill,
185
+ "data-state": "needs-you",
186
+ children: t("summary.needsYou", { count: needsYou })
187
+ }) : working > 0 && (0, react_jsx_runtime.jsx)("span", {
188
+ className: HandoffView_module_css_default.pill,
189
+ "data-state": "working",
190
+ children: t("summary.working", { count: working })
191
+ })]
192
+ }),
193
+ (0, react_jsx_runtime.jsxs)("button", {
194
+ type: "button",
195
+ className: HandoffView_module_css_default.hub,
196
+ "data-state": data.lead.state,
197
+ onClick: () => {
198
+ props.open(data.lead.sessionId);
199
+ },
200
+ "aria-label": t("card.open", {
201
+ name: leadName,
202
+ state: leadState
203
+ }),
204
+ children: [
205
+ (0, react_jsx_runtime.jsx)(Face, {
206
+ card: data.lead,
207
+ props,
208
+ size: 36
209
+ }),
210
+ (0, react_jsx_runtime.jsxs)("span", {
211
+ className: HandoffView_module_css_default.who,
212
+ children: [(0, react_jsx_runtime.jsx)("span", {
213
+ className: HandoffView_module_css_default.name,
214
+ children: leadName
215
+ }), (0, react_jsx_runtime.jsx)("span", {
216
+ className: HandoffView_module_css_default.muted,
217
+ children: data.lead.preset ?? t("lead.role")
218
+ })]
219
+ }),
220
+ (0, react_jsx_runtime.jsxs)("span", {
221
+ className: HandoffView_module_css_default.stat,
222
+ children: [(0, react_jsx_runtime.jsx)("b", { children: data.lead.handoffs }), (0, react_jsx_runtime.jsx)("span", { children: t("lead.count") })]
223
+ })
224
+ ]
225
+ }),
226
+ agents.length === 0 && (0, react_jsx_runtime.jsx)("p", {
227
+ className: HandoffView_module_css_default.muted,
228
+ children: t("empty")
229
+ }),
230
+ (0, react_jsx_runtime.jsx)("ul", {
231
+ className: HandoffView_module_css_default.agents,
232
+ children: agents.map((card) => {
233
+ const name = props.nameOf(card);
234
+ const state = t(`state.${card.state}`);
235
+ const items = lanes.get(card.sessionId) ?? [];
236
+ const isOpen = items.length > 0 && (expanded[card.sessionId] ?? card.state !== "idle");
237
+ return (0, react_jsx_runtime.jsxs)("li", {
238
+ className: HandoffView_module_css_default.agent,
239
+ "data-state": card.state,
240
+ children: [(0, react_jsx_runtime.jsxs)("div", {
241
+ className: HandoffView_module_css_default.row,
242
+ children: [(0, react_jsx_runtime.jsxs)("button", {
243
+ type: "button",
244
+ className: HandoffView_module_css_default.main,
245
+ onClick: () => {
246
+ props.open(card.sessionId);
247
+ },
248
+ "aria-label": t("card.open", {
249
+ name,
250
+ state
251
+ }),
252
+ children: [
253
+ (0, react_jsx_runtime.jsx)(Face, {
254
+ card,
255
+ props,
256
+ size: 32
257
+ }),
258
+ (0, react_jsx_runtime.jsxs)("span", {
259
+ className: HandoffView_module_css_default.who,
260
+ children: [(0, react_jsx_runtime.jsxs)("span", {
261
+ className: HandoffView_module_css_default.nameLine,
262
+ children: [(0, react_jsx_runtime.jsx)("span", {
263
+ className: HandoffView_module_css_default.name,
264
+ children: name
265
+ }), card.preset !== void 0 && (0, react_jsx_runtime.jsx)("span", {
266
+ className: HandoffView_module_css_default.preset,
267
+ children: card.preset
268
+ })]
269
+ }), (0, react_jsx_runtime.jsxs)("span", {
270
+ className: HandoffView_module_css_default.now,
271
+ children: [card.task === void 0 ? t("card.ready") : t("card.task", { task: card.task }), card.handoffs > 0 && (0, react_jsx_runtime.jsxs)("span", {
272
+ className: HandoffView_module_css_default.muted,
273
+ children: [" · ", t("card.count", { count: card.handoffs })]
274
+ })]
275
+ })]
276
+ }),
277
+ (0, react_jsx_runtime.jsx)("span", {
278
+ className: HandoffView_module_css_default.pill,
279
+ "data-state": card.state,
280
+ children: state
281
+ })
282
+ ]
283
+ }), items.length > 0 && (0, react_jsx_runtime.jsx)("button", {
284
+ type: "button",
285
+ className: HandoffView_module_css_default.toggle,
286
+ "aria-expanded": isOpen,
287
+ "aria-label": t("lane.toggle", { name }),
288
+ onClick: () => {
289
+ setExpanded((prev) => ({
290
+ ...prev,
291
+ [card.sessionId]: !isOpen
292
+ }));
293
+ },
294
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 14 })
295
+ })]
296
+ }), isOpen && (0, react_jsx_runtime.jsx)("section", {
297
+ className: HandoffView_module_css_default.lane,
298
+ "aria-label": t("lane.label", {
299
+ from: leadName,
300
+ to: name
301
+ }),
302
+ children: (0, react_jsx_runtime.jsx)("ol", {
303
+ className: HandoffView_module_css_default.items,
304
+ children: items.map((item, index) => {
305
+ const current = index === 0 && card.state !== "idle";
306
+ return (0, react_jsx_runtime.jsxs)("li", {
307
+ className: HandoffView_module_css_default.item,
308
+ children: [
309
+ (0, react_jsx_runtime.jsx)("span", {
310
+ className: HandoffView_module_css_default.mark,
311
+ "data-state": current ? card.state : "done",
312
+ "aria-hidden": "true"
313
+ }),
314
+ (0, react_jsx_runtime.jsx)("span", {
315
+ className: HandoffView_module_css_default.itemTask,
316
+ children: item.task
317
+ }),
318
+ item.brief !== void 0 && (0, react_jsx_runtime.jsx)("span", {
319
+ className: HandoffView_module_css_default.brief,
320
+ children: item.brief
321
+ }),
322
+ (0, react_jsx_runtime.jsxs)("span", {
323
+ className: HandoffView_module_css_default.itemMeta,
324
+ children: [
325
+ (0, react_jsx_runtime.jsx)("span", {
326
+ className: HandoffView_module_css_default.itemState,
327
+ children: current ? t("item.with", { name }) : t("item.back")
328
+ }),
329
+ (0, react_jsx_runtime.jsx)("span", {
330
+ className: HandoffView_module_css_default.time,
331
+ children: t("item.time", { time: time(item.time) })
332
+ }),
333
+ (0, react_jsx_runtime.jsx)("span", { children: t("item.via", { tool: item.tool }) })
334
+ ]
335
+ })
336
+ ]
337
+ }, item.callId);
338
+ })
339
+ })
340
+ })]
341
+ }, card.sessionId);
342
+ })
343
+ })
344
+ ]
345
+ });
346
+ }
347
+ //#endregion
348
+ //#region lib/types/client/view.js
349
+ function stateOf(id, list, status) {
350
+ const live = status.get(id);
351
+ if (live?.pendingInteraction !== void 0) return "needs-you";
352
+ return (live?.running ?? list.byId[id]?.running) === true ? "working" : "idle";
353
+ }
354
+ function presetOf(id, list) {
355
+ const preset = list.byId[id]?.projectionValues?.agentPreset;
356
+ return typeof preset === "string" ? preset : void 0;
357
+ }
358
+ /**
359
+ * Build the view for one lead Session.
360
+ * @param lead - the Session whose handoffs these are.
361
+ * @param handoffs - its handoffs, oldest first.
362
+ * @param catalog - its direct children, in creation order.
363
+ * @param list - the Session list, for presets and running state.
364
+ * @param status - live Session status, for running and waiting.
365
+ * @returns cards in creation order and lanes for agents that received work.
366
+ */
367
+ function buildHandoffView(lead, handoffs, catalog, list, status) {
368
+ const leadPreset = presetOf(lead, list);
369
+ const leadCard = {
370
+ sessionId: lead,
371
+ name: "lead",
372
+ state: stateOf(lead, list, status),
373
+ handoffs: handoffs.length,
374
+ ...leadPreset === void 0 ? {} : { preset: leadPreset }
375
+ };
376
+ const agents = catalog.map((entry) => {
377
+ const name = ("name" in entry ? entry.name : void 0) ?? entry.label ?? entry.id;
378
+ const mine = handoffs.filter((handoff) => handoff.child === name);
379
+ const latest = mine.at(-1);
380
+ const preset = presetOf(entry.id, list) ?? mine.findLast((handoff) => handoff.preset !== void 0)?.preset;
381
+ return {
382
+ sessionId: entry.id,
383
+ name,
384
+ state: stateOf(entry.id, list, status),
385
+ handoffs: mine.length,
386
+ ...preset === void 0 ? {} : { preset },
387
+ ...latest === void 0 ? {} : { task: latest.task }
388
+ };
389
+ });
390
+ return {
391
+ lead: leadCard,
392
+ agents,
393
+ lanes: agents.flatMap((card) => {
394
+ const items = handoffs.filter((handoff) => handoff.child === card.name).reverse();
395
+ return items.length === 0 ? [] : [{
396
+ to: card,
397
+ items
398
+ }];
399
+ })
400
+ };
401
+ }
402
+ //#endregion
403
+ //#region lib/types/client/HandoffTab.js
404
+ /** Right-panel Handoffs tab body: the current Session's agents and handoffs, live. */
405
+ const EMPTY = {
406
+ revision: -1,
407
+ entries: []
408
+ };
409
+ /**
410
+ * Fold the Session's events and catalog into the view.
411
+ * @param props - standard Session hooks, the event window, view helpers and copy.
412
+ * @returns the view.
413
+ */
414
+ function HandoffTab({ sessionId, useProjection, useSessions, useSessionStatus, events, t, ...view }) {
415
+ const window = (0, react.useSyncExternalStore)((listener) => events?.subscribe(listener) ?? (() => {}), () => events?.getSnapshot() ?? EMPTY);
416
+ return (0, react_jsx_runtime.jsx)(HandoffView, {
417
+ data: buildHandoffView(sessionId, (0, react.useMemo)(() => handoffsOf(window.entries), [window]), useProjection("subagentCatalog") ?? [], useSessions((state) => state), useSessionStatus((state) => state)),
418
+ t,
419
+ ...view
420
+ });
421
+ }
422
+ //#endregion
423
+ //#region lib/types/client/locales.js
424
+ /** Handoff view copy. */
425
+ const en = {
426
+ "tab.title": "Handoffs",
427
+ "tab.description": "See which agents this Session handed work to, what each was asked and what they are doing",
428
+ "lead": "This Session",
429
+ "state.working": "Working",
430
+ "state.needs-you": "Needs you",
431
+ "state.idle": "Idle",
432
+ "card.open": "Open {name}: {state}",
433
+ "card.task": "On: {task}",
434
+ "card.ready": "Ready for a task",
435
+ "card.count": "{count} handoffs",
436
+ "lane.label": "Handoffs from {from} to {to}",
437
+ "lane.toggle": "Show or hide handoffs to {name}",
438
+ "summary": "{agents} agents · {handoffs} handoffs",
439
+ "summary.needsYou": "{count} need you",
440
+ "summary.working": "{count} working",
441
+ "lead.role": "Hands out the work",
442
+ "lead.count": "handed off",
443
+ "item.with": "With {name}",
444
+ "item.back": "Handed back",
445
+ "item.time": "Handed off {time}",
446
+ "item.via": "via {tool}",
447
+ "empty": "No handoffs yet. When this Session delegates work to a subagent, the agent and the task appear here."
448
+ };
449
+ /** Chinese handoff view copy. */
450
+ const zh = {
451
+ "tab.title": "交接",
452
+ "tab.description": "查看本 Session 把工作交给了哪些智能体、各自的任务以及当前状态",
453
+ "lead": "本 Session",
454
+ "state.working": "工作中",
455
+ "state.needs-you": "需要你",
456
+ "state.idle": "空闲",
457
+ "card.open": "打开{name}:{state}",
458
+ "card.task": "正在处理:{task}",
459
+ "card.ready": "等待任务",
460
+ "card.count": "{count} 次交接",
461
+ "lane.label": "从{from}交给{to}的工作",
462
+ "lane.toggle": "展开或收起交给{name}的工作",
463
+ "summary": "{agents} 个智能体 · {handoffs} 次交接",
464
+ "summary.needsYou": "{count} 个需要你",
465
+ "summary.working": "{count} 个工作中",
466
+ "lead.role": "分配工作",
467
+ "lead.count": "次交接",
468
+ "item.with": "在{name}处",
469
+ "item.back": "已交回",
470
+ "item.time": "交接于 {time}",
471
+ "item.via": "通过 {tool}",
472
+ "empty": "还没有交接。本 Session 把工作委派给子智能体后,智能体和任务会显示在这里。"
473
+ };
474
+ //#endregion
475
+ //#region lib/types/client/index.js
476
+ /** Handoffs tab implementation identity and keyed Slot dispatch key. */
477
+ const HANDOFF_TAB_ID = "dsh-agent-crew-handoff-ui";
478
+ /** Services the tab and view read. */
479
+ const inject = [
480
+ "slots",
481
+ "locale",
482
+ "sessions",
483
+ "sidebarRightTabs",
484
+ "uiWorkspace",
485
+ "crew",
486
+ "crewFaces"
487
+ ];
488
+ /**
489
+ * Register the copy, the tab type, the tab body and the shared view.
490
+ * @param ctx - Client plugin context.
491
+ */
492
+ function apply(ctx) {
493
+ ctx.effect(() => ctx.locale.register("crewHandoff", {
494
+ en,
495
+ zh
496
+ }), "crew-handoff-ui: locale");
497
+ const t = ctx.locale.bind("crewHandoff");
498
+ const { crew } = ctx;
499
+ const memberOf = (card) => crew.members.getSnapshot().find((member) => member.id === card.preset)?.id;
500
+ const helpers = {
501
+ Face: ctx.crewFaces.Face,
502
+ nameOf: (card) => {
503
+ const member = memberOf(card);
504
+ if (member !== void 0) return card.name === "lead" ? crew.name(member) : `${card.name} · ${crew.name(member)}`;
505
+ return card.name === "lead" ? t("lead") : card.name;
506
+ },
507
+ designOf: (card) => {
508
+ const member = memberOf(card);
509
+ return member === void 0 ? void 0 : crew.design(member);
510
+ },
511
+ open: (sessionId) => {
512
+ ctx.uiWorkspace.openSession(sessionId);
513
+ }
514
+ };
515
+ ctx.reflect.provide("crewHandoffs", {
516
+ View: HandoffView,
517
+ helpers
518
+ });
519
+ ctx.effect(() => ctx.sidebarRightTabs.register({
520
+ id: HANDOFF_TAB_ID,
521
+ kind: "crew-handoffs",
522
+ title: () => t("tab.title"),
523
+ guide: [{
524
+ id: "crew-handoffs",
525
+ order: 62,
526
+ title: () => t("tab.title"),
527
+ description: () => t("tab.description")
528
+ }]
529
+ }), "crew-handoff-ui: tab type");
530
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
531
+ name: "sidebar.right.pane.tab",
532
+ key: HANDOFF_TAB_ID,
533
+ locale: "crewHandoff",
534
+ inject: (key) => ({
535
+ ...helpers,
536
+ events: ctx.sessions.binding(SessionId(key))?.eventSource
537
+ })
538
+ }, HandoffTab));
539
+ }
540
+ //#endregion
541
+ exports.HANDOFF_TAB_ID = HANDOFF_TAB_ID;
542
+ exports.HandoffView = HandoffView;
543
+ exports.apply = apply;
544
+ exports.buildHandoffView = buildHandoffView;
545
+ exports.handoffsOf = handoffsOf;
546
+ exports.inject = inject;
547
+ return module.exports;
548
+ }
549
+ });
550
+
551
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ //#region lib/types/index.js
2
+ /** Browser registration is discovered through the package client declaration; the Host half is crew-handoff-ui. */
3
+ /** Register the browser-only contribution without Host state. */
4
+ function apply() {}
5
+ //#endregion
6
+ export { apply };
@@ -0,0 +1,17 @@
1
+ import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { SessionEventSource } from '@deepseek-ai/dsh-api-session-controller/client';
3
+ import { type HandoffViewProps } from './HandoffView.tsx';
4
+ /** What the tab receives from its registration. */
5
+ export interface HandoffTabInjected extends Omit<HandoffViewProps, 'data' | 't'> {
6
+ /** The Session's event window, or undefined when it is not bound in this client. */
7
+ readonly events: SessionEventSource | undefined;
8
+ }
9
+ type Props = Pick<PropsRuntime<'sidebar.right.pane.tab'>, 'sessionId' | 'useProjection' | 'useSessions' | 'useSessionStatus'> & PropsLocale<'crewHandoff'> & HandoffTabInjected;
10
+ /**
11
+ * Fold the Session's events and catalog into the view.
12
+ * @param props - standard Session hooks, the event window, view helpers and copy.
13
+ * @returns the view.
14
+ */
15
+ export declare function HandoffTab({ sessionId, useProjection, useSessions, useSessionStatus, events, t, ...view }: Props): import("react").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=HandoffTab.d.ts.map
@@ -0,0 +1,24 @@
1
+ /** The lead as a hub card, then one row per agent, most urgent first, each opening into a timeline of what it was handed. */
2
+ import { type ComponentType } from 'react';
3
+ import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
4
+ import type { CrewFaceProps } from 'dsh-agent-crew-faces/client';
5
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
6
+ import type { AgentCard, HandoffViewData } from './view.ts';
7
+ /** Props of {@link HandoffView}. */
8
+ export interface HandoffViewProps {
9
+ readonly data: HandoffViewData;
10
+ readonly Face: ComponentType<CrewFaceProps>;
11
+ /** Display name for a card: the crew member's name for a crew preset, else the agent's own name. */
12
+ readonly nameOf: (card: AgentCard) => string;
13
+ /** Designed face for a crew preset, when the card runs one. */
14
+ readonly designOf: (card: AgentCard) => CrewFaceProps['design'] | undefined;
15
+ readonly open: (sessionId: SessionId) => void;
16
+ readonly t: TranslateNS<'crewHandoff'>;
17
+ }
18
+ /**
19
+ * Render the hub, agent rows and their timelines.
20
+ * @param props - view data, faces, names, navigation and copy.
21
+ * @returns the view.
22
+ */
23
+ export declare function HandoffView(props: HandoffViewProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=HandoffView.d.ts.map
@@ -0,0 +1,25 @@
1
+ /** Read handoffs from a Session's loaded events: each subagent start the Session made, with the task it gave. */
2
+ import type { SessionEventLikeEntry } from '@deepseek-ai/dsh-api-session-controller/client';
3
+ /** One task handed from the Session to a subagent. */
4
+ export interface Handoff {
5
+ readonly callId: string;
6
+ /** Child name the activation returned; matches the parent catalog entry's `name`. */
7
+ readonly child: string;
8
+ /** Short task label the model gave, such as "Draft the reply". */
9
+ readonly task: string;
10
+ /** First line of the prompt the child received. */
11
+ readonly brief?: string;
12
+ /** Preset the call asked for, when it named one. */
13
+ readonly preset?: string;
14
+ /** Delegation tool, such as `ask_research`. */
15
+ readonly tool: string;
16
+ readonly time: number;
17
+ }
18
+ /**
19
+ * Every successful subagent start in the window, oldest first. A start is a tool result whose presentation metadata is
20
+ * `{ kind: 'activation', name }`, which every subagent delegation tool returns.
21
+ * @param entries - the Session's loaded event window.
22
+ * @returns the handoffs.
23
+ */
24
+ export declare function handoffsOf(entries: readonly SessionEventLikeEntry[]): readonly Handoff[];
25
+ //# sourceMappingURL=handoffs.d.ts.map
@@ -0,0 +1,35 @@
1
+ /** Register the right-panel Handoffs tab and the handoff view for other crew pages. */
2
+ import type { ComponentType } from 'react';
3
+ import type { Context } from '@deepseek-ai/cordis';
4
+ import { type HandoffViewProps } from './HandoffView.tsx';
5
+ import { type HandoffKey } from './locales.ts';
6
+ export { handoffsOf, type Handoff } from './handoffs.ts';
7
+ export { buildHandoffView, type AgentCard, type AgentState, type HandoffLane, type HandoffViewData } from './view.ts';
8
+ export { HandoffView, type HandoffViewProps } from './HandoffView.tsx';
9
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
10
+ interface LocaleNamespaceMap {
11
+ crewHandoff: HandoffKey;
12
+ }
13
+ }
14
+ /** The handoff view shared with other crew pages, which may not import it directly. */
15
+ export interface CrewHandoffs {
16
+ readonly View: ComponentType<HandoffViewProps>;
17
+ /** View helpers bound to the crew roster and faces. */
18
+ readonly helpers: Omit<HandoffViewProps, 'data' | 't'>;
19
+ }
20
+ declare module '@deepseek-ai/cordis' {
21
+ interface Context {
22
+ /** Agent handoff view for other crew pages. */
23
+ crewHandoffs: CrewHandoffs;
24
+ }
25
+ }
26
+ /** Handoffs tab implementation identity and keyed Slot dispatch key. */
27
+ export declare const HANDOFF_TAB_ID = "dsh-agent-crew-handoff-ui";
28
+ /** Services the tab and view read. */
29
+ export declare const inject: string[];
30
+ /**
31
+ * Register the copy, the tab type, the tab body and the shared view.
32
+ * @param ctx - Client plugin context.
33
+ */
34
+ export declare function apply(ctx: Context): void;
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,30 @@
1
+ /** Handoff view copy. */
2
+ export declare const en: {
3
+ 'tab.title': string;
4
+ 'tab.description': string;
5
+ lead: string;
6
+ 'state.working': string;
7
+ 'state.needs-you': string;
8
+ 'state.idle': string;
9
+ 'card.open': string;
10
+ 'card.task': string;
11
+ 'card.ready': string;
12
+ 'card.count': string;
13
+ 'lane.label': string;
14
+ 'lane.toggle': string;
15
+ summary: string;
16
+ 'summary.needsYou': string;
17
+ 'summary.working': string;
18
+ 'lead.role': string;
19
+ 'lead.count': string;
20
+ 'item.with': string;
21
+ 'item.back': string;
22
+ 'item.time': string;
23
+ 'item.via': string;
24
+ empty: string;
25
+ };
26
+ /** Keys shared by every dictionary. */
27
+ export type HandoffKey = keyof typeof en;
28
+ /** Chinese handoff view copy. */
29
+ export declare const zh: Record<HandoffKey, string>;
30
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,44 @@
1
+ /** Turn handoffs, the parent catalog and live Session state into agent cards and handoff lanes. */
2
+ import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
3
+ import type { SessionStatusSnapshot } from '@deepseek-ai/dsh-client-ui-session/client';
4
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
5
+ import type { SubagentCatalogEntry } from '@deepseek-ai/dsh-subagent/client';
6
+ import type { Handoff } from './handoffs.ts';
7
+ /** Live state shown on a card; `needs-you` wins over `working`. */
8
+ export type AgentState = 'working' | 'needs-you' | 'idle';
9
+ /** One agent card. */
10
+ export interface AgentCard {
11
+ /** Child Session, or the lead Session itself. */
12
+ readonly sessionId: SessionId;
13
+ /** Catalog name, or `lead` for the Session itself. */
14
+ readonly name: string;
15
+ /** Preset the Session runs, when the Session list reports one. */
16
+ readonly preset?: string;
17
+ readonly state: AgentState;
18
+ /** Task of the newest handoff to this agent. */
19
+ readonly task?: string;
20
+ /** Handoffs this agent received; for the lead, handoffs it made. */
21
+ readonly handoffs: number;
22
+ }
23
+ /** All handoffs from the lead to one agent, newest first. */
24
+ export interface HandoffLane {
25
+ readonly to: AgentCard;
26
+ readonly items: readonly Handoff[];
27
+ }
28
+ /** What the view draws. */
29
+ export interface HandoffViewData {
30
+ readonly lead: AgentCard;
31
+ readonly agents: readonly AgentCard[];
32
+ readonly lanes: readonly HandoffLane[];
33
+ }
34
+ /**
35
+ * Build the view for one lead Session.
36
+ * @param lead - the Session whose handoffs these are.
37
+ * @param handoffs - its handoffs, oldest first.
38
+ * @param catalog - its direct children, in creation order.
39
+ * @param list - the Session list, for presets and running state.
40
+ * @param status - live Session status, for running and waiting.
41
+ * @returns cards in creation order and lanes for agents that received work.
42
+ */
43
+ export declare function buildHandoffView(lead: SessionId, handoffs: readonly Handoff[], catalog: readonly SubagentCatalogEntry[], list: SessionListState, status: SessionStatusSnapshot): HandoffViewData;
44
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Browser registration is discovered through the package client declaration; the Host half is crew-handoff-ui. */
2
+ /** Register the browser-only contribution without Host state. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map
package/locale/de.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew-Übergaben",
4
+ "description": "Ein Übergaben-Tab, der zeigt, an welche Agenten eine Sitzung Arbeit übergeben hat, worum jeder gebeten wurde und ob er arbeitet, auf dich wartet oder untätig ist."
5
+ }
6
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew handoffs",
4
+ "description": "A Handoffs tab showing which agents a session handed work to, what each was asked and whether it is working, waiting on you or idle."
5
+ }
6
+ }
package/locale/es.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Traspasos de la crew",
4
+ "description": "Una pestaña Traspasos que muestra a qué agentes pasó trabajo una sesión, qué se pidió a cada uno y si está trabajando, esperándote o inactivo."
5
+ }
6
+ }
package/locale/fr.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Passations de la crew",
4
+ "description": "Un onglet Passations qui montre à quels agents une session a confié du travail, ce qui a été demandé à chacun et s’il travaille, vous attend ou est inactif."
5
+ }
6
+ }
package/locale/ja.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "クルーの引き継ぎ",
4
+ "description": "引き継ぎタブに、セッションがどのエージェントに仕事を渡したか、それぞれに何を頼んだか、作業中・あなた待ち・待機中のどれかを表示します。"
5
+ }
6
+ }
package/locale/pt.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Repasses da crew",
4
+ "description": "Uma aba Repasses que mostra para quais agentes uma sessão passou trabalho, o que foi pedido a cada um e se está trabalhando, esperando por você ou ocioso."
5
+ }
6
+ }
package/locale/ru.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Передачи команды",
4
+ "description": "Вкладка «Передачи» показывает, каким агентам сессия передала работу, что поручено каждому и работает ли он, ждёт вас или простаивает."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew 交接",
4
+ "description": "交接标签页显示会话把工作交给了哪些智能体、各自的任务,以及它们在工作、等待你还是空闲。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,73 @@
1
1
  {
2
2
  "name": "dsh-agent-crew-handoff-ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "Handoffs tab: agent cards with live state and lanes of the tasks a Session handed to each subagent",
4
+ "version": "0.1.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/tusharcora/dsh-agent-crew.git",
8
+ "directory": "packages/crew-handoff-ui"
9
+ },
10
+ "type": "module",
11
+ "main": "lib/index.js",
12
+ "types": "lib/types/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./lib/types/index.d.ts",
16
+ "default": "./lib/index.js"
17
+ },
18
+ "./src/*": "./src/*",
19
+ "./package.json": "./package.json",
20
+ "./client": {
21
+ "types": "./lib/types/client/index.d.ts",
22
+ "default": "./lib/client.js"
23
+ },
24
+ "./locale/*.json": "./locale/*.json"
25
+ },
26
+ "dsh": {
27
+ "client": {
28
+ "platform": "web",
29
+ "inject": [
30
+ "@deepseek-ai/dsh-api-session-controller",
31
+ "@deepseek-ai/dsh-client-locale",
32
+ "@deepseek-ai/dsh-client-ui-renderer",
33
+ "@deepseek-ai/dsh-client-ui-session",
34
+ "@deepseek-ai/dsh-client-ui-sidebar-right",
35
+ "@deepseek-ai/dsh-client-ui-workspace",
36
+ "dsh-agent-crew-agents-ui",
37
+ "dsh-agent-crew-faces"
38
+ ]
39
+ }
40
+ },
41
+ "license": "MIT",
42
+ "peerDependencies": {
43
+ "@deepseek-ai/cordis": "~4.1.0"
44
+ },
45
+ "files": [
46
+ "locale/*.json",
47
+ "lib/index.js",
48
+ "lib/client.js",
49
+ "lib/types/**/*.d.ts",
50
+ "LICENSE"
51
+ ],
52
+ "peerDependenciesMeta": {
53
+ "@deepseek-ai/cordis": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "keywords": [
58
+ "dsh",
59
+ "dsh-plugin",
60
+ "deepseek-harness",
61
+ "deepseek-harness-plugin",
62
+ "deepseek",
63
+ "dsh-agent-crew"
64
+ ],
65
+ "author": "Tushar (https://github.com/tusharcora)",
66
+ "homepage": "https://github.com/tusharcora/dsh-agent-crew#readme",
67
+ "bugs": {
68
+ "url": "https://github.com/tusharcora/dsh-agent-crew/issues"
69
+ },
70
+ "engines": {
71
+ "dsh": ">=0.2.2-rc.1 <0.3.0"
72
+ }
73
+ }