dsh-session-toolbox 0.1.10 → 0.1.12

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/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client.jsx", "../package.json", "../src/draft-vault.js"],
4
- "sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u5F52\u6863\u5230\u5F52\u6863\u6C60' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Archive' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u5F52\u6863\u6C60 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\t// Known projects, so the target can be picked by name instead of typed as a path.\n\tconst projects = state?.projects ?? [];\n\t// Which project the typed session currently belongs to (computed host-side).\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => {\n\t\tif (!sessionId) { setFromProject(null); return; }\n\t\tlet alive = true;\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\tonChange={e => { setToCwd(e.target.value); setPlan(null); }}>\n\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t)}\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t{sessionId && (\n\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\u8BE5\u4F1A\u8BDD\uFF0C\u6216\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u5F52\u6863\u6C60</h3>\n\t\t\t<p className=\"muted\">\n\t\t\t\tDSH \u6CA1\u6709\u72EC\u7ACB\u7684\u300C\u5220\u9664\u4F1A\u8BDD\u300D\u2014\u2014\u4F1A\u8BDD\u53EA\u80FD<b>\u5F52\u6863</b>\u3002\u8FD9\u91CC\u5C31\u662F\u5B98\u65B9\u7684\u5F52\u6863\u6C60\uFF1A\u5165\u6C60\u7684\u4F1A\u8BDD\u4ECD\u7136\u662F\u5B98\u65B9\u5F52\u6863\u72B6\u6001\uFF08\u4FA7\u680F\u7684\u5F52\u6863\u89C6\u56FE\u91CC\u540C\u6837\u80FD\u770B\u5230\uFF09\uFF0C\u672C\u63D2\u4EF6\u989D\u5916\u8BB0\u5F55\u4E86\u5B83\u5165\u6C60\u65F6\u7684<b>\u9879\u76EE\u5F52\u5C5E\u3001\u5217\u8868\u4F4D\u7F6E\u4E0E\u7F6E\u9876\u72B6\u6001</b>\uFF0C\u56E0\u6B64\u53EF\u4EE5\u4E00\u952E\u8FD8\u539F\u5230\u539F\u4F4D\u3002\n\t\t\t\t\u5F52\u6863\u9879\u76EE\u5219\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u53EA\u79FB\u9664\u9879\u76EE\u6CE8\u518C\u8868\u9879\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5165\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002\n\t\t\t</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u6C60\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\u5230\u539F\u4F4D').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u79FB\u51FA{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u5F52\u6863\uFF1F\uFF08\u4F1A\u8BDD\u5C06\u8FDB\u5165\u5F52\u6863\u6C60\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u5F52\u6863\u5230\u5F52\u6863\u6C60'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\t// Same project picker as the panel: the dialog is what the context menu opens,\n\t// so it must not fall back to typing a path by hand.\n\tconst [projects, setProjects] = useState([]);\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tuseEffect(() => {\n\t\tlet alive = true;\n\t\tvoid loadState().then(next => { if (alive) setProjects(next?.projects ?? []); }).catch(() => {});\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry { await callHost({ type: 'move', sessionId, toCwd, createTarget }); close(); }\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t\t</p>\n\t\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\t\tonChange={e => setToCwd(e.target.value)}>\n\t\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t)}\n\t\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.10\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt: sentAt ?? 0 }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), sentAt: 0, seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\tfunction attachmentRail(editor) {\n\t\t// Official structure: attachment cards are imgs with blob: srcs sitting\n\t\t// above the contenteditable root inside the composer bar subtree.\n\t\tconst bar = editor?.closest?.('[data-composer-bar], [data-conversation-region=\"composer\"]') ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (bar?.querySelectorAll === undefined) return undefined;\n\t\treturn [...bar.querySelectorAll('img')].filter(img => img.getAttribute?.('src')?.startsWith('blob:'));\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\t// Never resurrect images that already travelled with a sent message: that is\n\t\t// what duplicated screenshots whenever a session was switched back to.\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId && !record.sentAt);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\t// A restore hands the images back to the official composer; they now live\n\t\t// there (and will be retired when sent), so keep a single copy in the vault.\n\t\tif (accepted) for (const record of pending) await putRecord({ ...record, seeded: true });\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\t/**\n\t * Mark a session's backed-up drafts as already sent, so they are never\n\t * replayed into the composer again.\n\t *\n\t * Inferring \"sent\" from the attachment rail reaching zero used to miss the\n\t * common case: the rail is already empty by the time the session mounts\n\t * again, so the transition never happened while we were watching and the\n\t * old images were restored on every switch. Observing the composer while it\n\t * is mounted (below) plus this explicit retirement keeps the vault honest.\n\t */\n\tasync function retireSession(sessionId, reason) {\n\t\tlet retired = 0;\n\t\tfor (const record of await allRecords()) {\n\t\t\tif (record.sessionId !== sessionId || record.sentAt) continue;\n\t\t\tawait putRecord({ ...record, sentAt: now() });\n\t\t\tretired += 1;\n\t\t}\n\t\tif (retired > 0) {\n\t\t\tnotice(reason === 'sent'\n\t\t\t\t? `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u968F\u6D88\u606F\u53D1\u9001\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`\n\t\t\t\t: `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u6E05\u7A7A\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`);\n\t\t\tvoid syncHost();\n\t\t}\n\t\tchanged();\n\t\treturn retired;\n\t}\n\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Cards disappeared while the composer was mounted: the message was\n\t\t\t\t// sent (or the user removed them), so retire the backups for good.\n\t\t\t\tawait retireSession(sessionId, 'sent');\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\n\t\t// Belt and braces: listen for the send gesture itself, so a fast unmount\n\t\t// (enter then immediately switch away) still retires the drafts.\n\t\tconst onKeyDown = event => {\n\t\t\tif (event.key !== 'Enter' || event.shiftKey || event.isComposing) return;\n\t\t\tconst target = event.target;\n\t\t\tif (target?.closest?.('[data-conversation-session]') === null || target?.closest?.('[data-conversation-session]') === undefined) return;\n\t\t\tif ((attachmentRail(editor)?.length ?? 0) > 0) void retireSession(sessionId, 'sent');\n\t\t};\n\t\tdocument.addEventListener('keydown', onKeyDown, true);\n\t\treturn () => { observer.disconnect(); document.removeEventListener('keydown', onKeyDown, true); };\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\t/** Retire a session's unsent drafts (called when its images were sent). */\n\t\tretire: async (sessionId = currentSession(), reason = 'sent') => retireSession(sessionId, reason),\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\n/**\n * Broadcast a one-line notice to the toolbox page after the dialog closes.\n *\n * The context menu opens MoveDialog outside the page tree, so a local setState\n * cannot reach the panel; a window event keeps the message visible where the\n * user can act on it without coupling the two components.\n */\nexport const NOTICE_EVENT = 'dsh-session-toolbox:notice';\nfunction dispatchNotice(message) {\n\ttry { window.dispatchEvent(new CustomEvent(NOTICE_EVENT, { detail: message })); } catch { /* ignore */ }\n}\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u5F52\u6863\u5230\u5F52\u6863\u6C60' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Archive' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\t// The context menu's dialog lives outside this tree; it reports completion\n\t// through a window event, and the panel refetches so the project list and the\n\t// moved session's placement are current without a DSH restart.\n\tuseEffect(() => {\n\t\tconst onNotice = event => {\n\t\t\tconst message = typeof event?.detail === 'string' ? event.detail : '';\n\t\t\tif (message) say(message);\n\t\t\tvoid loadState().catch(() => {});\n\t\t};\n\t\twindow.addEventListener(NOTICE_EVENT, onNotice);\n\t\treturn () => window.removeEventListener(NOTICE_EVENT, onNotice);\n\t}, []);\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u5F52\u6863\u6C60 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\t// Known projects, so the target can be picked by name instead of typed as a path.\n\tconst projects = state?.projects ?? [];\n\t// Which project the typed session currently belongs to (computed host-side).\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => {\n\t\tif (!sessionId) { setFromProject(null); return; }\n\t\tlet alive = true;\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\tonChange={e => { setToCwd(e.target.value); setPlan(null); }}>\n\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t)}\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t{sessionId && (\n\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\u8BE5\u4F1A\u8BDD\uFF0C\u6216\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u5F52\u6863\u6C60</h3>\n\t\t\t<p className=\"muted\">\n\t\t\t\tDSH \u6CA1\u6709\u72EC\u7ACB\u7684\u300C\u5220\u9664\u4F1A\u8BDD\u300D\u2014\u2014\u4F1A\u8BDD\u53EA\u80FD<b>\u5F52\u6863</b>\u3002\u8FD9\u91CC\u5C31\u662F\u5B98\u65B9\u7684\u5F52\u6863\u6C60\uFF1A\u5165\u6C60\u7684\u4F1A\u8BDD\u4ECD\u7136\u662F\u5B98\u65B9\u5F52\u6863\u72B6\u6001\uFF08\u4FA7\u680F\u7684\u5F52\u6863\u89C6\u56FE\u91CC\u540C\u6837\u80FD\u770B\u5230\uFF09\uFF0C\u672C\u63D2\u4EF6\u989D\u5916\u8BB0\u5F55\u4E86\u5B83\u5165\u6C60\u65F6\u7684<b>\u9879\u76EE\u5F52\u5C5E\u3001\u5217\u8868\u4F4D\u7F6E\u4E0E\u7F6E\u9876\u72B6\u6001</b>\uFF0C\u56E0\u6B64\u53EF\u4EE5\u4E00\u952E\u8FD8\u539F\u5230\u539F\u4F4D\u3002\n\t\t\t\t\u5F52\u6863\u9879\u76EE\u5219\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u53EA\u79FB\u9664\u9879\u76EE\u6CE8\u518C\u8868\u9879\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5165\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002\n\t\t\t</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u6C60\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\u5230\u539F\u4F4D').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u79FB\u51FA{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u5F52\u6863\uFF1F\uFF08\u4F1A\u8BDD\u5C06\u8FDB\u5165\u5F52\u6863\u6C60\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u5F52\u6863\u5230\u5F52\u6863\u6C60'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\t// Same project picker as the panel: the dialog is what the context menu opens,\n\t// so it must not fall back to typing a path by hand.\n\tconst [projects, setProjects] = useState([]);\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tuseEffect(() => {\n\t\tlet alive = true;\n\t\tvoid loadState().then(next => { if (alive) setProjects(next?.projects ?? []); }).catch(() => {});\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry {\n\t\t\tconst result = await callHost({ type: 'move', sessionId, toCwd, createTarget });\n\t\t\t// The host has already re-read the session headers. The sidebar list is a\n\t\t\t// server-driven snapshot, so the row appears there on the next refresh; say\n\t\t\t// that plainly instead of closing on a silent success.\n\t\t\tclose();\n\t\t\tdispatchNotice(result?.deferred\n\t\t\t\t? '\u6587\u4EF6\u5DF2\u8FC1\u79FB\uFF1BDSH \u8D26\u672C\u5C06\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u6536\u655B\uFF0C\u5237\u65B0\u4FA7\u680F\u540E\u53EF\u89C1'\n\t\t\t\t: '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5237\u65B0\u4FA7\u680F\u540E\u8BE5\u4F1A\u8BDD\u5C31\u4F1A\u51FA\u73B0\u5728\u76EE\u6807\u9879\u76EE\u4E0B');\n\t\t}\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t\t</p>\n\t\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\t\tonChange={e => setToCwd(e.target.value)}>\n\t\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t)}\n\t\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.12\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt, sentAt: sentAt ?? 0 }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), sentAt: 0, seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\t/**\n\t * Image cards currently sitting in the composer.\n\t *\n\t * The official composer region is `[data-composer-seat]` (carrying\n\t * `data-conversation-region=\"composer\"`); the old selector also looked for a\n\t * `data-composer-bar` attribute that the current UI never emits, which made\n\t * every lookup fall through to a brittle parentElement walk and usually\n\t * return zero cards. A zero rail in turn defeated both the send detector and\n\t * the \"composer already holds them\" guard.\n\t */\n\tfunction attachmentRail(editor) {\n\t\tconst seat = editor?.closest?.('[data-composer-seat], [data-conversation-region=\"composer\"]');\n\t\tconst root = seat ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (root?.querySelectorAll === undefined) return undefined;\n\t\treturn [...root.querySelectorAll('img')].filter(img => {\n\t\t\tconst src = img.getAttribute?.('src') ?? '';\n\t\t\treturn src.startsWith('blob:') || src.startsWith('data:image/');\n\t\t});\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\t// Never resurrect images that already travelled with a sent message. Two\n\t\t// independent guards: an explicit sent flag, and a \"already handed back to\n\t\t// the composer\" flag so a single batch is dispatched at most once even if\n\t\t// the rail probe misses the cards.\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId && !record.sentAt && !record.delivered);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\t// A restore hands the images back to the official composer; they now live\n\t\t// there (and will be retired when sent), so keep a single copy in the vault.\n\t\tif (accepted) for (const record of pending) await putRecord({ ...record, seeded: true, delivered: now() });\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\t/**\n\t * Mark a session's backed-up drafts as already sent, so they are never\n\t * replayed into the composer again.\n\t *\n\t * Inferring \"sent\" from the attachment rail reaching zero used to miss the\n\t * common case: the rail is already empty by the time the session mounts\n\t * again, so the transition never happened while we were watching and the\n\t * old images were restored on every switch. Observing the composer while it\n\t * is mounted (below) plus this explicit retirement keeps the vault honest.\n\t */\n\tasync function retireSession(sessionId, reason) {\n\t\tlet retired = 0;\n\t\tfor (const record of await allRecords()) {\n\t\t\tif (record.sessionId !== sessionId || record.sentAt) continue;\n\t\t\tawait putRecord({ ...record, sentAt: now() });\n\t\t\tretired += 1;\n\t\t}\n\t\tif (retired > 0) {\n\t\t\tnotice(reason === 'sent'\n\t\t\t\t? `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u968F\u6D88\u606F\u53D1\u9001\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`\n\t\t\t\t: `\u672C\u4F1A\u8BDD\u8349\u7A3F\u5DF2\u6E05\u7A7A\uFF0C${retired} \u5F20\u5907\u4EFD\u4E0D\u518D\u6062\u590D`);\n\t\t\tvoid syncHost();\n\t\t}\n\t\tchanged();\n\t\treturn retired;\n\t}\n\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Cards disappeared while the composer was mounted: the message was\n\t\t\t\t// sent (or the user removed them), so retire the backups for good.\n\t\t\t\tawait retireSession(sessionId, 'sent');\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\n\t\t// Belt and braces: listen for the send gesture itself, so a fast unmount\n\t\t// (enter then immediately switch away) still retires the drafts.\n\t\tconst onKeyDown = event => {\n\t\t\tif (event.key !== 'Enter' || event.shiftKey || event.isComposing) return;\n\t\t\tconst target = event.target;\n\t\t\tif (target?.closest?.('[data-conversation-session]') === null || target?.closest?.('[data-conversation-session]') === undefined) return;\n\t\t\tif ((attachmentRail(editor)?.length ?? 0) > 0) void retireSession(sessionId, 'sent');\n\t\t};\n\t\tdocument.addEventListener('keydown', onKeyDown, true);\n\t\treturn () => { observer.disconnect(); document.removeEventListener('keydown', onKeyDown, true); };\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\t/** Retire a session's unsent drafts (called when its images were sent). */\n\t\tretire: async (sessionId = currentSession(), reason = 'sent') => retireSession(sessionId, reason),\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["store", "name", "setBusy"]
7
7
  }
