@senguoyun/dsh-arkme 0.1.57 → 0.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/docs/harness-activity.md +18 -0
  2. package/docs/harness-quick-create.md +13 -0
  3. package/docs/harness-session-dropdown.md +65 -0
  4. package/docs/harness-sidebar-settings.md +9 -0
  5. package/docs/harness-trajectory-navigation.md +28 -0
  6. package/docs/plugin-release.md +1 -1
  7. package/lib/client.js +643 -31
  8. package/lib/client.js.map +1 -1
  9. package/lib/harness-sidebar-client.js +840 -0
  10. package/lib/harness-trajectory-client.js +254 -0
  11. package/lib/index.js +117 -8
  12. package/lib/persistent-extension.js +1 -1
  13. package/lib/{persistent-runtime-BnDj8qcY.js → persistent-runtime-ByM3JB3z.js} +15 -7
  14. package/lib/types/arkme-service.d.ts.map +1 -1
  15. package/lib/types/client/ArkmeCallSurface.d.ts.map +1 -1
  16. package/lib/types/client/ArkmePersistentShell.d.ts.map +1 -1
  17. package/lib/types/client/ArkmeQuickAdd.d.ts +5 -2
  18. package/lib/types/client/ArkmeQuickAdd.d.ts.map +1 -1
  19. package/lib/types/client/ArkmeVirtualWorkspace.d.ts +3 -1
  20. package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
  21. package/lib/types/client/DeepSeekHarnessSurface.d.ts +2 -1
  22. package/lib/types/client/DeepSeekHarnessSurface.d.ts.map +1 -1
  23. package/lib/types/client/frame-menu-dismissal.d.ts +3 -0
  24. package/lib/types/client/frame-menu-dismissal.d.ts.map +1 -0
  25. package/lib/types/client/harness-activity-reporter.d.ts +4 -0
  26. package/lib/types/client/harness-activity-reporter.d.ts.map +1 -0
  27. package/lib/types/client/harness-activity.d.ts +49 -0
  28. package/lib/types/client/harness-activity.d.ts.map +1 -0
  29. package/lib/types/client/harness-new-session.d.ts +3 -0
  30. package/lib/types/client/harness-new-session.d.ts.map +1 -0
  31. package/lib/types/client/harness-session-dropdown.d.ts +9 -0
  32. package/lib/types/client/harness-session-dropdown.d.ts.map +1 -0
  33. package/lib/types/client/harness-session-hover.d.ts +3 -0
  34. package/lib/types/client/harness-session-hover.d.ts.map +1 -0
  35. package/lib/types/client/harness-session-list-default.d.ts +12 -0
  36. package/lib/types/client/harness-session-list-default.d.ts.map +1 -0
  37. package/lib/types/client/harness-session-menu-bridge.d.ts +16 -0
  38. package/lib/types/client/harness-session-menu-bridge.d.ts.map +1 -0
  39. package/lib/types/client/harness-session-summary.d.ts +28 -0
  40. package/lib/types/client/harness-session-summary.d.ts.map +1 -0
  41. package/lib/types/client/harness-sidebar-client.d.ts +5 -0
  42. package/lib/types/client/harness-sidebar-client.d.ts.map +1 -0
  43. package/lib/types/client/harness-trajectory-client.d.ts +5 -0
  44. package/lib/types/client/harness-trajectory-client.d.ts.map +1 -0
  45. package/lib/types/client/harness-trajectory-menu.d.ts +7 -0
  46. package/lib/types/client/harness-trajectory-menu.d.ts.map +1 -0
  47. package/lib/types/client/live-photo-playback.d.ts.map +1 -1
  48. package/lib/types/client/use-harness-activity.d.ts +3 -0
  49. package/lib/types/client/use-harness-activity.d.ts.map +1 -0
  50. package/lib/types/dsh-remote/session-persistence.d.ts +4 -0
  51. package/lib/types/dsh-remote/session-persistence.d.ts.map +1 -0
  52. package/lib/types/dsh-remote/transport-fragment.d.ts.map +1 -1
  53. package/lib/types/harness-embed-contract.d.ts +2 -0
  54. package/lib/types/harness-embed-contract.d.ts.map +1 -1
  55. package/lib/types/harness-embed-route.d.ts +3 -1
  56. package/lib/types/harness-embed-route.d.ts.map +1 -1
  57. package/lib/types/index.d.ts.map +1 -1
  58. package/lib/types/services/call-history-service.d.ts +4 -2
  59. package/lib/types/services/call-history-service.d.ts.map +1 -1
  60. package/lib/types/services/chat-service.d.ts.map +1 -1
  61. package/package.json +5 -5
