@prettier-ai/dsh-client-ui-subagent 0.1.2-alpha.1
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 +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +116 -0
- package/README.zh.md +116 -0
- package/lib/client.js +866 -0
- package/lib/index.js +11 -0
- package/lib/invariant.js +24 -0
- package/lib/types/client/SubagentHeaderLineage.d.ts +19 -0
- package/lib/types/client/SubagentReadOnlyComposer.d.ts +17 -0
- package/lib/types/client/index.d.ts +19 -0
- package/lib/types/client/locales.d.ts +48 -0
- package/lib/types/client/subagent-lineage.d.ts +21 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +90 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@prettier-ai/dsh-client-ui-subagent",
|
|
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 react_dom = require("react-dom");
|
|
10
|
+
let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
|
|
11
|
+
//#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-subagent/src/client/SubagentHeaderLineage.module.css.mjs
|
|
12
|
+
const css$1 = ".JwEVGW_root{align-items:center;gap:10px;min-width:0;display:inline-flex;position:relative}.JwEVGW_switcherRoot{min-width:0;margin-left:6px}.JwEVGW_trigger,.JwEVGW_switcherTrigger{min-height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;align-items:center;padding:3px 2px;font-size:12px;line-height:18px;display:inline-flex}.JwEVGW_trigger{gap:4px}.JwEVGW_switcherTrigger{min-width:0;max-width:244px;color:var(--dsw-alias-label-primary);gap:4px;font-weight:500}.JwEVGW_ancestorSwitcherTrigger{color:var(--dsw-alias-label-tertiary);font-weight:400}.JwEVGW_switcherTitle{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.JwEVGW_switcherTrigger svg{flex:none}.JwEVGW_separator{color:var(--dsw-alias-label-caption);font-size:14px;line-height:20px}.JwEVGW_activitySlot{flex:none;width:10px;height:10px;display:inline-flex}.JwEVGW_trigger:hover,.JwEVGW_trigger:focus-visible{color:var(--dsw-alias-label-secondary)}.JwEVGW_switcherTrigger:hover,.JwEVGW_switcherTrigger:focus-visible{color:var(--dsw-alias-label-primary)}.JwEVGW_ancestorSwitcherTrigger:hover,.JwEVGW_ancestorSwitcherTrigger:focus-visible{color:var(--dsw-alias-label-tertiary)}.JwEVGW_trigger svg,.JwEVGW_switcherTrigger svg{transition:transform .12s}.JwEVGW_triggerOpen{transform:rotate(180deg)}.JwEVGW_menu{z-index:100;box-sizing:border-box;background:var(--dsw-specific-menu);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);width:336px;max-width:min(400px,100vw - 32px);max-height:min(560px,100vh - 140px);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;flex-direction:column;padding:4px;display:flex;position:fixed;overflow:auto}.JwEVGW_node{min-width:0;position:relative}.JwEVGW_menu>.JwEVGW_node{margin-left:-3px}.JwEVGW_row{box-sizing:border-box;width:100%;min-height:50px;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;outline:none;align-items:flex-start;gap:8px;padding:7px 8px 7px 11px;font-size:13px;line-height:18px;display:flex;position:relative}.JwEVGW_row:hover>.JwEVGW_clickarea,.JwEVGW_row:focus-visible>.JwEVGW_clickarea{background:var(--dsw-alias-interactive-bg-hover)}.JwEVGW_clickarea{box-sizing:border-box;border-radius:8px;flex:1;align-self:stretch;align-items:flex-start;gap:8px;min-width:0;margin:-7px -8px;padding:7px 8px;display:flex}.JwEVGW_row>[data-state],.JwEVGW_clickarea>[data-state]{margin-top:4px}.JwEVGW_disabled{color:var(--dsw-alias-label-dimmed);cursor:not-allowed}.JwEVGW_disabled:hover{background:0 0}.JwEVGW_loadingRow{cursor:default}.JwEVGW_disclosure,.JwEVGW_disclosureSpace{flex:none;width:14px;height:18px}.JwEVGW_disclosure{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;justify-content:center;align-items:center;padding:0;transition:transform .12s;display:inline-flex}.JwEVGW_disclosure:hover{color:var(--dsw-alias-label-primary)}.JwEVGW_disclosureOpen{transform:rotate(90deg)}.JwEVGW_content{flex-direction:column;flex:1;min-width:0;display:flex}.JwEVGW_label,.JwEVGW_summary{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.JwEVGW_label{color:inherit;font-weight:400}.JwEVGW_currentLabel{font-weight:600}.JwEVGW_summary,.JwEVGW_metrics{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.JwEVGW_metrics{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap;flex:none;grid-template-rows:18px 16px;display:grid}.JwEVGW_metricToken{grid-row:1;line-height:18px}.JwEVGW_metricDuration{grid-row:2}.JwEVGW_children{margin-left:18px;padding-left:4px;position:relative}.JwEVGW_children:before,.JwEVGW_children>.JwEVGW_node:before{content:\"\";border-left:1px solid var(--dsw-alias-border-l2);position:absolute;left:0}.JwEVGW_children:before{height:26px;top:-26px}.JwEVGW_children[aria-busy=true]:before{content:none}.JwEVGW_children>.JwEVGW_node:before{top:0;bottom:0;left:-4px}.JwEVGW_children>.JwEVGW_node:last-child:before{height:17px;bottom:auto}.JwEVGW_children>.JwEVGW_node>.JwEVGW_row:before{content:\"\";border-top:1px solid var(--dsw-alias-border-l2);width:14px;position:absolute;top:16px;left:-4px}.JwEVGW_notice,.JwEVGW_error{color:var(--dsw-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.JwEVGW_error{color:var(--dsw-alias-state-error-primary);justify-content:space-between;align-items:center;gap:12px;display:flex}.JwEVGW_refresh{color:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;align-items:center;gap:4px;padding:4px 6px;display:inline-flex}.JwEVGW_refresh:hover{background:var(--dsw-alias-interactive-bg-hover)}";
|
|
13
|
+
const tagId$1 = "@prettier-ai/dsh-client-ui-subagent/SubagentHeaderLineage.module.css";
|
|
14
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
15
|
+
const tag = document.createElement("style");
|
|
16
|
+
tag.dataset.plugin = "@prettier-ai/dsh-client-ui-subagent";
|
|
17
|
+
tag.dataset.pluginCss = tagId$1;
|
|
18
|
+
tag.textContent = css$1;
|
|
19
|
+
document.head.appendChild(tag);
|
|
20
|
+
}
|
|
21
|
+
var SubagentHeaderLineage_module_css_default = {
|
|
22
|
+
"activitySlot": "JwEVGW_activitySlot",
|
|
23
|
+
"ancestorSwitcherTrigger": "JwEVGW_ancestorSwitcherTrigger",
|
|
24
|
+
"children": "JwEVGW_children",
|
|
25
|
+
"clickarea": "JwEVGW_clickarea",
|
|
26
|
+
"content": "JwEVGW_content",
|
|
27
|
+
"currentLabel": "JwEVGW_currentLabel",
|
|
28
|
+
"disabled": "JwEVGW_disabled",
|
|
29
|
+
"disclosure": "JwEVGW_disclosure",
|
|
30
|
+
"disclosureOpen": "JwEVGW_disclosureOpen",
|
|
31
|
+
"disclosureSpace": "JwEVGW_disclosureSpace",
|
|
32
|
+
"error": "JwEVGW_error",
|
|
33
|
+
"label": "JwEVGW_label",
|
|
34
|
+
"loadingRow": "JwEVGW_loadingRow",
|
|
35
|
+
"menu": "JwEVGW_menu",
|
|
36
|
+
"metricDuration": "JwEVGW_metricDuration",
|
|
37
|
+
"metricToken": "JwEVGW_metricToken",
|
|
38
|
+
"metrics": "JwEVGW_metrics",
|
|
39
|
+
"node": "JwEVGW_node",
|
|
40
|
+
"notice": "JwEVGW_notice",
|
|
41
|
+
"refresh": "JwEVGW_refresh",
|
|
42
|
+
"root": "JwEVGW_root",
|
|
43
|
+
"row": "JwEVGW_row",
|
|
44
|
+
"separator": "JwEVGW_separator",
|
|
45
|
+
"summary": "JwEVGW_summary",
|
|
46
|
+
"switcherRoot": "JwEVGW_switcherRoot",
|
|
47
|
+
"switcherTitle": "JwEVGW_switcherTitle",
|
|
48
|
+
"switcherTrigger": "JwEVGW_switcherTrigger",
|
|
49
|
+
"trigger": "JwEVGW_trigger",
|
|
50
|
+
"triggerOpen": "JwEVGW_triggerOpen"
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region lib/types/client/subagent-lineage.js
|
|
54
|
+
/** UI Subagent-owned projection of descendant counts from Session summaries. */
|
|
55
|
+
/**
|
|
56
|
+
* Index uninterrupted subagent descendants under each ancestor.
|
|
57
|
+
* @param summaries - Session summaries keyed by id.
|
|
58
|
+
* @returns descendant totals keyed by possible parent id.
|
|
59
|
+
*/
|
|
60
|
+
function indexSubagentDescendants(summaries) {
|
|
61
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const descendant of Object.values(summaries)) {
|
|
63
|
+
if (descendant.origin !== "subagent") continue;
|
|
64
|
+
const seen = /* @__PURE__ */ new Set();
|
|
65
|
+
let current = descendant;
|
|
66
|
+
while (current?.origin === "subagent" && current.parentId !== void 0 && !seen.has(current.id)) {
|
|
67
|
+
seen.add(current.id);
|
|
68
|
+
const aggregate = indexed.get(current.parentId);
|
|
69
|
+
if (aggregate === void 0) indexed.set(current.parentId, {
|
|
70
|
+
count: 1,
|
|
71
|
+
runningCount: descendant.running ? 1 : 0
|
|
72
|
+
});
|
|
73
|
+
else {
|
|
74
|
+
aggregate.count += 1;
|
|
75
|
+
if (descendant.running) aggregate.runningCount += 1;
|
|
76
|
+
}
|
|
77
|
+
current = summaries[current.parentId];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return indexed;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region lib/types/client/SubagentHeaderLineage.js
|
|
84
|
+
function diagnosticReason(entry, t) {
|
|
85
|
+
switch (entry.reason) {
|
|
86
|
+
case "corrupt": return t("diagnostic.corrupt");
|
|
87
|
+
case "unsupported": return t("diagnostic.unsupported");
|
|
88
|
+
case "unavailable": return t("diagnostic.unavailable");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function treeItems(root) {
|
|
92
|
+
return root === null ? [] : Array.from(root.querySelectorAll("[role=\"treeitem\"]:not([aria-disabled=\"true\"])"));
|
|
93
|
+
}
|
|
94
|
+
/** Compact token count shared in shape with the conversation stats strip. */
|
|
95
|
+
function formatTokens(value, t) {
|
|
96
|
+
const scaled = (next) => next >= 100 ? String(Math.round(next)) : String(Math.round(next * 10) / 10);
|
|
97
|
+
if (value < 1e3) return String(value);
|
|
98
|
+
if (value < 1e6) return t("tokens.thousand", { value: scaled(value / 1e3) });
|
|
99
|
+
return t("tokens.million", { value: scaled(value / 1e6) });
|
|
100
|
+
}
|
|
101
|
+
/** Sum the four disjoint durable provider-usage buckets. */
|
|
102
|
+
function tokenTotal(usage) {
|
|
103
|
+
return usage === void 0 ? void 0 : usage.uncachedInputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
104
|
+
}
|
|
105
|
+
/** Exact whole-second active-turn duration for one catalog row. */
|
|
106
|
+
function activityDuration(summary, activity, now) {
|
|
107
|
+
if (summary === void 0) return void 0;
|
|
108
|
+
const timing = summary.projectionValues?.subagentTiming;
|
|
109
|
+
if (timing === void 0) return void 0;
|
|
110
|
+
if (timing.active === void 0) return timing.settledMs;
|
|
111
|
+
const end = activity === "running" ? now : timing.active.through;
|
|
112
|
+
return timing.settledMs + Math.max(0, end - timing.active.since);
|
|
113
|
+
}
|
|
114
|
+
function splitDuration(ms) {
|
|
115
|
+
const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
|
|
116
|
+
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
117
|
+
const totalHours = Math.floor(totalMinutes / 60);
|
|
118
|
+
return {
|
|
119
|
+
seconds: totalSeconds % 60,
|
|
120
|
+
minutes: totalMinutes % 60,
|
|
121
|
+
hours: totalHours % 24,
|
|
122
|
+
days: Math.floor(totalHours / 24),
|
|
123
|
+
totalMinutes,
|
|
124
|
+
totalHours
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Format a duration with decreasing visual precision at larger scales. */
|
|
128
|
+
function formatDuration(ms, t) {
|
|
129
|
+
const { seconds, minutes, hours, days, totalMinutes, totalHours } = splitDuration(ms);
|
|
130
|
+
if (days >= 365) {
|
|
131
|
+
const years = Math.floor(days / 365);
|
|
132
|
+
const months = Math.floor(days % 365 / 30);
|
|
133
|
+
return months === 0 ? t("duration.years", { years }) : t("duration.yearsMonths", {
|
|
134
|
+
years,
|
|
135
|
+
months
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (days >= 30) {
|
|
139
|
+
const months = Math.floor(days / 30);
|
|
140
|
+
const remainingDays = days % 30;
|
|
141
|
+
return remainingDays === 0 ? t("duration.months", { months }) : t("duration.monthsDays", {
|
|
142
|
+
months,
|
|
143
|
+
days: remainingDays
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (days > 0) return hours === 0 ? t("duration.days", { days }) : t("duration.daysHours", {
|
|
147
|
+
days,
|
|
148
|
+
hours
|
|
149
|
+
});
|
|
150
|
+
if (totalHours > 0) return t("duration.hours", {
|
|
151
|
+
hours: totalHours,
|
|
152
|
+
minutes: String(minutes).padStart(2, "0"),
|
|
153
|
+
seconds: String(seconds).padStart(2, "0")
|
|
154
|
+
});
|
|
155
|
+
if (totalMinutes > 0) return t("duration.minutes", {
|
|
156
|
+
minutes: totalMinutes,
|
|
157
|
+
seconds: String(seconds).padStart(2, "0")
|
|
158
|
+
});
|
|
159
|
+
return t("duration.seconds", { seconds });
|
|
160
|
+
}
|
|
161
|
+
/** Preserve exact whole seconds for hover and accessible naming. */
|
|
162
|
+
function formatExactDuration(ms, t) {
|
|
163
|
+
const { seconds, minutes, hours, days } = splitDuration(ms);
|
|
164
|
+
return days === 0 ? formatDuration(ms, t) : t("duration.exactDays", {
|
|
165
|
+
days,
|
|
166
|
+
hours: String(hours).padStart(2, "0"),
|
|
167
|
+
minutes: String(minutes).padStart(2, "0"),
|
|
168
|
+
seconds: String(seconds).padStart(2, "0")
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const NO_DESCENDANTS = {
|
|
172
|
+
count: 0,
|
|
173
|
+
runningCount: 0
|
|
174
|
+
};
|
|
175
|
+
function SubagentSwitcherIcon() {
|
|
176
|
+
return (0, react_jsx_runtime.jsxs)("svg", {
|
|
177
|
+
width: "16",
|
|
178
|
+
height: "16",
|
|
179
|
+
viewBox: "0 0 20 20",
|
|
180
|
+
fill: "none",
|
|
181
|
+
"aria-hidden": "true",
|
|
182
|
+
children: [(0, react_jsx_runtime.jsx)("path", {
|
|
183
|
+
d: "M5.99951 12.7L8.95546 14.9478C9.40011 15.2859 9.62244 15.455 9.87526 15.488C9.95774 15.4988 10.0413 15.4988 10.1238 15.488C10.3766 15.455 10.5989 15.2859 11.0436 14.9478L13.9995 12.7",
|
|
184
|
+
stroke: "currentColor",
|
|
185
|
+
strokeWidth: "1.5"
|
|
186
|
+
}), (0, react_jsx_runtime.jsx)("path", {
|
|
187
|
+
d: "M13.9995 7.7417L11.0436 5.49387C10.5989 5.15574 10.3766 4.98668 10.1238 4.95362C10.0413 4.94283 9.95775 4.94283 9.87527 4.95362C9.62245 4.98668 9.40012 5.15574 8.95547 5.49387L5.99952 7.7417",
|
|
188
|
+
stroke: "currentColor",
|
|
189
|
+
strokeWidth: "1.5"
|
|
190
|
+
})]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/** Render the known direct-child shape while its authoritative catalog hydrates. */
|
|
194
|
+
function CatalogLoadingRows({ parentSessionId, summaries, level, t }) {
|
|
195
|
+
const children = Object.values(summaries).filter((summary) => summary.origin === "subagent" && summary.parentId === parentSessionId);
|
|
196
|
+
if (children.length === 0) return (0, react_jsx_runtime.jsx)("div", {
|
|
197
|
+
className: SubagentHeaderLineage_module_css_default.notice,
|
|
198
|
+
children: t("loading.label")
|
|
199
|
+
});
|
|
200
|
+
return children.map((summary) => (0, react_jsx_runtime.jsx)("div", {
|
|
201
|
+
className: SubagentHeaderLineage_module_css_default.node,
|
|
202
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
203
|
+
role: "treeitem",
|
|
204
|
+
"aria-disabled": "true",
|
|
205
|
+
"aria-level": level,
|
|
206
|
+
"aria-label": t("loading.aria"),
|
|
207
|
+
className: `${SubagentHeaderLineage_module_css_default.row} ${SubagentHeaderLineage_module_css_default.disabled} ${SubagentHeaderLineage_module_css_default.loadingRow}`,
|
|
208
|
+
children: [
|
|
209
|
+
(0, react_jsx_runtime.jsx)("span", { className: SubagentHeaderLineage_module_css_default.disclosureSpace }),
|
|
210
|
+
(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: summary.running ? "ongoing" : "done" }),
|
|
211
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
212
|
+
className: SubagentHeaderLineage_module_css_default.content,
|
|
213
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
214
|
+
className: SubagentHeaderLineage_module_css_default.label,
|
|
215
|
+
children: t("loading.label")
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
]
|
|
219
|
+
})
|
|
220
|
+
}, summary.id));
|
|
221
|
+
}
|
|
222
|
+
/** Render one catalog level and recurse only through explicitly expanded rows. */
|
|
223
|
+
function CatalogRows({ parentSessionId, currentSessionId, catalog, catalogs, summaries, expanded, level, now, openChild, refresh, toggleBranch, closeCatalog, t }) {
|
|
224
|
+
const emptyLoading = catalog.state === "loading" && catalog.entries.length === 0;
|
|
225
|
+
const reserveDisclosure = catalog.entries.some((entry) => entry.kind === "child" && entry.hasChildren);
|
|
226
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
227
|
+
emptyLoading && (0, react_jsx_runtime.jsx)(CatalogLoadingRows, {
|
|
228
|
+
parentSessionId,
|
|
229
|
+
summaries,
|
|
230
|
+
level,
|
|
231
|
+
t
|
|
232
|
+
}),
|
|
233
|
+
catalog.state === "error" && (0, react_jsx_runtime.jsxs)("div", {
|
|
234
|
+
className: SubagentHeaderLineage_module_css_default.error,
|
|
235
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: catalog.error?.message ?? t("load.error") }), (0, react_jsx_runtime.jsxs)("button", {
|
|
236
|
+
type: "button",
|
|
237
|
+
className: SubagentHeaderLineage_module_css_default.refresh,
|
|
238
|
+
onClick: () => {
|
|
239
|
+
refresh(parentSessionId);
|
|
240
|
+
},
|
|
241
|
+
children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconRefreshOutline14, {}), t("retry")]
|
|
242
|
+
})]
|
|
243
|
+
}),
|
|
244
|
+
catalog.entries.map((entry) => {
|
|
245
|
+
if (entry.kind === "diagnostic") {
|
|
246
|
+
const reason = diagnosticReason(entry, t);
|
|
247
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
248
|
+
className: SubagentHeaderLineage_module_css_default.node,
|
|
249
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
250
|
+
role: "treeitem",
|
|
251
|
+
"aria-disabled": "true",
|
|
252
|
+
"aria-level": level,
|
|
253
|
+
"aria-label": `${entry.id} ${reason}`,
|
|
254
|
+
className: `${SubagentHeaderLineage_module_css_default.row} ${SubagentHeaderLineage_module_css_default.disabled}`,
|
|
255
|
+
title: reason,
|
|
256
|
+
children: [
|
|
257
|
+
reserveDisclosure && (0, react_jsx_runtime.jsx)("span", { className: SubagentHeaderLineage_module_css_default.disclosureSpace }),
|
|
258
|
+
(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "error" }),
|
|
259
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
260
|
+
className: SubagentHeaderLineage_module_css_default.content,
|
|
261
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
262
|
+
className: SubagentHeaderLineage_module_css_default.label,
|
|
263
|
+
children: entry.id
|
|
264
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
265
|
+
className: SubagentHeaderLineage_module_css_default.summary,
|
|
266
|
+
children: reason
|
|
267
|
+
})]
|
|
268
|
+
})
|
|
269
|
+
]
|
|
270
|
+
})
|
|
271
|
+
}, entry.id);
|
|
272
|
+
}
|
|
273
|
+
const childCatalog = catalogs[entry.id];
|
|
274
|
+
const isCurrent = entry.id === currentSessionId;
|
|
275
|
+
const isExpanded = expanded.has(entry.id);
|
|
276
|
+
const knownLeaf = !entry.hasChildren;
|
|
277
|
+
const childLoading = childCatalog === void 0 || childCatalog.state === "loading" && childCatalog.entries.length === 0;
|
|
278
|
+
const summary = summaries[entry.id];
|
|
279
|
+
const label = entry.label ?? entry.id;
|
|
280
|
+
const mode = entry.mode === "one-shot" ? t("mode.oneShot") : t("mode.continuable");
|
|
281
|
+
const activity = entry.activity === "running" ? t("activity.running") : t("activity.inactive");
|
|
282
|
+
const secondary = [
|
|
283
|
+
summary?.title,
|
|
284
|
+
mode,
|
|
285
|
+
activity
|
|
286
|
+
].filter((value) => value !== void 0).join(" · ");
|
|
287
|
+
const totalTokens = tokenTotal(summary?.projectionValues?.tokenUsage);
|
|
288
|
+
const durationMs = activityDuration(summary, entry.activity, now);
|
|
289
|
+
const tokenMetric = totalTokens === void 0 ? void 0 : t("tokens.total", { value: formatTokens(totalTokens, t) });
|
|
290
|
+
const durationMetric = durationMs === void 0 ? void 0 : {
|
|
291
|
+
compact: formatDuration(durationMs, t),
|
|
292
|
+
exact: formatExactDuration(durationMs, t)
|
|
293
|
+
};
|
|
294
|
+
const metrics = [tokenMetric, durationMetric?.exact].filter((value) => value !== void 0).join(" · ");
|
|
295
|
+
const open = () => {
|
|
296
|
+
openChild({
|
|
297
|
+
parentSessionId,
|
|
298
|
+
childSessionId: entry.id,
|
|
299
|
+
mode: entry.mode
|
|
300
|
+
});
|
|
301
|
+
closeCatalog();
|
|
302
|
+
};
|
|
303
|
+
const handleKey = (event) => {
|
|
304
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
event.stopPropagation();
|
|
307
|
+
open();
|
|
308
|
+
} else if (event.key === "ArrowRight" && !knownLeaf && !isExpanded || event.key === "ArrowLeft" && isExpanded) {
|
|
309
|
+
event.preventDefault();
|
|
310
|
+
event.stopPropagation();
|
|
311
|
+
toggleBranch(entry.id);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const toggle = (event) => {
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
event.stopPropagation();
|
|
317
|
+
toggleBranch(entry.id);
|
|
318
|
+
};
|
|
319
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
320
|
+
className: SubagentHeaderLineage_module_css_default.node,
|
|
321
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
322
|
+
role: "treeitem",
|
|
323
|
+
tabIndex: 0,
|
|
324
|
+
"aria-level": level,
|
|
325
|
+
"aria-current": isCurrent || void 0,
|
|
326
|
+
"aria-label": [
|
|
327
|
+
label,
|
|
328
|
+
secondary,
|
|
329
|
+
metrics
|
|
330
|
+
].filter((value) => value !== "").join(" "),
|
|
331
|
+
...knownLeaf ? {} : { "aria-expanded": isExpanded },
|
|
332
|
+
className: SubagentHeaderLineage_module_css_default.row,
|
|
333
|
+
onClick: open,
|
|
334
|
+
onKeyDown: handleKey,
|
|
335
|
+
children: [knownLeaf ? reserveDisclosure && (0, react_jsx_runtime.jsx)("span", { className: SubagentHeaderLineage_module_css_default.disclosureSpace }) : (0, react_jsx_runtime.jsx)("button", {
|
|
336
|
+
type: "button",
|
|
337
|
+
tabIndex: -1,
|
|
338
|
+
className: `${SubagentHeaderLineage_module_css_default.disclosure} ${isExpanded ? SubagentHeaderLineage_module_css_default.disclosureOpen : ""}`,
|
|
339
|
+
"aria-label": t(isExpanded ? "branch.collapse" : "branch.expand", { label }),
|
|
340
|
+
onClick: toggle,
|
|
341
|
+
children: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
342
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
343
|
+
className: SubagentHeaderLineage_module_css_default.clickarea,
|
|
344
|
+
children: [
|
|
345
|
+
(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: entry.activity === "running" ? "ongoing" : "done" }),
|
|
346
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
347
|
+
className: SubagentHeaderLineage_module_css_default.content,
|
|
348
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
349
|
+
className: `${SubagentHeaderLineage_module_css_default.label} ${isCurrent ? SubagentHeaderLineage_module_css_default.currentLabel : ""}`,
|
|
350
|
+
children: label
|
|
351
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
352
|
+
className: SubagentHeaderLineage_module_css_default.summary,
|
|
353
|
+
children: secondary
|
|
354
|
+
})]
|
|
355
|
+
}),
|
|
356
|
+
metrics !== "" && (0, react_jsx_runtime.jsxs)("span", {
|
|
357
|
+
className: SubagentHeaderLineage_module_css_default.metrics,
|
|
358
|
+
children: [tokenMetric !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
359
|
+
className: SubagentHeaderLineage_module_css_default.metricToken,
|
|
360
|
+
children: tokenMetric
|
|
361
|
+
}), durationMetric !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
362
|
+
className: SubagentHeaderLineage_module_css_default.metricDuration,
|
|
363
|
+
title: t("duration.exactTitle", { duration: durationMetric.exact }),
|
|
364
|
+
children: durationMetric.compact
|
|
365
|
+
})]
|
|
366
|
+
})
|
|
367
|
+
]
|
|
368
|
+
})]
|
|
369
|
+
}), isExpanded && !knownLeaf && (0, react_jsx_runtime.jsx)("div", {
|
|
370
|
+
role: "group",
|
|
371
|
+
className: SubagentHeaderLineage_module_css_default.children,
|
|
372
|
+
"aria-busy": childLoading || void 0,
|
|
373
|
+
children: childCatalog === void 0 ? (0, react_jsx_runtime.jsx)(CatalogLoadingRows, {
|
|
374
|
+
parentSessionId: entry.id,
|
|
375
|
+
summaries,
|
|
376
|
+
level: level + 1,
|
|
377
|
+
t
|
|
378
|
+
}) : (0, react_jsx_runtime.jsx)(CatalogRows, {
|
|
379
|
+
parentSessionId: entry.id,
|
|
380
|
+
currentSessionId,
|
|
381
|
+
catalog: childCatalog,
|
|
382
|
+
catalogs,
|
|
383
|
+
summaries,
|
|
384
|
+
expanded,
|
|
385
|
+
level: level + 1,
|
|
386
|
+
now,
|
|
387
|
+
openChild,
|
|
388
|
+
refresh,
|
|
389
|
+
toggleBranch,
|
|
390
|
+
closeCatalog,
|
|
391
|
+
t
|
|
392
|
+
})
|
|
393
|
+
})]
|
|
394
|
+
}, entry.id);
|
|
395
|
+
})
|
|
396
|
+
] });
|
|
397
|
+
}
|
|
398
|
+
const MENU_VIEWPORT_MARGIN = 16;
|
|
399
|
+
/** Place a portaled catalog below its trigger without crossing the viewport edge. */
|
|
400
|
+
function catalogMenuPosition(trigger) {
|
|
401
|
+
const rect = trigger.getBoundingClientRect();
|
|
402
|
+
const width = Math.min(336, window.innerWidth - MENU_VIEWPORT_MARGIN * 2);
|
|
403
|
+
return {
|
|
404
|
+
top: rect.bottom + 5,
|
|
405
|
+
left: Math.min(Math.max(MENU_VIEWPORT_MARGIN, rect.left), window.innerWidth - width - MENU_VIEWPORT_MARGIN)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/** One trigger-plus-tree dropdown over the catalog rooted at `rootSessionId`. */
|
|
409
|
+
function CatalogDropdown({ rootSessionId, currentSessionId, displayTitle, openTitle, variant, separator = false, useSessions, openChild, refresh, setCatalogOpen, t }) {
|
|
410
|
+
const ancestorSwitcher = variant === "switcher" && openTitle !== void 0;
|
|
411
|
+
const catalogs = useSessions((state) => state.subagentsByParent);
|
|
412
|
+
const summaries = useSessions((state) => state.byId);
|
|
413
|
+
const catalog = catalogs[rootSessionId];
|
|
414
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
415
|
+
const [menuPosition, setMenuPosition] = (0, react.useState)();
|
|
416
|
+
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
417
|
+
const [expanded, setExpanded] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
418
|
+
const rootRef = (0, react.useRef)(null);
|
|
419
|
+
const triggerRef = (0, react.useRef)(null);
|
|
420
|
+
const menuRef = (0, react.useRef)(null);
|
|
421
|
+
const hoverOpenTimer = (0, react.useRef)(void 0);
|
|
422
|
+
const hoverCloseTimer = (0, react.useRef)(void 0);
|
|
423
|
+
const observedCatalogs = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
424
|
+
const setCatalogOpenRef = (0, react.useRef)(setCatalogOpen);
|
|
425
|
+
setCatalogOpenRef.current = setCatalogOpen;
|
|
426
|
+
const currentEntry = currentSessionId === void 0 ? void 0 : catalog?.entries.find((entry) => entry.kind === "child" && entry.id === currentSessionId);
|
|
427
|
+
const switcherDisplayTitle = currentEntry?.kind === "child" ? currentEntry.label ?? currentEntry.id : displayTitle;
|
|
428
|
+
const healthy = catalog?.entries.filter((entry) => entry.kind === "child") ?? [];
|
|
429
|
+
const descendants = (0, react.useMemo)(() => indexSubagentDescendants(summaries).get(rootSessionId) ?? NO_DESCENDANTS, [rootSessionId, summaries]);
|
|
430
|
+
const descendantCount = Math.max(healthy.length, descendants.count);
|
|
431
|
+
const totalCountKey = descendantCount === 1 ? "count.total.one" : "count.total.other";
|
|
432
|
+
const runningCountKey = descendants.runningCount === 1 ? "count.running.one" : "count.running.other";
|
|
433
|
+
const presentedCatalog = (descendants.count > 0 || variant === "switcher") && (catalog === void 0 || catalog.state === "ready" && catalog.entries.length === 0) ? {
|
|
434
|
+
entries: [],
|
|
435
|
+
parentAvailable: catalog?.parentAvailable ?? false,
|
|
436
|
+
state: "loading",
|
|
437
|
+
error: null
|
|
438
|
+
} : catalog;
|
|
439
|
+
const observeCatalog = (parentSessionId, next) => {
|
|
440
|
+
if (next) observedCatalogs.current.add(parentSessionId);
|
|
441
|
+
else observedCatalogs.current.delete(parentSessionId);
|
|
442
|
+
setCatalogOpen(parentSessionId, next);
|
|
443
|
+
};
|
|
444
|
+
const closeAllCatalogs = () => {
|
|
445
|
+
for (const parentSessionId of observedCatalogs.current) setCatalogOpen(parentSessionId, false);
|
|
446
|
+
observedCatalogs.current.clear();
|
|
447
|
+
setExpanded(/* @__PURE__ */ new Set());
|
|
448
|
+
};
|
|
449
|
+
const cancelHoverClose = () => {
|
|
450
|
+
if (hoverCloseTimer.current === void 0) return;
|
|
451
|
+
clearTimeout(hoverCloseTimer.current);
|
|
452
|
+
hoverCloseTimer.current = void 0;
|
|
453
|
+
};
|
|
454
|
+
const cancelHoverOpen = () => {
|
|
455
|
+
if (hoverOpenTimer.current === void 0) return;
|
|
456
|
+
clearTimeout(hoverOpenTimer.current);
|
|
457
|
+
hoverOpenTimer.current = void 0;
|
|
458
|
+
};
|
|
459
|
+
const changeOpen = (next, restoreFocus = false) => {
|
|
460
|
+
cancelHoverOpen();
|
|
461
|
+
cancelHoverClose();
|
|
462
|
+
if (next) {
|
|
463
|
+
const trigger = triggerRef.current;
|
|
464
|
+
/* v8 ignore next -- a queued callback can outlive the trigger */
|
|
465
|
+
if (trigger === null) return;
|
|
466
|
+
setOpen(true);
|
|
467
|
+
setMenuPosition(catalogMenuPosition(trigger));
|
|
468
|
+
setNow(Date.now());
|
|
469
|
+
observeCatalog(rootSessionId, true);
|
|
470
|
+
} else {
|
|
471
|
+
setOpen(false);
|
|
472
|
+
setMenuPosition(void 0);
|
|
473
|
+
closeAllCatalogs();
|
|
474
|
+
}
|
|
475
|
+
if (restoreFocus) queueMicrotask(() => {
|
|
476
|
+
triggerRef.current?.focus();
|
|
477
|
+
});
|
|
478
|
+
};
|
|
479
|
+
const scheduleHoverOpen = () => {
|
|
480
|
+
cancelHoverOpen();
|
|
481
|
+
cancelHoverClose();
|
|
482
|
+
if (open) return;
|
|
483
|
+
hoverOpenTimer.current = setTimeout(() => {
|
|
484
|
+
hoverOpenTimer.current = void 0;
|
|
485
|
+
changeOpen(true);
|
|
486
|
+
}, 150);
|
|
487
|
+
};
|
|
488
|
+
const scheduleHoverClose = () => {
|
|
489
|
+
cancelHoverOpen();
|
|
490
|
+
cancelHoverClose();
|
|
491
|
+
hoverCloseTimer.current = setTimeout(() => {
|
|
492
|
+
hoverCloseTimer.current = void 0;
|
|
493
|
+
changeOpen(false);
|
|
494
|
+
}, 120);
|
|
495
|
+
};
|
|
496
|
+
const closeBranch = (root) => {
|
|
497
|
+
const closing = /* @__PURE__ */ new Set();
|
|
498
|
+
const visit = (parentSessionId) => {
|
|
499
|
+
if (closing.has(parentSessionId) || !expanded.has(parentSessionId)) return;
|
|
500
|
+
closing.add(parentSessionId);
|
|
501
|
+
const branch = catalogs[parentSessionId];
|
|
502
|
+
for (const entry of branch?.entries ?? []) if (entry.kind === "child") visit(entry.id);
|
|
503
|
+
};
|
|
504
|
+
visit(root);
|
|
505
|
+
for (const parentSessionId of closing) observeCatalog(parentSessionId, false);
|
|
506
|
+
setExpanded((current) => new Set([...current].filter((id) => !closing.has(id))));
|
|
507
|
+
};
|
|
508
|
+
const toggleBranch = (childSessionId) => {
|
|
509
|
+
if (expanded.has(childSessionId)) {
|
|
510
|
+
closeBranch(childSessionId);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
setExpanded((current) => new Set(current).add(childSessionId));
|
|
514
|
+
observeCatalog(childSessionId, true);
|
|
515
|
+
};
|
|
516
|
+
(0, react.useEffect)(() => {
|
|
517
|
+
if (!open) return;
|
|
518
|
+
const closeOutside = (event) => {
|
|
519
|
+
if (event.target instanceof Node && !rootRef.current?.contains(event.target) && !menuRef.current?.contains(event.target)) changeOpen(false);
|
|
520
|
+
};
|
|
521
|
+
document.addEventListener("pointerdown", closeOutside);
|
|
522
|
+
return () => {
|
|
523
|
+
document.removeEventListener("pointerdown", closeOutside);
|
|
524
|
+
};
|
|
525
|
+
}, [open]);
|
|
526
|
+
(0, react.useEffect)(() => {
|
|
527
|
+
if (!open) return;
|
|
528
|
+
const placeMenu = () => {
|
|
529
|
+
const trigger = triggerRef.current;
|
|
530
|
+
/* v8 ignore next -- native resize or scroll can outlive the trigger */
|
|
531
|
+
if (trigger === null) return;
|
|
532
|
+
setMenuPosition(catalogMenuPosition(trigger));
|
|
533
|
+
};
|
|
534
|
+
window.addEventListener("resize", placeMenu);
|
|
535
|
+
document.addEventListener("scroll", placeMenu, true);
|
|
536
|
+
return () => {
|
|
537
|
+
window.removeEventListener("resize", placeMenu);
|
|
538
|
+
document.removeEventListener("scroll", placeMenu, true);
|
|
539
|
+
};
|
|
540
|
+
}, [open]);
|
|
541
|
+
(0, react.useEffect)(() => {
|
|
542
|
+
if (!open || descendants.runningCount === 0) return;
|
|
543
|
+
const timer = setInterval(() => {
|
|
544
|
+
setNow(Date.now());
|
|
545
|
+
}, 1e3);
|
|
546
|
+
return () => {
|
|
547
|
+
clearInterval(timer);
|
|
548
|
+
};
|
|
549
|
+
}, [open, descendants.runningCount]);
|
|
550
|
+
(0, react.useEffect)(() => () => {
|
|
551
|
+
cancelHoverOpen();
|
|
552
|
+
cancelHoverClose();
|
|
553
|
+
for (const parentSessionId of observedCatalogs.current) setCatalogOpenRef.current(parentSessionId, false);
|
|
554
|
+
observedCatalogs.current.clear();
|
|
555
|
+
}, []);
|
|
556
|
+
const visible = presentedCatalog !== void 0 && (variant === "switcher" || presentedCatalog.state === "error" || presentedCatalog.entries.length > 0 || descendantCount > 0);
|
|
557
|
+
(0, react.useEffect)(() => {
|
|
558
|
+
if (visible) return;
|
|
559
|
+
cancelHoverOpen();
|
|
560
|
+
cancelHoverClose();
|
|
561
|
+
if (!open) return;
|
|
562
|
+
setOpen(false);
|
|
563
|
+
closeAllCatalogs();
|
|
564
|
+
}, [visible, open]);
|
|
565
|
+
if (!visible) return null;
|
|
566
|
+
const focusAt = (index) => {
|
|
567
|
+
const items = treeItems(menuRef.current);
|
|
568
|
+
if (items.length === 0) return;
|
|
569
|
+
items[(index + items.length) % items.length]?.focus();
|
|
570
|
+
};
|
|
571
|
+
const navigate = (event) => {
|
|
572
|
+
const items = treeItems(menuRef.current);
|
|
573
|
+
const index = items.indexOf(document.activeElement);
|
|
574
|
+
if (event.key === "Escape") {
|
|
575
|
+
event.preventDefault();
|
|
576
|
+
changeOpen(false, true);
|
|
577
|
+
} else if (event.key === "Home") {
|
|
578
|
+
event.preventDefault();
|
|
579
|
+
focusAt(0);
|
|
580
|
+
} else if (event.key === "End") {
|
|
581
|
+
event.preventDefault();
|
|
582
|
+
focusAt(items.length - 1);
|
|
583
|
+
} else if (event.key === "ArrowDown") {
|
|
584
|
+
event.preventDefault();
|
|
585
|
+
focusAt(index + 1);
|
|
586
|
+
} else if (event.key === "ArrowUp") {
|
|
587
|
+
event.preventDefault();
|
|
588
|
+
focusAt(index < 0 ? items.length - 1 : index - 1);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
592
|
+
className: `${SubagentHeaderLineage_module_css_default.root} ${variant === "switcher" ? SubagentHeaderLineage_module_css_default.switcherRoot : ""}`,
|
|
593
|
+
ref: rootRef,
|
|
594
|
+
onKeyDown: navigate,
|
|
595
|
+
onMouseEnter: scheduleHoverOpen,
|
|
596
|
+
onMouseLeave: scheduleHoverClose,
|
|
597
|
+
children: [
|
|
598
|
+
separator && (0, react_jsx_runtime.jsx)("span", {
|
|
599
|
+
className: SubagentHeaderLineage_module_css_default.separator,
|
|
600
|
+
children: "/"
|
|
601
|
+
}),
|
|
602
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
603
|
+
ref: triggerRef,
|
|
604
|
+
type: "button",
|
|
605
|
+
className: variant === "switcher" ? `${SubagentHeaderLineage_module_css_default.switcherTrigger} ${ancestorSwitcher ? SubagentHeaderLineage_module_css_default.ancestorSwitcherTrigger : ""}` : SubagentHeaderLineage_module_css_default.trigger,
|
|
606
|
+
"aria-haspopup": "tree",
|
|
607
|
+
"aria-expanded": open,
|
|
608
|
+
"aria-label": variant === "switcher" ? t("switcher.aria", { title: switcherDisplayTitle }) : t(descendants.runningCount > 0 ? runningCountKey : totalCountKey, { count: descendants.runningCount > 0 ? descendants.runningCount : descendantCount }),
|
|
609
|
+
onClick: openTitle === void 0 ? void 0 : () => {
|
|
610
|
+
cancelHoverOpen();
|
|
611
|
+
if (open) changeOpen(false);
|
|
612
|
+
openTitle();
|
|
613
|
+
},
|
|
614
|
+
onKeyDown: (event) => {
|
|
615
|
+
if (event.key !== "ArrowDown") return;
|
|
616
|
+
event.preventDefault();
|
|
617
|
+
if (!open) changeOpen(true);
|
|
618
|
+
queueMicrotask(() => {
|
|
619
|
+
focusAt(0);
|
|
620
|
+
});
|
|
621
|
+
},
|
|
622
|
+
children: [variant === "switcher" ? (0, react_jsx_runtime.jsx)("span", {
|
|
623
|
+
className: SubagentHeaderLineage_module_css_default.switcherTitle,
|
|
624
|
+
children: switcherDisplayTitle
|
|
625
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [descendants.runningCount > 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
626
|
+
className: SubagentHeaderLineage_module_css_default.activitySlot,
|
|
627
|
+
children: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" })
|
|
628
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
629
|
+
className: SubagentHeaderLineage_module_css_default.count,
|
|
630
|
+
children: t(totalCountKey, { count: descendantCount })
|
|
631
|
+
})] }), variant === "switcher" ? (0, react_jsx_runtime.jsx)(SubagentSwitcherIcon, {}) : (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? SubagentHeaderLineage_module_css_default.triggerOpen : void 0 })]
|
|
632
|
+
}),
|
|
633
|
+
open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsx)("div", {
|
|
634
|
+
ref: menuRef,
|
|
635
|
+
className: SubagentHeaderLineage_module_css_default.menu,
|
|
636
|
+
style: menuPosition,
|
|
637
|
+
role: "tree",
|
|
638
|
+
"aria-label": t("tree.aria"),
|
|
639
|
+
onMouseEnter: cancelHoverClose,
|
|
640
|
+
onMouseLeave: scheduleHoverClose,
|
|
641
|
+
children: (0, react_jsx_runtime.jsx)(CatalogRows, {
|
|
642
|
+
parentSessionId: rootSessionId,
|
|
643
|
+
currentSessionId,
|
|
644
|
+
catalog: presentedCatalog,
|
|
645
|
+
catalogs,
|
|
646
|
+
summaries,
|
|
647
|
+
expanded,
|
|
648
|
+
level: 1,
|
|
649
|
+
now,
|
|
650
|
+
openChild,
|
|
651
|
+
refresh,
|
|
652
|
+
toggleBranch,
|
|
653
|
+
closeCatalog: () => {
|
|
654
|
+
changeOpen(false);
|
|
655
|
+
},
|
|
656
|
+
t
|
|
657
|
+
})
|
|
658
|
+
}), document.body)
|
|
659
|
+
]
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Render one breadcrumb title together with its subagent navigation.
|
|
664
|
+
* @param props - Breadcrumb title, session standard props, and catalog actions.
|
|
665
|
+
* @returns An ordinary-title descendant count, or a title-and-chevron sibling switcher.
|
|
666
|
+
*/
|
|
667
|
+
function SubagentHeaderLineage({ lineageSessionId, displayTitle, openTitle, useSessions, openChild, refresh, setCatalogOpen, t }) {
|
|
668
|
+
const parentId = useSessions((state) => {
|
|
669
|
+
const summary = state.byId[lineageSessionId];
|
|
670
|
+
return summary?.origin === "subagent" ? summary.parentId : void 0;
|
|
671
|
+
});
|
|
672
|
+
const shared = {
|
|
673
|
+
useSessions,
|
|
674
|
+
openChild,
|
|
675
|
+
refresh,
|
|
676
|
+
setCatalogOpen,
|
|
677
|
+
t
|
|
678
|
+
};
|
|
679
|
+
if (parentId === void 0) return (0, react_jsx_runtime.jsx)(CatalogDropdown, {
|
|
680
|
+
rootSessionId: lineageSessionId,
|
|
681
|
+
variant: "count",
|
|
682
|
+
separator: true,
|
|
683
|
+
...shared
|
|
684
|
+
}, lineageSessionId);
|
|
685
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(CatalogDropdown, {
|
|
686
|
+
rootSessionId: parentId,
|
|
687
|
+
currentSessionId: lineageSessionId,
|
|
688
|
+
variant: "switcher",
|
|
689
|
+
displayTitle,
|
|
690
|
+
...openTitle === void 0 ? {} : { openTitle },
|
|
691
|
+
...shared
|
|
692
|
+
}, lineageSessionId), openTitle === void 0 && (0, react_jsx_runtime.jsx)(CatalogDropdown, {
|
|
693
|
+
rootSessionId: lineageSessionId,
|
|
694
|
+
variant: "count",
|
|
695
|
+
...shared
|
|
696
|
+
}, lineageSessionId)] });
|
|
697
|
+
}
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-subagent/src/client/SubagentReadOnlyComposer.module.css.mjs
|
|
700
|
+
const css = ".dWMx3G_frame{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);min-height:54px;color:var(--dsw-alias-label-tertiary);border-radius:14px;justify-content:center;align-items:center;gap:8px;margin:0 24px 20px;padding:10px 16px;font-size:13px;line-height:20px;display:flex}.dWMx3G_frame strong{color:var(--dsw-alias-label-primary);font-weight:510}";
|
|
701
|
+
const tagId = "@prettier-ai/dsh-client-ui-subagent/SubagentReadOnlyComposer.module.css";
|
|
702
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
703
|
+
const tag = document.createElement("style");
|
|
704
|
+
tag.dataset.plugin = "@prettier-ai/dsh-client-ui-subagent";
|
|
705
|
+
tag.dataset.pluginCss = tagId;
|
|
706
|
+
tag.textContent = css;
|
|
707
|
+
document.head.appendChild(tag);
|
|
708
|
+
}
|
|
709
|
+
var SubagentReadOnlyComposer_module_css_default = { "frame": "dWMx3G_frame" };
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region lib/types/client/SubagentReadOnlyComposer.js
|
|
712
|
+
/**
|
|
713
|
+
* Explain why the normal composer is unavailable for an addressed child.
|
|
714
|
+
* @param props - selector-owned read-only reason plus standard slot props.
|
|
715
|
+
* @returns A read-only composer replacement.
|
|
716
|
+
*/
|
|
717
|
+
function SubagentReadOnlyComposer({ matched, t }) {
|
|
718
|
+
const oneShot = matched.reason === "one-shot";
|
|
719
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
720
|
+
className: SubagentReadOnlyComposer_module_css_default.frame,
|
|
721
|
+
role: "status",
|
|
722
|
+
children: [(0, react_jsx_runtime.jsx)("strong", { children: t(oneShot ? "readonly.oneShot.title" : "readonly.title") }), (0, react_jsx_runtime.jsx)("span", { children: t(oneShot ? "readonly.oneShot.body" : "readonly.body") })]
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region lib/types/client/locales.js
|
|
727
|
+
/** `subagent` namespace dictionaries. */
|
|
728
|
+
/** Dictionary namespace owned by this plugin. */
|
|
729
|
+
const NS = "subagent";
|
|
730
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
731
|
+
const zh = {
|
|
732
|
+
"diagnostic.corrupt": "会话记录损坏",
|
|
733
|
+
"diagnostic.unsupported": "子代理记录版本不受支持",
|
|
734
|
+
"diagnostic.unavailable": "会话记录暂不可用",
|
|
735
|
+
"duration.seconds": "{seconds}秒",
|
|
736
|
+
"duration.minutes": "{minutes}分{seconds}秒",
|
|
737
|
+
"duration.hours": "{hours}小时{minutes}分{seconds}秒",
|
|
738
|
+
"duration.days": "{days}天",
|
|
739
|
+
"duration.daysHours": "{days}天{hours}小时",
|
|
740
|
+
"duration.months": "约{months}个月",
|
|
741
|
+
"duration.monthsDays": "约{months}个月{days}天",
|
|
742
|
+
"duration.years": "约{years}年",
|
|
743
|
+
"duration.yearsMonths": "约{years}年{months}个月",
|
|
744
|
+
"duration.exactDays": "{days}天{hours}小时{minutes}分{seconds}秒",
|
|
745
|
+
"duration.exactTitle": "总活跃耗时:{duration}",
|
|
746
|
+
"tokens.thousand": "{value}K",
|
|
747
|
+
"tokens.million": "{value}M",
|
|
748
|
+
"tokens.total": "{value} tok",
|
|
749
|
+
"loading.label": "正在加载子代理…",
|
|
750
|
+
"loading.aria": "正在加载子代理",
|
|
751
|
+
"load.error": "无法加载子代理",
|
|
752
|
+
"retry": "重试",
|
|
753
|
+
"mode.oneShot": "一次性",
|
|
754
|
+
"mode.continuable": "可继续",
|
|
755
|
+
"activity.running": "正在运行",
|
|
756
|
+
"activity.inactive": "当前未运行",
|
|
757
|
+
"branch.collapse": "收起 {label} 的下级子代理",
|
|
758
|
+
"branch.expand": "展开 {label} 的下级子代理",
|
|
759
|
+
"count.total.one": "{count} 个子代理",
|
|
760
|
+
"count.total.other": "{count} 个子代理",
|
|
761
|
+
"count.running.one": "{count} 个子代理,正在运行",
|
|
762
|
+
"count.running.other": "{count} 个子代理,正在运行",
|
|
763
|
+
"switcher.aria": "切换子代理:{title}",
|
|
764
|
+
"tree.aria": "子代理会话",
|
|
765
|
+
"readonly.oneShot.title": "一次性子代理记录",
|
|
766
|
+
"readonly.title": "此子代理暂时只读",
|
|
767
|
+
"readonly.oneShot.body": "一次性任务不支持后续消息,可在这里查看完整执行记录。",
|
|
768
|
+
"readonly.body": "父会话当前不在线,重新打开父会话后即可继续发送消息。"
|
|
769
|
+
};
|
|
770
|
+
/** English dictionary, key-identical to the Chinese source of truth. */
|
|
771
|
+
const en = {
|
|
772
|
+
"diagnostic.corrupt": "corrupted session record",
|
|
773
|
+
"diagnostic.unsupported": "unsupported subagent record version",
|
|
774
|
+
"diagnostic.unavailable": "session record temporarily unavailable",
|
|
775
|
+
"duration.seconds": "{seconds}s",
|
|
776
|
+
"duration.minutes": "{minutes}m {seconds}s",
|
|
777
|
+
"duration.hours": "{hours}h {minutes}m {seconds}s",
|
|
778
|
+
"duration.days": "{days}d",
|
|
779
|
+
"duration.daysHours": "{days}d {hours}h",
|
|
780
|
+
"duration.months": "~{months}mo",
|
|
781
|
+
"duration.monthsDays": "~{months}mo {days}d",
|
|
782
|
+
"duration.years": "~{years}y",
|
|
783
|
+
"duration.yearsMonths": "~{years}y {months}mo",
|
|
784
|
+
"duration.exactDays": "{days}d {hours}h {minutes}m {seconds}s",
|
|
785
|
+
"duration.exactTitle": "Total active duration: {duration}",
|
|
786
|
+
"tokens.thousand": "{value}K",
|
|
787
|
+
"tokens.million": "{value}M",
|
|
788
|
+
"tokens.total": "{value} tok",
|
|
789
|
+
"loading.label": "Loading subagents…",
|
|
790
|
+
"loading.aria": "Loading subagents",
|
|
791
|
+
"load.error": "Unable to load subagents",
|
|
792
|
+
"retry": "Retry",
|
|
793
|
+
"mode.oneShot": "one-shot",
|
|
794
|
+
"mode.continuable": "continuable",
|
|
795
|
+
"activity.running": "running",
|
|
796
|
+
"activity.inactive": "not running",
|
|
797
|
+
"branch.collapse": "Collapse {label} descendants",
|
|
798
|
+
"branch.expand": "Expand {label} descendants",
|
|
799
|
+
"count.total.one": "{count} subagent",
|
|
800
|
+
"count.total.other": "{count} subagents",
|
|
801
|
+
"count.running.one": "{count} subagent running",
|
|
802
|
+
"count.running.other": "{count} subagents running",
|
|
803
|
+
"switcher.aria": "Switch subagent: {title}",
|
|
804
|
+
"tree.aria": "Subagent sessions",
|
|
805
|
+
"readonly.oneShot.title": "One-shot subagent record",
|
|
806
|
+
"readonly.title": "This subagent is read-only for now",
|
|
807
|
+
"readonly.oneShot.body": "One-shot tasks do not accept follow-ups; review the full execution record here.",
|
|
808
|
+
"readonly.body": "The parent session is offline; reopen it to continue sending messages."
|
|
809
|
+
};
|
|
810
|
+
//#endregion
|
|
811
|
+
//#region lib/types/client/index.js
|
|
812
|
+
/** Required services for conversation slots and session navigation. */
|
|
813
|
+
const inject = [
|
|
814
|
+
"sessions",
|
|
815
|
+
"slots",
|
|
816
|
+
"locale"
|
|
817
|
+
];
|
|
818
|
+
/** Claim the composer for one-shot history or an unavailable continuation owner. */
|
|
819
|
+
function selectReadOnlySubagent(owner) {
|
|
820
|
+
const subagent = owner.session?.subagent;
|
|
821
|
+
if (subagent === void 0 || subagent === null) return null;
|
|
822
|
+
if (subagent.address.mode === "one-shot") return { reason: "one-shot" };
|
|
823
|
+
if (subagent.parentAvailable !== false) return null;
|
|
824
|
+
return owner.session?.running === true ? null : { reason: "parent-unavailable" };
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Client plugin body: register the subagent catalog and read-only composer seats.
|
|
828
|
+
* @param ctx - client root context.
|
|
829
|
+
*/
|
|
830
|
+
function apply(ctx) {
|
|
831
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
832
|
+
zh,
|
|
833
|
+
en
|
|
834
|
+
}), "ui-subagent: dictionaries");
|
|
835
|
+
const sessions = ctx.sessions;
|
|
836
|
+
const catalogActions = (_parentSessionId) => ({
|
|
837
|
+
openChild(address) {
|
|
838
|
+
sessions.openSubagent(address);
|
|
839
|
+
},
|
|
840
|
+
refresh(parentSessionId) {
|
|
841
|
+
sessions.refreshSubagents(parentSessionId);
|
|
842
|
+
},
|
|
843
|
+
setCatalogOpen(parentSessionId, open) {
|
|
844
|
+
sessions.setSubagentCatalogOpen(parentSessionId, open);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
ctx.slots.inject("conversation.session.header.lineage", () => ctx.slots.register({
|
|
848
|
+
name: "conversation.session.header.lineage",
|
|
849
|
+
locale: NS,
|
|
850
|
+
inject: catalogActions
|
|
851
|
+
}, SubagentHeaderLineage));
|
|
852
|
+
ctx.slots.inject("conversation.composer", () => ctx.slots.register({
|
|
853
|
+
name: "conversation.composer",
|
|
854
|
+
priority: -10,
|
|
855
|
+
locale: NS,
|
|
856
|
+
select: selectReadOnlySubagent
|
|
857
|
+
}, SubagentReadOnlyComposer));
|
|
858
|
+
}
|
|
859
|
+
//#endregion
|
|
860
|
+
exports.apply = apply;
|
|
861
|
+
exports.inject = inject;
|
|
862
|
+
return module.exports;
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
//# sourceMappingURL=client.js.map
|