@@ -8,7 +8,7 @@ import {
8
8
  guardLedger,
9
9
  guardManifest,
10
10
  guardSettings
11
- } from "./chunk-QXW2NIKH.js";
11
+ } from "./chunk-EXE2TWBF.js";
12
12
 
13
13
  // src/index.mjs
14
14
  import { mkdir, realpath, stat } from "node:fs/promises";
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.10",
23
+ version: "0.1.12",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
@@ -253,9 +253,25 @@ async function applyInner(ctx, options = {}) {
253
253
  }
254
254
  function hostCapabilities(ctx) {
255
255
  return {
256
+ /**
257
+ * Whether a session is actively working.
258
+ *
259
+ * The old check asked whether `sessions.list()` contained the id, but that
260
+ * list holds every session the host has loaded — a session that is merely
261
+ * open (or was opened at some point) counted as live and blocked every
262
+ * move. The official activity hook is `workspace/session-activity`: a
263
+ * `turn` entry means a turn is in flight (including one waiting on an
264
+ * approval or an answer), which is exactly what must not be relocated.
265
+ */
256
266
  sessionLive: (sessionId) => {
257
- const sessions = ctx.get("sessions");
258
- return sessions ? sessions.list().some((session) => session.header?.id === sessionId) : false;
267
+ try {
268
+ const activities = ctx.waterfall?.("workspace/session-activity", { sessionId }, () => []);
269
+ if (activities === void 0) return false;
270
+ if (typeof activities?.then === "function") return false;
271
+ return Array.isArray(activities) && activities.some((entry) => entry?.kind === "turn");
272
+ } catch {
273
+ return false;
274
+ }
259
275
  },
260
276
  listHeaders: async () => {
261
277
  const persistence = ctx.get("sessionPersistence");
@@ -286,6 +302,21 @@ function hostCapabilities(ctx) {
286
302
  },
287
303
  resyncIndex: async () => {
288
304
  return ctx.get("sessionQuery") !== void 0 ? "lazy" : "unavailable";
305
+ },
306
+ /**
307
+ * Re-read the session headers so the registry (and every list built from
308
+ * it) sees the session's new location. This is the host-side half of
309
+ * "refresh"; the client half is a refetch scoped to this one session.
310
+ */
311
+ refreshPlacement: async (sessionId) => {
312
+ const registry = ctx.get("workspaceRegistry");
313
+ if (registry === void 0) return "unavailable";
314
+ if (typeof registry.listStoredHeaders === "function" && typeof registry.replaceHeaderIndex === "function") {
315
+ await registry.replaceHeaderIndex(await registry.listStoredHeaders());
316
+ }
317
+ await ctx.get("sessionPersistence")?.refresh?.().catch?.(() => {
318
+ });
319
+ return registry.sessionKnown !== void 0 && sessionId !== void 0 ? await registry.sessionKnown(sessionId) ? "known" : "unknown" : "refreshed";
289
320
  }
290
321
  };
291
322
  }
@@ -358,7 +389,9 @@ function registryAdapter(ctx) {
358
389
  });