@@ -0,0 +1,840 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@senguoyun/dsh-arkme/harness-sidebar",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region src/client/harness-session-list-default.ts
9
+ const SESSION_LIST_DEFAULT_MARKER = "arkme.harness.session-list-default.v1";
10
+ function adoptFlatSessionDefault(groupBy, setGroupBy, storage) {
11
+ if (groupBy !== "workspace" && groupBy !== "flat") return false;
12
+ try {
13
+ if (storage.getItem("arkme.harness.session-list-default.v1") === "1") return true;
14
+ } catch {}
15
+ if (groupBy !== "flat") setGroupBy("flat");
16
+ try {
17
+ storage.setItem(SESSION_LIST_DEFAULT_MARKER, "1");
18
+ } catch {}
19
+ return true;
20
+ }
21
+ /**
22
+ * Share the native root-scoped store through the public slot/store contract.
23
+ * A renderless footer contribution receives its real actions and useStore.
24
+ * Do not replace the browser, copy its state, call private store resolvers,
25
+ * or rewrite the native persistence key/schema. Only grouping gets a one-time
26
+ * initial choice; ordering (including an existing manual choice) stays native.
27
+ */
28
+ function installHarnessSessionListDefault(ctx) {
29
+ const slots = ctx.slots;
30
+ if (typeof slots.entriesOfSlot !== "function" || typeof slots.subscribe !== "function" || typeof slots.spec !== "function") return () => {};
31
+ let initialized = false;
32
+ function Defaults({ actions, useStore }) {
33
+ const groupBy = useStore((state) => state.groupBy);
34
+ (0, react.useLayoutEffect)(() => {
35
+ if (initialized) return;
36
+ initialized = adoptFlatSessionDefault(groupBy, actions.setGroupBy, {
37
+ getItem: (key) => window.localStorage.getItem(key),
38
+ setItem: (key, value) => window.localStorage.setItem(key, value)
39
+ });
40
+ }, [actions, groupBy]);
41
+ return null;
42
+ }
43
+ return slots.inject("sidebar.footer.action", () => {
44
+ let mounted;
45
+ let dispose;
46
+ const sync = () => {
47
+ const store = slots.entriesOfSlot("sidebar.workspaces")[0]?.store;
48
+ const next = slots.spec("sidebar.workspaces")?.scope === "root" && slots.spec("sidebar.footer.action")?.scope === "root" && store !== void 0 && typeof store !== "function" && typeof store.create === "function" && typeof store.spec?.actions?.setGroupBy === "function" ? store : void 0;
49
+ if (next === mounted) return;
50
+ dispose?.();
51
+ dispose = void 0;
52
+ mounted = next;
53
+ if (next) dispose = slots.register({
54
+ name: "sidebar.footer.action",
55
+ id: "arkme-session-list-default",
56
+ store: next
57
+ }, Defaults);
58
+ };
59
+ const unsubscribe = slots.subscribe("sidebar.workspaces", sync);
60
+ sync();
61
+ return () => {
62
+ unsubscribe();
63
+ dispose?.();
64
+ };
65
+ });
66
+ }
67
+ //#endregion
68
+ //#region src/client/harness-session-menu-bridge.ts
69
+ /** Private presentation bridge between Arkme and its existing same-origin Harness frame. */
70
+ const HARNESS_MENU_OPEN = "arkme:harness-menu-open";
71
+ const HARNESS_MENU_CLOSE = "arkme:harness-menu-close";
72
+ const HARNESS_MENU_POSITION = "arkme:harness-menu-position";
73
+ //#endregion
74
+ //#region src/client/harness-session-dropdown.ts
75
+ const PREFIX = "data-arkme-session-";
76
+ const HEADER = "[data-slot=\"conversation.session.header\"] > header";
77
+ const SIDEBAR = "[data-slot=\"sidebar\"]";
78
+ const COPY = [{
79
+ expand: "打开侧边栏",
80
+ collapse: "收起侧边栏",
81
+ create: "新建会话",
82
+ blank: "新会话",
83
+ sessions: "会话",
84
+ choose: "切换会话",
85
+ search: "搜索会话",
86
+ view: "视图选项",
87
+ add: "添加工作区"
88
+ }, {
89
+ expand: "Open sidebar",
90
+ collapse: "Collapse sidebar",
91
+ create: "New session",
92
+ blank: "New Session",
93
+ sessions: "Sessions",
94
+ choose: "Switch session",
95
+ search: "Search sessions",
96
+ view: "View options",
97
+ add: "Add workspace"
98
+ }];
99
+ function mark(node, key, value = "") {
100
+ if (node.getAttribute(PREFIX + key) !== value) node.setAttribute(PREFIX + key, value);
101
+ }
102
+ function variable(node, key, value) {
103
+ if (node.style.getPropertyValue(key) !== value) node.style.setProperty(key, value);
104
+ }
105
+ /** Recognize only the native shell contract. Unknown upstream layouts keep their sidebar. */
106
+ function shell(doc) {
107
+ const slot = doc.querySelector(SIDEBAR);
108
+ const column = slot?.parentElement;
109
+ const frame = column?.parentElement;
110
+ const root = slot?.firstElementChild;
111
+ const center = frame?.children[1];
112
+ const right = frame?.children[2];
113
+ const tracks = frame?.style.gridTemplateColumns.match(/^\S+px minmax\(0(?:px)?, 1fr\) (\S+px)$/);
114
+ if (!root || !column || !frame || !center || !tracks || frame.firstElementChild !== column || !right?.hasAttribute("data-rightbar-col") || !root.querySelector("[data-slot=\"sidebar.workspaces\"]")) return;
115
+ const brand = root.firstElementChild;
116
+ const toggle = brand?.querySelector("button[aria-label]:last-child");
117
+ const copy = COPY.find((c) => c.expand === toggle?.getAttribute("aria-label") || c.collapse === toggle?.getAttribute("aria-label"));
118
+ const create = [...root.querySelectorAll(":scope > button[aria-label]")].find((button) => button.getAttribute("aria-label") === copy?.create);
119
+ if (!brand || !toggle || !copy || !create) return;
120
+ return {
121
+ slot,
122
+ column,
123
+ frame,
124
+ root,
125
+ center,
126
+ brand,
127
+ toggle,
128
+ create,
129
+ copy,
130
+ rightTrack: tracks[1]
131
+ };
132
+ }
133
+ /** Recognize the official workspace toolbar without moving any React-owned node. */
134
+ function compactTools(value) {
135
+ const header = (value.root.querySelector("[data-slot=\"sidebar.workspaces\"]")?.firstElementChild)?.firstElementChild;
136
+ if (!header) return [];
137
+ const buttons = [...header.querySelectorAll("button[aria-label]")];
138
+ const search = buttons.find((button) => button.getAttribute("aria-label") === value.copy.search);
139
+ const view = buttons.find((button) => button.getAttribute("aria-label") === value.copy.view);
140
+ const children = [...header.children];
141
+ const searchArea = search && children.find((child) => child.contains(search));
142
+ const actions = view && children.find((child) => child.contains(view));
143
+ const heading = header.firstElementChild;
144
+ if (!searchArea || !actions || searchArea === actions || !heading || heading.tagName !== "SPAN" || !header.querySelector("[data-slot=\"sidebar.workspaces.directoryFlow\"]") || [...actions.querySelectorAll("button")].some((button) => ![value.copy.view, value.copy.add].some((label) => label === button.getAttribute("aria-label")))) return [];
145
+ return [
146
+ [header, "tools-header"],
147
+ [heading, "tools-label"],
148
+ [searchArea, "tools-search"],
149
+ [actions, "tools-actions"]
150
+ ];
151
+ }
152
+ /** Keep the native title and action slot in their React ancestry; only stack their layout. */
153
+ function summaryLayout(header) {
154
+ if (!header || header.getAttribute("aria-hidden") === "true") return [];
155
+ const actions = header.querySelector("[data-slot=\"conversation.session.header.actions\"]")?.parentElement;
156
+ const cluster = actions?.parentElement;
157
+ const row = cluster?.parentElement;
158
+ const nav = cluster?.querySelector(":scope > nav");
159
+ if (!actions || !cluster || !row || !nav || row.parentElement !== header || cluster.children.length !== 2 || cluster.firstElementChild !== nav || cluster.lastElementChild !== actions) return [];
160
+ return [
161
+ [row, "title-row"],
162
+ [cluster, "title-cluster"],
163
+ [actions, "summary"],
164
+ [nav, "title-nav"]
165
+ ];
166
+ }
167
+ /**
168
+ * Presentation-only adapter: the native SidebarRoot, WorkspaceBrowser, their
169
+ * live slots, handlers, menus, stores and React ancestry all stay mounted in
170
+ * place. No copied session list or private API. Only the sidebar's position,
171
+ * the first grid track, and the title trigger are adapted; disposal restores
172
+ * them. There is no public title/zero-width-sidebar slot in DSH 0.1.5-rc.2.
173
+ */
174
+ function installHarnessSessionDropdown(doc) {
175
+ const win = doc.defaultView;
176
+ if (!win || !doc.body) return () => {};
177
+ let current;
178
+ let title;
179
+ let open = false;
180
+ let external;
181
+ let disposed = false;
182
+ let scheduled = false;
183
+ const rowFocusCleanups = /* @__PURE__ */ new Map();
184
+ let toolbarMarks = [];
185
+ let summaryMarks = [];
186
+ let initiallyCollapsed = false;
187
+ let expandedByUs = false;
188
+ let columnBefore = {
189
+ role: null,
190
+ label: null,
191
+ inert: false
192
+ };
193
+ const host = doc.createElement("span");
194
+ mark(host, "anchor");
195
+ const trigger = doc.createElement("button");
196
+ trigger.type = "button";
197
+ trigger.setAttribute("aria-haspopup", "dialog");
198
+ trigger.setAttribute("aria-expanded", "false");
199
+ mark(trigger, "trigger");
200
+ const label = doc.createElement("span");
201
+ const arrow = doc.createElement("span");
202
+ const chevron = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
203
+ chevron.setAttribute("viewBox", "0 0 16 16");
204
+ chevron.setAttribute("width", "14");
205
+ chevron.setAttribute("height", "14");
206
+ chevron.setAttribute("fill", "none");
207
+ const chevronPath = doc.createElementNS("http://www.w3.org/2000/svg", "path");
208
+ chevronPath.setAttribute("d", "m4 6 4 4 4-4");
209
+ chevronPath.setAttribute("stroke", "currentColor");
210
+ chevronPath.setAttribute("stroke-width", "1.5");
211
+ chevronPath.setAttribute("stroke-linecap", "round");
212
+ chevronPath.setAttribute("stroke-linejoin", "round");
213
+ chevron.append(chevronPath);
214
+ arrow.append(chevron);
215
+ arrow.setAttribute("aria-hidden", "true");
216
+ trigger.append(label, arrow);
217
+ host.append(trigger);
218
+ const style = doc.createElement("style");
219
+ mark(style, "style");
220
+ style.textContent = `
221
+ html[${PREFIX}preview], html[${PREFIX}preview] body { background: transparent !important; }
222
+ html[${PREFIX}preview] body { visibility: hidden !important; }
223
+ html[${PREFIX}preview] [${PREFIX}column], html[${PREFIX}preview] [role="menu"],
224
+ html[${PREFIX}preview] [role="dialog"], html[${PREFIX}preview] [role="alertdialog"] { visibility: visible !important; }
225
+ [${PREFIX}frame] { grid-template-columns: 0px minmax(0, 1fr) var(--arkme-session-rightbar) !important; }
226
+ [${PREFIX}frame] > :nth-child(2) { grid-column: 2; }
227
+ [${PREFIX}frame] > [data-rightbar-col] { grid-column: 3; }
228
+ [${PREFIX}frame] > [data-side="sidebar"] { display: none !important; }
229
+ [${PREFIX}column] {
230
+ position: fixed !important; left: var(--arkme-session-left); top: var(--arkme-session-top);
231
+ width: var(--arkme-session-width); height: auto; max-height: var(--arkme-session-height); z-index: 80;
232
+ border: 1px solid var(--dsw-alias-border-l3, #e5e7eb); border-radius: 10px;
233
+ box-shadow: 0 10px 32px #0000001f; box-sizing: border-box; overflow: visible !important;
234
+ display: none !important; pointer-events: none;
235
+ }
236
+ [${PREFIX}column][${PREFIX}open] { display: block !important; pointer-events: auto; }
237
+ [${PREFIX}root] { width: 100% !important; height: auto; max-height: var(--arkme-session-height); border-radius: 9px; padding: 8px 10px !important; position: relative; }
238
+ [${PREFIX}brand], [${PREFIX}native-title] { display: none !important; }
239
+ [${PREFIX}create] { order: 10; flex-shrink: 0; margin: 8px 0 0 !important; }
240
+ [${PREFIX}root][${PREFIX}compact-tools] [${PREFIX}create] { width: calc(100% - var(--arkme-session-tools-width) - 8px); }
241
+ [${PREFIX}tools-header] { display: contents !important; }
242
+ [${PREFIX}tools-label], [${PREFIX}tools-search] { display: none !important; }
243
+ [${PREFIX}tools-actions] {
244
+ position: absolute !important; bottom: 8px; right: 10px; height: 38px;
245
+ display: flex !important; align-items: center; gap: 4px; max-width: none !important;
246
+ opacity: 1 !important; visibility: visible !important; transform: none !important;
247
+ }
248
+ [${PREFIX}anchor] { display: inline-flex; width: max-content; min-width: 0; max-width: 100%; }
249
+ [${PREFIX}title-row] { align-items: flex-start; }
250
+ [${PREFIX}title-cluster] { flex-direction: column; align-items: stretch; gap: 4px; }
251
+ [${PREFIX}title-nav] { width: 100%; }
252
+ [${PREFIX}summary] { min-width: 0; min-height: 18px; gap: 0; flex-wrap: wrap; color: var(--dsw-alias-label-secondary, #626872); font-size: 12px; line-height: 18px; }
253
+ [${PREFIX}summary] [data-slot="conversation.session.header.actions"] > * { font-size: inherit; line-height: inherit; }
254
+ [${PREFIX}turn-count] { display: inline-flex; align-items: center; white-space: nowrap; }
255
+ [${PREFIX}summary] [data-slot="conversation.session.header.actions"] > * + [${PREFIX}turn-count]::before { content: '·'; margin: 0 7px; }
256
+ [${PREFIX}anchor][${PREFIX}fallback] { width: max-content; position: absolute; top: 10px; left: 20px; z-index: 10; max-width: calc(100% - 40px); }
257
+ [${PREFIX}trigger] {
258
+ display: inline-flex; align-items: stretch; gap: 0; min-width: 0; max-width: 100%; height: 30px;
259
+ border: 1px solid var(--dsw-alias-border-l3, #d4d6da); border-radius: 10px; padding: 0; margin-left: 0; box-sizing: border-box;
260
+ color: var(--dsw-alias-label-primary, inherit); background: var(--dsw-alias-button-elevated-fill, #fff); font: inherit;
261
+ font-size: 16px; font-weight: 600; line-height: 28px; overflow: hidden; cursor: pointer;
262
+ }
263
+ [${PREFIX}trigger] > span:first-child { padding: 0 10px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
264
+ [${PREFIX}trigger] > span:last-child {
265
+ color: var(--dsw-alias-label-secondary, #626872); flex: none; width: 28px;
266
+ border-left: 1px solid var(--dsw-alias-border-l3, #d4d6da); display: flex; align-items: center; justify-content: center;
267
+ }
268
+ [${PREFIX}trigger][aria-expanded="true"] svg { transform: rotate(180deg); }
269
+ [${PREFIX}trigger]:hover, [${PREFIX}trigger][aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover, #f3f4f6); }
270
+ [${PREFIX}trigger]:focus-visible, [${PREFIX}column] [role="treeitem"]:focus-visible { outline: 2px solid #4c70ef; outline-offset: -2px; }
271
+ `;
272
+ doc.head.append(style);
273
+ function close(focus = false) {
274
+ if (!open) return;
275
+ const request = external;
276
+ external = void 0;
277
+ open = false;
278
+ current?.column.removeAttribute("data-arkme-session-open");
279
+ if (current) current.column.inert = true;
280
+ trigger.setAttribute("aria-expanded", "false");
281
+ if (request) request.onClose(focus);
282
+ else if (focus && trigger.isConnected) trigger.focus();
283
+ }
284
+ function restore() {
285
+ close();
286
+ for (const cleanup of rowFocusCleanups.values()) cleanup();
287
+ for (const [node, key] of toolbarMarks) node.removeAttribute(PREFIX + key);
288
+ toolbarMarks = [];
289
+ for (const [node, key] of summaryMarks) node.removeAttribute(PREFIX + key);
290
+ summaryMarks = [];
291
+ title?.removeAttribute("data-arkme-session-native-title");
292
+ title = void 0;
293
+ host.remove();
294
+ if (current) {
295
+ const { frame, column, root, brand, create, toggle } = current;
296
+ for (const [node, key] of [
297
+ [frame, "frame"],
298
+ [column, "column"],
299
+ [root, "root"],
300
+ [brand, "brand"],
301
+ [create, "create"]
302
+ ]) node.removeAttribute(PREFIX + key);
303
+ frame.style.removeProperty("--arkme-session-rightbar");
304
+ root.removeAttribute("data-arkme-session-compact-tools");
305
+ root.style.removeProperty("--arkme-session-tools-width");
306
+ for (const key of [
307
+ "left",
308
+ "top",
309
+ "width",
310
+ "height"
311
+ ]) column.style.removeProperty("--arkme-session-" + key);
312
+ column.inert = columnBefore.inert;
313
+ for (const [key, value] of [["role", columnBefore.role], ["aria-label", columnBefore.label]]) if (value === null) column.removeAttribute(key);
314
+ else column.setAttribute(key, value);
315
+ if (expandedByUs && initiallyCollapsed && toggle.isConnected && toggle.getAttribute("aria-label") === current.copy.collapse) toggle.click();
316
+ }
317
+ current = void 0;
318
+ expandedByUs = false;
319
+ }
320
+ function position() {
321
+ if (!current) return;
322
+ const rect = trigger.getBoundingClientRect();
323
+ const width = Math.min(320, win.innerWidth - 24);
324
+ const anchor = external?.anchor();
325
+ const top = anchor ? Math.max(12, Math.min(anchor.top, win.innerHeight - Math.min(560, win.innerHeight - 24) - 12)) : Math.max(8, Math.min(rect.bottom + 7, win.innerHeight - 100));
326
+ variable(current.column, "--arkme-session-left", `${Math.max(12, Math.min(anchor?.left ?? rect.left, win.innerWidth - width - 12))}px`);
327
+ variable(current.column, "--arkme-session-top", `${top}px`);
328
+ variable(current.column, "--arkme-session-width", `${width}px`);
329
+ variable(current.column, "--arkme-session-height", `${Math.min(560, win.innerHeight - top - 12)}px`);
330
+ external?.onLayout();
331
+ }
332
+ function sync() {
333
+ scheduled = false;
334
+ if (disposed) return;
335
+ const next = shell(doc);
336
+ if (!next) {
337
+ restore();
338
+ return;
339
+ }
340
+ if (current?.root !== next.root || current.frame !== next.frame) {
341
+ restore();
342
+ current = next;
343
+ columnBefore = {
344
+ role: next.column.getAttribute("role"),
345
+ label: next.column.getAttribute("aria-label"),
346
+ inert: next.column.inert === true
347
+ };
348
+ initiallyCollapsed = next.toggle.getAttribute("aria-label") === next.copy.expand;
349
+ current.column.inert = true;
350
+ current.column.setAttribute("role", "dialog");
351
+ current.column.setAttribute("aria-label", next.copy.choose);
352
+ } else current = next;
353
+ mark(next.frame, "frame");
354
+ mark(next.column, "column");
355
+ mark(next.root, "root");
356
+ mark(next.brand, "brand");
357
+ mark(next.create, "create");
358
+ const nextTools = compactTools(next);
359
+ for (const [node, key] of toolbarMarks) if (!nextTools.some(([nextNode, nextKey]) => node === nextNode && key === nextKey)) node.removeAttribute(PREFIX + key);
360
+ toolbarMarks = nextTools;
361
+ for (const [node, key] of toolbarMarks) mark(node, key);
362
+ const tools = toolbarMarks.find(([, key]) => key === "tools-actions")?.[0];
363
+ if (tools) {
364
+ mark(next.root, "compact-tools");
365
+ const count = tools.querySelectorAll("button").length;
366
+ variable(next.root, "--arkme-session-tools-width", `${count * 28 + Math.max(0, count - 1) * 4}px`);
367
+ } else {
368
+ next.root.removeAttribute("data-arkme-session-compact-tools");
369
+ next.root.style.removeProperty("--arkme-session-tools-width");
370
+ }
371
+ variable(next.frame, "--arkme-session-rightbar", next.rightTrack);
372
+ const header = next.center.querySelector(HEADER);
373
+ const nextSummary = summaryLayout(header ?? void 0);
374
+ for (const [node, key] of summaryMarks) if (!nextSummary.some(([nextNode, nextKey]) => node === nextNode && key === nextKey)) node.removeAttribute(PREFIX + key);
375
+ summaryMarks = nextSummary;
376
+ for (const [node, key] of summaryMarks) mark(node, key);
377
+ const visibleHeader = header && header.getAttribute("aria-hidden") !== "true";
378
+ const nativeTitle = visibleHeader ? header?.querySelector("nav > :last-child > button:disabled") : void 0;
379
+ if (title !== nativeTitle) {
380
+ title?.removeAttribute("data-arkme-session-native-title");
381
+ title = nativeTitle ?? void 0;
382
+ }
383
+ if (title?.parentElement) {
384
+ mark(title, "native-title");
385
+ host.removeAttribute("data-arkme-session-fallback");
386
+ if (host.parentElement !== title.parentElement) title.before(host);
387
+ } else if (visibleHeader && header.querySelector("nav")) {
388
+ const nav = header.querySelector("nav");
389
+ host.removeAttribute("data-arkme-session-fallback");
390
+ if (host.parentElement !== nav) nav.prepend(host);
391
+ } else {
392
+ mark(host, "fallback");
393
+ if (host.parentElement !== next.center) next.center.append(host);
394
+ }
395
+ const text = title?.textContent?.trim() || (visibleHeader ? next.copy.sessions : next.copy.blank);
396
+ if (label.textContent !== text) label.textContent = text;
397
+ trigger.title = text;
398
+ trigger.setAttribute("aria-label", `${next.copy.choose}:${text}`);
399
+ position();
400
+ }
401
+ function schedule() {
402
+ if (disposed || scheduled) return;
403
+ scheduled = true;
404
+ win.queueMicrotask(sync);
405
+ }
406
+ function show(request) {
407
+ if (!current) return;
408
+ if (current.toggle.getAttribute("aria-label") === current.copy.expand) {
409
+ current.toggle.click();
410
+ expandedByUs = true;
411
+ }
412
+ open = true;
413
+ external = request;
414
+ current.column.inert = false;
415
+ mark(current.column, "open");
416
+ trigger.setAttribute("aria-expanded", request ? "false" : "true");
417
+ if (request) request.accepted = true;
418
+ position();
419
+ win.requestAnimationFrame(() => current?.column.querySelector("[role=\"treeitem\"][aria-selected=\"true\"]")?.scrollIntoView({ block: "nearest" }));
420
+ }
421
+ trigger.addEventListener("click", () => {
422
+ if (open && !external) {
423
+ close();
424
+ return;
425
+ }
426
+ close();
427
+ show();
428
+ });
429
+ function externalOpen(event) {
430
+ const request = event.detail;
431
+ if (!request || !current) return;
432
+ close();
433
+ show(request);
434
+ }
435
+ function externalClose() {
436
+ if (!external) return;
437
+ for (const overlay of doc.querySelectorAll("[role=\"menu\"], [role=\"alertdialog\"], [role=\"dialog\"]:not([data-arkme-session-column])")) overlay.dispatchEvent(new win.KeyboardEvent("keydown", {
438
+ key: "Escape",
439
+ bubbles: true
440
+ }));
441
+ close();
442
+ }
443
+ function pointer(event) {
444
+ if (!open || !current) return;
445
+ const target = event.target;
446
+ if (external && target?.ownerDocument !== doc) return;
447
+ if (target && (host.contains(target) || current.column.contains(target))) return;
448
+ if (target?.closest?.("[role=\"menu\"], [role=\"dialog\"], [role=\"alertdialog\"]")) return;
449
+ close();
450
+ }
451
+ function click(event) {
452
+ if (!open || !current) return;
453
+ const target = event.target;
454
+ if (!target || !current.column.contains(target)) return;
455
+ const row = target.closest("[role=\"treeitem\"][aria-selected]");
456
+ const buttonLabel = target.closest("button")?.getAttribute("aria-label") ?? "";
457
+ const workspaceCreate = /^在“.+”中新建会话$/.test(buttonLabel) || /^New session in /.test(buttonLabel);
458
+ if (row && !target.closest("button, input, [role=\"menu\"]") || current.create.contains(target) || workspaceCreate || target.closest("nav button")) {
459
+ const request = external;
460
+ close(!request);
461
+ request?.onSelect();
462
+ }
463
+ }
464
+ function key(event) {
465
+ if (!open || !current) return;
466
+ const target = event.target;
467
+ if (doc.querySelector("[role=\"menu\"], [role=\"alertdialog\"], [role=\"dialog\"]:not([data-arkme-session-column])")) return;
468
+ if (event.key === "Escape") {
469
+ event.preventDefault();
470
+ close(true);
471
+ return;
472
+ }
473
+ if (!target || target.matches("input, textarea, [contenteditable=\"true\"]")) return;
474
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
475
+ if (!host.contains(target) && !current.column.contains(target)) return;
476
+ const rows = [...current.column.querySelectorAll("[role=\"treeitem\"][aria-selected]")].filter((row) => row.getClientRects().length > 0);
477
+ if (!rows.length) return;
478
+ const index = rows.findIndex((row) => row === target);
479
+ const next = rows[index < 0 ? event.key === "ArrowDown" ? 0 : rows.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + rows.length) % rows.length];
480
+ if (!next.hasAttribute("tabindex")) {
481
+ next.tabIndex = -1;
482
+ const remove = () => {
483
+ next.removeAttribute("tabindex");
484
+ next.removeEventListener("blur", remove);
485
+ rowFocusCleanups.delete(next);
486
+ };
487
+ rowFocusCleanups.set(next, remove);
488
+ next.addEventListener("blur", remove);
489
+ }
490
+ next.focus();
491
+ next.scrollIntoView({ block: "nearest" });
492
+ event.preventDefault();
493
+ }
494
+ function rowKey(event) {
495
+ const target = event.target;
496
+ if (open && (event.key === "Enter" || event.key === " ") && target?.matches("[role=\"treeitem\"][aria-selected]") && current?.column.contains(target)) {
497
+ event.preventDefault();
498
+ target.click();
499
+ }
500
+ }
501
+ const observer = new win.MutationObserver(schedule);
502
+ observer.observe(doc.body, {
503
+ childList: true,
504
+ subtree: true,
505
+ characterData: true,
506
+ attributes: true,
507
+ attributeFilter: [
508
+ "class",
509
+ "style",
510
+ "aria-hidden",
511
+ "aria-selected",
512
+ "disabled",
513
+ "data-slot"
514
+ ]
515
+ });
516
+ doc.addEventListener("pointerdown", pointer, true);
517
+ doc.addEventListener("click", click);
518
+ doc.addEventListener("keydown", key);
519
+ doc.addEventListener("keydown", rowKey);
520
+ doc.addEventListener(HARNESS_MENU_OPEN, externalOpen);
521
+ doc.addEventListener(HARNESS_MENU_CLOSE, externalClose);
522
+ doc.addEventListener(HARNESS_MENU_POSITION, position);
523
+ let parentDoc;
524
+ try {
525
+ if (win.parent !== win) parentDoc = win.parent.document;
526
+ } catch {}
527
+ parentDoc?.addEventListener("pointerdown", pointer, true);
528
+ win.addEventListener("resize", schedule);
529
+ sync();
530
+ return () => {
531
+ disposed = true;
532
+ observer.disconnect();
533
+ doc.removeEventListener("pointerdown", pointer, true);
534
+ doc.removeEventListener("click", click);
535
+ doc.removeEventListener("keydown", key);
536
+ doc.removeEventListener("keydown", rowKey);
537
+ doc.removeEventListener(HARNESS_MENU_OPEN, externalOpen);
538
+ doc.removeEventListener(HARNESS_MENU_CLOSE, externalClose);
539
+ doc.removeEventListener(HARNESS_MENU_POSITION, position);
540
+ parentDoc?.removeEventListener("pointerdown", pointer, true);
541
+ win.removeEventListener("resize", schedule);
542
+ restore();
543
+ style.remove();
544
+ };
545
+ }
546
+ //#endregion
547
+ //#region src/client/harness-session-summary.ts
548
+ const SLOT = "conversation.session.header.actions";
549
+ const LOCALE = "arkme.harness.sessionSummary";
550
+ /** Missing/changed projection schemas must not be presented as zero conversations. */
551
+ function sessionTurnCount(value) {
552
+ if (typeof value !== "object" || value === null || !("turns" in value)) return;
553
+ const turns = value.turns;
554
+ return typeof turns === "number" && Number.isSafeInteger(turns) && turns >= 0 ? turns : void 0;
555
+ }
556
+ function SessionTurnCount({ useProjection, t }) {
557
+ const turns = sessionTurnCount(useProjection("sessionStats"));
558
+ if (turns === void 0) return null;
559
+ return (0, react.createElement)("span", {
560
+ "data-arkme-session-turn-count": "",
561
+ "data-turns": turns
562
+ }, t("count", { count: turns }));
563
+ }
564
+ /** Add a session-scoped reader; paging, compaction and push updates stay host-owned. */
565
+ function installHarnessSessionSummary(ctx) {
566
+ if (typeof ctx.locale?.register !== "function" || typeof ctx.slots.spec !== "function") return () => {};
567
+ const removeLocale = ctx.locale.register(LOCALE, {
568
+ zh: { count: "{count} 次对话" },
569
+ en: { count: "{count} turns" }
570
+ });
571
+ const removeSlot = ctx.slots.inject(SLOT, () => {
572
+ const spec = ctx.slots.spec(SLOT);
573
+ if (spec?.kind !== "list" || spec.scope !== "session") return () => {};
574
+ return ctx.slots.register({
575
+ name: SLOT,
576
+ id: "arkme-session-turn-count",
577
+ order: 0,
578
+ locale: LOCALE
579
+ }, SessionTurnCount);
580
+ });
581
+ return () => {
582
+ removeSlot();
583
+ removeLocale();
584
+ };
585
+ }
586
+ //#endregion
587
+ //#region src/client/harness-activity.ts
588
+ /** Display-only account-scoped summary. Official sessions remain the source of running/pending state. */
589
+ const HARNESS_ACTIVITY_ATTRIBUTE = "data-arkme-harness-activity";
590
+ const HARNESS_ACTIVITY_EVENT = "arkme:harness-activity";
591
+ const pendingKinds = /* @__PURE__ */ new Set([
592
+ "approval",
593
+ "plan-review",
594
+ "question"
595
+ ]);
596
+ /** Only unread reminder ids are local; no copied sessions, histories or execution states. */
597
+ var HarnessActivityTracker = class {
598
+ scope;
599
+ storage;
600
+ unread = /* @__PURE__ */ new Set();
601
+ previous = /* @__PURE__ */ new Map();
602
+ key;
603
+ persisted = "";
604
+ constructor(scope, storage) {
605
+ this.scope = scope;
606
+ this.storage = storage;
607
+ this.key = `arkme.harness.unviewed.v1:${scope}`;
608
+ try {
609
+ const saved = JSON.parse(storage?.getItem(this.key) ?? "[]");
610
+ if (Array.isArray(saved)) this.unread = new Set(saved.filter((id) => typeof id === "string" && id.length > 0 && id.length <= 512));
611
+ } catch {}
612
+ this.persisted = JSON.stringify([...this.unread].sort());
613
+ }
614
+ update(list, archived, pending, visibleSession) {
615
+ const result = {
616
+ scope: this.scope,
617
+ running: [],
618
+ unread: [],
619
+ pending: []
620
+ };
621
+ if (list.phase !== "ready") return result;
622
+ const current = list.current === void 0 ? void 0 : list.byId[list.current];
623
+ if (current) result.current = {
624
+ id: current.id,
625
+ title: current.blank ? "新会话" : current.displayTitle || "未命名对话"
626
+ };
627
+ else if (list.current === void 0) result.current = null;
628
+ const hidden = new Set(archived);
629
+ const groups = /* @__PURE__ */ new Map();
630
+ for (const id of list.ids) {
631
+ const session = list.byId[id];
632
+ if (!session || session.origin === "subagent" || hidden.has(id) || session.blank) continue;
633
+ groups.set(id, {
634
+ item: {
635
+ id,
636
+ title: session.displayTitle || "未命名对话"
637
+ },
638
+ running: false,
639
+ pending: false,
640
+ completed: session.completed === true
641
+ });
642
+ }
643
+ for (const session of Object.values(list.byId)) {
644
+ let root = session;
645
+ const seen = /* @__PURE__ */ new Set();
646
+ while (root?.origin === "subagent" && root.parentId && !seen.has(root.id)) {
647
+ seen.add(root.id);
648
+ root = list.byId[root.parentId];
649
+ }
650
+ const group = root && groups.get(root.id);
651
+ if (!group) continue;
652
+ group.running ||= session.running === true;
653
+ const kind = pending === void 0 ? session.pendingInteraction : pending.get(session.id)?.kind;
654
+ group.pending ||= kind !== void 0 && pendingKinds.has(kind);
655
+ }
656
+ for (const id of this.unread) if (!groups.has(id)) this.unread.delete(id);
657
+ for (const [id, group] of groups) {
658
+ const active = group.running || group.pending;
659
+ const previous = this.previous.get(id);
660
+ if (active) this.unread.delete(id);
661
+ else if (previous?.active || group.completed && !previous?.completed) this.unread.add(id);
662
+ if (visibleSession === id) this.unread.delete(id);
663
+ this.previous.set(id, {
664
+ active,
665
+ completed: group.completed
666
+ });
667
+ if (group.pending) result.pending.push(group.item);
668
+ else if (group.running) result.running.push(group.item);
669
+ else if (this.unread.has(id)) result.unread.push(group.item);
670
+ }
671
+ for (const id of this.previous.keys()) if (!groups.has(id)) this.previous.delete(id);
672
+ const saved = JSON.stringify([...this.unread].sort());
673
+ if (saved !== this.persisted) {
674
+ this.persisted = saved;
675
+ try {
676
+ this.storage?.setItem(this.key, saved);
677
+ } catch {}
678
+ }
679
+ return result;
680
+ }
681
+ };
682
+ //#endregion
683
+ //#region src/client/harness-activity-reporter.ts
684
+ /** A renderless public-slot reader bridges the native iframe's actual state to the Arkme row. */
685
+ function installHarnessActivityReporter(ctx, surface, doc = document) {
686
+ if (typeof ctx.slots.spec !== "function") return () => {};
687
+ const parentDoc = surface.ownerDocument;
688
+ let tracker;
689
+ let latest;
690
+ const notify = () => parentDoc.dispatchEvent(new parentDoc.defaultView.Event(HARNESS_ACTIVITY_EVENT));
691
+ const clear = () => {
692
+ if (!surface.hasAttribute("data-arkme-harness-activity")) return;
693
+ surface.removeAttribute(HARNESS_ACTIVITY_ATTRIBUTE);
694
+ notify();
695
+ };
696
+ const sync = () => {
697
+ const scope = surface.getAttribute("data-arkme-account-scope");
698
+ if (!scope || !surface.getAttribute("data-arkme-account-id")) {
699
+ tracker = void 0;
700
+ clear();
701
+ return;
702
+ }
703
+ if (!latest) return;
704
+ if (tracker?.scope !== scope) {
705
+ let storage;
706
+ try {
707
+ storage = doc.defaultView?.localStorage;
708
+ } catch {}
709
+ tracker = new HarnessActivityTracker(scope, storage);
710
+ }
711
+ const visible = surface.getAttribute("data-arkme-visible") === "true" && surface.getAttribute("data-arkme-follow-session") === "true" && parentDoc.visibilityState === "visible" && parentDoc.hasFocus();
712
+ const value = tracker.update(latest.list, latest.archived, latest.pending, visible ? latest.list.current : void 0);
713
+ const raw = JSON.stringify(value);
714
+ if (surface.getAttribute("data-arkme-harness-activity") !== raw) {
715
+ surface.setAttribute(HARNESS_ACTIVITY_ATTRIBUTE, raw);
716
+ notify();
717
+ }
718
+ };
719
+ function Publish(props) {
720
+ (0, react.useLayoutEffect)(() => {
721
+ latest = props;
722
+ sync();
723
+ }, [
724
+ props.list,
725
+ props.archived,
726
+ props.pending
727
+ ]);
728
+ return null;
729
+ }
730
+ function WithPending({ usePending, ...props }) {
731
+ const pending = usePending((value) => value);
732
+ return (0, react.createElement)(Publish, {
733
+ ...props,
734
+ pending
735
+ });
736
+ }
737
+ function Reporter({ useSessions, useWorkspaces, useSessionPendingInteraction }) {
738
+ const list = useSessions((value) => value);
739
+ const archived = useWorkspaces((value) => value.archivedSessionIds);
740
+ return useSessionPendingInteraction ? (0, react.createElement)(WithPending, {
741
+ list,
742
+ archived,
743
+ usePending: useSessionPendingInteraction
744
+ }) : (0, react.createElement)(Publish, {
745
+ list,
746
+ archived,
747
+ pending: void 0
748
+ });
749
+ }
750
+ const observer = new MutationObserver(sync);
751
+ observer.observe(surface, {
752
+ attributes: true,
753
+ attributeFilter: [
754
+ "data-arkme-visible",
755
+ "data-arkme-follow-session",
756
+ "data-arkme-account-id",
757
+ "data-arkme-account-scope"
758
+ ]
759
+ });
760
+ parentDoc.addEventListener("visibilitychange", sync);
761
+ parentDoc.defaultView?.addEventListener("focus", sync);
762
+ parentDoc.defaultView?.addEventListener("blur", sync);
763
+ const remove = ctx.slots.inject("sidebar.footer.action", () => {
764
+ const spec = ctx.slots.spec("sidebar.footer.action");
765
+ if (spec?.kind !== "list" || spec.scope !== "root") return () => {};
766
+ return ctx.slots.register({
767
+ name: "sidebar.footer.action",
768
+ id: "arkme-harness-activity",
769
+ order: 100
770
+ }, Reporter);
771
+ });
772
+ return () => {
773
+ remove();
774
+ observer.disconnect();
775
+ parentDoc.removeEventListener("visibilitychange", sync);
776
+ parentDoc.defaultView?.removeEventListener("focus", sync);
777
+ parentDoc.defaultView?.removeEventListener("blur", sync);
778
+ clear();
779
+ };
780
+ }
781
+ //#endregion
782
+ //#region src/client/harness-sidebar-client.ts
783
+ const inject = ["slots", "locale"];
784
+ /** The outer Arkme shell owns authentication and the avatar settings entry. */
785
+ function apply(ctx) {
786
+ ctx.effect(() => {
787
+ const surface = window.frameElement?.parentElement;
788
+ if (surface?.getAttribute("data-arkme-owned") !== "deepseek-harness-surface") return () => void 0;
789
+ let restoreSettings;
790
+ let restoreDropdown;
791
+ let restoreDefault;
792
+ let restoreSummary;
793
+ let restoreActivity;
794
+ const sync = () => {
795
+ const accountId = Number(surface.getAttribute("data-arkme-account-id"));
796
+ const authenticated = Number.isSafeInteger(accountId) && accountId > 0;
797
+ if (authenticated && restoreSettings === void 0) {
798
+ restoreSettings = ctx.slots.inject("sidebar.settings", () => ctx.slots.register({
799
+ name: "sidebar.settings",
800
+ priority: -100
801
+ }, () => null));
802
+ restoreDefault = installHarnessSessionListDefault(ctx);
803
+ restoreSummary = installHarnessSessionSummary(ctx);
804
+ restoreActivity = installHarnessActivityReporter(ctx, surface);
805
+ restoreDropdown = installHarnessSessionDropdown(document);
806
+ } else if (!authenticated) {
807
+ restoreActivity?.();
808
+ restoreActivity = void 0;
809
+ restoreSummary?.();
810
+ restoreSummary = void 0;
811
+ restoreDefault?.();
812
+ restoreDefault = void 0;
813
+ restoreDropdown?.();
814
+ restoreDropdown = void 0;
815
+ restoreSettings?.();
816
+ restoreSettings = void 0;
817
+ }
818
+ };
819
+ const observer = new MutationObserver(sync);
820
+ observer.observe(surface, {
821
+ attributes: true,
822
+ attributeFilter: ["data-arkme-account-id"]
823
+ });
824
+ sync();
825
+ return () => {
826
+ observer.disconnect();
827
+ restoreActivity?.();
828
+ restoreSummary?.();
829
+ restoreDefault?.();
830
+ restoreDropdown?.();
831
+ restoreSettings?.();
832
+ };
833
+ }, "arkme: native sidebar settings follow account state");
834
+ }
835
+ //#endregion
836
+ exports.apply = apply;
837
+ exports.inject = inject;
838
+ return module.exports;
839
+ }
840
+ });