359
390
  return entity;
360
391
  },
361
- purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present"
392
+ purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present",
393
+ /** Re-read headers so a restored/archived session's placement is visible. */
394
+ refreshPlacement: async (sessionId) => hostCapabilities(ctx).refreshPlacement(sessionId)
362
395
  };
363
396
  }
364
397
  function deepMerge(base, patch) {
package/lib/host.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  guardLedger,
9
9
  guardManifest,
10
10
  guardSettings
11
- } from "./chunk-QXW2NIKH.js";
11
+ } from "./chunk-EXE2TWBF.js";
12
12
 
13
13
  // src/index.mjs
14
14
  import { mkdir, realpath, stat } from "node:fs/promises";
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.10",
23
+ version: "0.1.12",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
@@ -253,9 +253,25 @@ async function applyInner(ctx, options = {}) {
253
253
  }
254
254
  function hostCapabilities(ctx) {
255
255
  return {
256
+ /**
257
+ * Whether a session is actively working.
258
+ *
259
+ * The old check asked whether `sessions.list()` contained the id, but that
260
+ * list holds every session the host has loaded — a session that is merely
261
+ * open (or was opened at some point) counted as live and blocked every
262
+ * move. The official activity hook is `workspace/session-activity`: a
263
+ * `turn` entry means a turn is in flight (including one waiting on an
264
+ * approval or an answer), which is exactly what must not be relocated.
265
+ */
256
266
  sessionLive: (sessionId) => {
257
- const sessions = ctx.get("sessions");
258
- return sessions ? sessions.list().some((session) => session.header?.id === sessionId) : false;
267
+ try {
268
+ const activities = ctx.waterfall?.("workspace/session-activity", { sessionId }, () => []);
269
+ if (activities === void 0) return false;
270
+ if (typeof activities?.then === "function") return false;
271
+ return Array.isArray(activities) && activities.some((entry) => entry?.kind === "turn");
272
+ } catch {
273
+ return false;
274
+ }
259
275
  },
260
276
  listHeaders: async () => {
261
277
  const persistence = ctx.get("sessionPersistence");
@@ -286,6 +302,21 @@ function hostCapabilities(ctx) {
286
302
  },
287
303
  resyncIndex: async () => {
288
304
  return ctx.get("sessionQuery") !== void 0 ? "lazy" : "unavailable";
305
+ },
306
+ /**
307
+ * Re-read the session headers so the registry (and every list built from
308
+ * it) sees the session's new location. This is the host-side half of
309
+ * "refresh"; the client half is a refetch scoped to this one session.
310
+ */
311
+ refreshPlacement: async (sessionId) => {
312
+ const registry = ctx.get("workspaceRegistry");
313
+ if (registry === void 0) return "unavailable";
314
+ if (typeof registry.listStoredHeaders === "function" && typeof registry.replaceHeaderIndex === "function") {
315
+ await registry.replaceHeaderIndex(await registry.listStoredHeaders());
316
+ }
317
+ await ctx.get("sessionPersistence")?.refresh?.().catch?.(() => {
318
+ });
319
+ return registry.sessionKnown !== void 0 && sessionId !== void 0 ? await registry.sessionKnown(sessionId) ? "known" : "unknown" : "refreshed";
289
320
  }
290
321
  };
291
322
  }
@@ -358,7 +389,9 @@ function registryAdapter(ctx) {
358
389
  });
359
390
  return entity;
360
391
  },
361
- purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present"
392
+ purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present",
393
+ /** Re-read headers so a restored/archived session's placement is visible. */
394
+ refreshPlacement: async (sessionId) => hostCapabilities(ctx).refreshPlacement(sessionId)
362
395
  };
363
396
  }
364
397
  function deepMerge(base, patch) {
package/lib/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  guardLedger,
9
9
  guardManifest,
10
10
  guardSettings
11
- } from "./chunk-QXW2NIKH.js";
11
+ } from "./chunk-EXE2TWBF.js";
12
12
 
13
13
  // src/index.mjs
14
14
  import { mkdir, realpath, stat } from "node:fs/promises";
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
20
20
  // package.json
21
21
  var package_default = {
22
22
  name: "dsh-session-toolbox",
23
- version: "0.1.10",
23
+ version: "0.1.12",
24
24
  repository: {
25
25
  type: "git",
26
26
  url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
@@ -253,9 +253,25 @@ async function applyInner(ctx, options = {}) {
253
253
  }
254
254
  function hostCapabilities(ctx) {
255
255
  return {
256
+ /**
257
+ * Whether a session is actively working.
258
+ *
259
+ * The old check asked whether `sessions.list()` contained the id, but that
260
+ * list holds every session the host has loaded — a session that is merely
261
+ * open (or was opened at some point) counted as live and blocked every
262
+ * move. The official activity hook is `workspace/session-activity`: a
263
+ * `turn` entry means a turn is in flight (including one waiting on an
264
+ * approval or an answer), which is exactly what must not be relocated.
265
+ */
256
266
  sessionLive: (sessionId) => {
257
- const sessions = ctx.get("sessions");
258
- return sessions ? sessions.list().some((session) => session.header?.id === sessionId) : false;
267
+ try {
268
+ const activities = ctx.waterfall?.("workspace/session-activity", { sessionId }, () => []);
269
+ if (activities === void 0) return false;
270
+ if (typeof activities?.then === "function") return false;
271
+ return Array.isArray(activities) && activities.some((entry) => entry?.kind === "turn");
272
+ } catch {
273
+ return false;
274
+ }
259
275
  },
260
276
  listHeaders: async () => {
261
277
  const persistence = ctx.get("sessionPersistence");
@@ -286,6 +302,21 @@ function hostCapabilities(ctx) {
286
302
  },
287
303
  resyncIndex: async () => {
288
304
  return ctx.get("sessionQuery") !== void 0 ? "lazy" : "unavailable";
305
+ },
306
+ /**
307
+ * Re-read the session headers so the registry (and every list built from
308
+ * it) sees the session's new location. This is the host-side half of
309
+ * "refresh"; the client half is a refetch scoped to this one session.
310
+ */
311
+ refreshPlacement: async (sessionId) => {
312
+ const registry = ctx.get("workspaceRegistry");
313
+ if (registry === void 0) return "unavailable";
314
+ if (typeof registry.listStoredHeaders === "function" && typeof registry.replaceHeaderIndex === "function") {
315
+ await registry.replaceHeaderIndex(await registry.listStoredHeaders());
316
+ }
317
+ await ctx.get("sessionPersistence")?.refresh?.().catch?.(() => {
318
+ });
319
+ return registry.sessionKnown !== void 0 && sessionId !== void 0 ? await registry.sessionKnown(sessionId) ? "known" : "unknown" : "refreshed";
289
320
  }
290
321
  };
291
322
  }
@@ -358,7 +389,9 @@ function registryAdapter(ctx) {
358
389
  });
359
390
  return entity;
360
391
  },
361
- purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present"
392
+ purgeSession: async () => ctx.get("sessionPersistence")?.delete === void 0 ? "unavailable" : "present",
393
+ /** Re-read headers so a restored/archived session's placement is visible. */
394
+ refreshPlacement: async (sessionId) => hostCapabilities(ctx).refreshPlacement(sessionId)
362
395
  };
363
396
  }
364
397
  function deepMerge(base, patch) {