dsh-pause-session 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/shared.ts","../src/client/burst.ts","../src/client/control.ts","../src/client/view.ts","../src/client/global.ts","../src/client/icons.tsx","../src/client/pill.tsx","../src/client/locales.ts","../src/client/index.tsx"],"sourcesContent":["/**\n * The identities both halves of dsh-pause-session name, the status encodings they\n * share, and the one spelling a host log line gives a caught value.\n *\n * The browser bundle may value-import only the shell's baseline module table,\n * so this module imports nothing: the client half reads command names and the\n * decode functions from it directly, and the host half derives the branded\n * identities from the same strings.\n *\n * A third-party plugin's only live channel to the browser is the command\n * registry — it can add neither a `ctx.remote` namespace nor a forwarded event,\n * both of which are compiled into the harness's own API assembly. A command\n * descriptor's only free-form field is its `description`, and a command\n * result's only free-form field is its `text`, so the park state travels as a\n * prefix plus `;`-separated fields on one of those. The text stays\n * human-readable: the same words are what the slash-command menu shows.\n *\n * Every value inside a field is escaped, so a decoded name is the exact name\n * that was encoded. Session ids, tool names, and call ids are the host's own\n * data and may contain the `;`, `,`, `/`, or newline a field is separated by; a\n * folded spelling would decode as a different name — a different row, looked up\n * again.\n * @module dsh-pause-session/shared\n */\n\n/** Command that pauses the receiving session before its next model request. */\nexport const PAUSE_COMMAND = 'pause'\n\n/** Command that resumes the receiving session; registered only while that session is paused. */\nexport const RESUME_COMMAND = 'pause-resume'\n\n/** Command that resumes every parked session the registry remembers. */\nexport const RESUME_ALL_COMMAND = 'pause-resume-all'\n\n/** Command that registers and parks every running session tree in one action. */\nexport const PAUSE_ALL_COMMAND = 'pause-all'\n\n/** Command that reports the whole-app park state; registered globally, always. */\nexport const STATE_ALL_COMMAND = 'pause-state-all'\n\n/** Command that reports the receiving session's park state; registered only while it is paused. */\nexport const STATUS_COMMAND = 'pause-state'\n\n/** Command that turns the receiving session's own goal re-arm on or off; registered only while it is paused. */\nexport const GOAL_COMMAND = 'pause-goal'\n\n/** Command that reports every session that did not stop cleanly. */\nexport const TRIAGE_COMMAND = 'pause-triage'\n\n/** Command that resumes one session from the decision list exactly as the clean path would. */\nexport const TRIAGE_CONTINUE_COMMAND = 'pause-triage-continue'\n\n/** Command that resumes one session from the decision list with the unknown-outcome note. */\nexport const TRIAGE_NOTE_COMMAND = 'pause-triage-note'\n\n/** Command that opens one session from the decision list without spending a request. */\nexport const TRIAGE_REVIEW_COMMAND = 'pause-triage-review'\n\n/** Command that leaves one session from the decision list exactly as it is. */\nexport const TRIAGE_LEAVE_COMMAND = 'pause-triage-leave'\n\n/** Command that turns one listed session's goal re-arm on or off. */\nexport const TRIAGE_GOAL_COMMAND = 'pause-triage-goal'\n\n/** Stable definition id of the pause command, matched by identity rather than by copy. */\nexport const PAUSE_DEFINITION_ID = 'dsh-pause-session/pause'\n\n/** Stable definition id of the resume command: the host's own \"this session is paused\" fact. */\nexport const RESUME_DEFINITION_ID = 'dsh-pause-session/resume'\n\n/** Stable definition id of the global resume command, matched by identity rather than by copy. */\nexport const RESUME_ALL_DEFINITION_ID = 'dsh-pause-session/resume-all'\n\n/** Stable definition id of the whole-app pause command. */\nexport const PAUSE_ALL_DEFINITION_ID = 'dsh-pause-session/pause-all'\n\n/** Stable definition id of the whole-app status read. */\nexport const STATE_ALL_DEFINITION_ID = 'dsh-pause-session/state-all'\n\n/** Stable definition id of the status read, matched by identity rather than by copy. */\nexport const STATUS_DEFINITION_ID = 'dsh-pause-session/status'\n\n/** Stable definition id of the paused session's own goal re-arm toggle. */\nexport const GOAL_DEFINITION_ID = 'dsh-pause-session/goal'\n\n/** Stable definition id of the decision-list read. */\nexport const TRIAGE_DEFINITION_ID = 'dsh-pause-session/triage'\n\n/** Stable definition id of the decision-list \"continue\" verb. */\nexport const TRIAGE_CONTINUE_DEFINITION_ID = 'dsh-pause-session/triage-continue'\n\n/** Stable definition id of the decision-list \"continue with the unknown-outcome note\" verb. */\nexport const TRIAGE_NOTE_DEFINITION_ID = 'dsh-pause-session/triage-note'\n\n/** Stable definition id of the decision-list \"open without activating\" verb. */\nexport const TRIAGE_REVIEW_DEFINITION_ID = 'dsh-pause-session/triage-review'\n\n/** Stable definition id of the decision-list \"leave it alone today\" verb. */\nexport const TRIAGE_LEAVE_DEFINITION_ID = 'dsh-pause-session/triage-leave'\n\n/** Stable definition id of the decision-list goal re-arm toggle. */\nexport const TRIAGE_GOAL_DEFINITION_ID = 'dsh-pause-session/triage-goal'\n\n/** First token of every encoded status, so a descriptor is never parsed by accident. */\nexport const STATUS_PREFIX = 'pause: '\n\n/**\n * One list field's text when the list is empty. A value that would itself spell\n * this marker is escaped by {@link escapeField}, so an empty list and a list\n * holding the marker stay different texts.\n */\nconst EMPTY_FIELD = '-'\n\n/**\n * One field or item value, ready to sit inside an encoded text: every separator\n * (`;`, `,`, `/`, newline) and the escape character itself are escaped, and a\n * value that would spell {@link EMPTY_FIELD} is escaped so it is not read as an\n * empty list.\n * @param text - the value to encode, as the host holds it.\n * @returns the value as it appears inside an encoded field.\n */\nfunction escapeField(text: string): string {\n const escaped = text.replace(/[\\\\;,\\n/]/g, character => character === '\\n' ? '\\\\n' : `\\\\${character}`)\n return escaped.startsWith(EMPTY_FIELD) ? `\\\\${escaped}` : escaped\n}\n\n/**\n * One value previously written by {@link escapeField}.\n * @param text - the encoded value, escapes included.\n * @returns the value the host holds.\n */\nfunction unescapeField(text: string): string {\n return text.replace(/\\\\(.)/g, (_escape, character: string) => character === 'n' ? '\\n' : character)\n}\n\n/**\n * Split one encoded text on the separators that are not escaped, keeping each\n * escape beside the character it protects so {@link unescapeField} can read it.\n * @param text - an encoded text, without its prefix.\n * @param separator - the single character to split on.\n * @returns the parts, in order; always at least one.\n */\nfunction splitFields(text: string, separator: string): string[] {\n const parts: string[] = []\n let part = ''\n for (let at = 0; at < text.length; at += 1) {\n const character = text[at] ?? ''\n if (character === '\\\\') {\n part += character + (text[at + 1] ?? '')\n at += 1\n continue\n }\n if (character === separator) {\n parts.push(part)\n part = ''\n continue\n }\n part += character\n }\n parts.push(part)\n return parts\n}\n\n/**\n * One field of an encoded text, by its key.\n * @param fields - the separated fields of one encoded text.\n * @param key - the field's key, without its `:`.\n * @returns the field's value with its escapes still in place, or `undefined`\n * when the text does not carry that field.\n */\nfunction fieldOf(fields: readonly string[], key: string): string | undefined {\n const prefix = `${key}:`\n const at = fields.findIndex(field => field.trim().startsWith(prefix))\n return at < 0 ? undefined : fields[at]?.trim().slice(prefix.length).trim()\n}\n\n/**\n * One log line's text for a caught value.\n * @param error - the caught value.\n * @returns the error's message, or the value's own spelling.\n */\nexport function describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/**\n * The four park outcomes, listed in the order a park walks them — and the one\n * vocabulary the live status, the durable registry, and the published words all\n * derive from, so they cannot drift apart:\n *\n * - `draining`: paused, and the session is still working — a tool is running, or\n * a turn is between steps.\n * - `parking`: standing still at the park point; the session-level flush has\n * not resolved yet.\n * - `safe`: parked and flushed, so it is safe to walk away. The only clean one.\n * - `flush-failed`: parked, but the flush failed — the durable promise is not\n * kept, and it must never be read as clean.\n */\nexport const PARK_PHASES = ['draining', 'parking', 'safe', 'flush-failed'] as const\n\n/** One park outcome. */\nexport type ParkPhase = (typeof PARK_PHASES)[number]\n\n/**\n * How far along a phase is, worst first. The direction is not the order a park\n * walks: it orders how much of the promise each outcome keeps, which is what a\n * fold over several sessions has to compare. `safe` keeps all of it, `draining`\n * and `parking` are on their way, `flush-failed` has stopped trying — a park\n * that failed its flush is further from clean than one that is still working at\n * it, so it must never be the outcome a report rounds down to.\n */\nconst PHASE_SEVERITY: Record<ParkPhase, number> = {\n safe: 0,\n draining: 1,\n parking: 2,\n 'flush-failed': 3,\n}\n\n/**\n * Fold several records into the one verdict that speaks for all of them: the\n * worst phase any of them reached, and whether every one of them is clean.\n *\n * This is how a session's control answers for its tree. \"Safe to close\" is a\n * promise about everything the press registered, so it may be said only when\n * every member reached the flushed park point; anything else is reported as the\n * least-clean state in the set, never rounded up to the session's own.\n * @param phases - the durable phases of the records being spoken for.\n * @returns the worst phase, and whether that verdict is the clean one.\n */\nexport function foldTreePhases(phases: Iterable<ParkPhase>): { phase: ParkPhase; safe: boolean } {\n let worst: ParkPhase = 'safe'\n for (const phase of phases) {\n if (PHASE_SEVERITY[phase] > PHASE_SEVERITY[worst]) worst = phase\n }\n return { phase: worst, safe: worst === 'safe' }\n}\n\n/** The one word each phase is published as, and the whole decoding vocabulary. */\nconst PHASE_WORD: Record<ParkPhase, string> = {\n draining: 'pausing',\n parking: 'saving',\n safe: 'paused',\n 'flush-failed': 'save failed',\n}\n\n/**\n * The two states of one record's goal re-arm field, in the one spelling both\n * the per-session status and the decision list publish it with.\n */\nconst GOAL_ON = 'on'\nconst GOAL_OFF = 'off'\n\n/** Everything the browser is told about one paused session. */\nexport interface ParkStatus {\n /**\n * The phase of everything that session's press registered — the session and\n * the subagent members under it, folded by {@link foldTreePhases}. A member\n * still draining or stalled holds this at its own phase, because the control\n * that owns the tree may not say \"safe to close\" while any member of it is\n * not.\n */\n readonly phase: ParkPhase\n /** Whether the host commits to \"you can close the lid now\". */\n readonly safe: boolean\n /** Tools in flight when the status was published; empty once parked. */\n readonly runningTools: readonly string[]\n /**\n * Whether the resume that takes this pause back may re-open the session's\n * goal — the durable record's own opt-out, which outlives this process. It is\n * absent when this host could not read the record that holds it; a browser\n * that is not told shows no switch rather than a state it would have to\n * guess, and a guess here would tell the user their money is safe when\n * nobody knows whether it is.\n */\n readonly rearmGoal?: boolean\n}\n\n/**\n * Encode one status for a command descriptor.\n * @param status - the state to publish.\n * @returns the descriptor text, e.g. `pause: draining; tools: edit, bash; goal: on`.\n */\nexport function encodeParkStatus(status: ParkStatus): string {\n const head = `${STATUS_PREFIX}${PHASE_WORD[status.phase]}`\n const tools = status.runningTools.length === 0\n ? ''\n : `; tools: ${status.runningTools.map(escapeField).join(', ')}`\n const goal = status.rearmGoal === undefined ? '' : `; goal: ${status.rearmGoal ? GOAL_ON : GOAL_OFF}`\n return `${head}${tools}${goal}`\n}\n\n/**\n * Decode a status previously written by {@link encodeParkStatus}.\n * @param text - a command descriptor's description.\n * @returns the status, or `undefined` when this text carries none — a foreign\n * command, or a reading taken before the host published anything.\n */\nexport function decodeParkStatus(text: string): ParkStatus | undefined {\n if (!text.startsWith(STATUS_PREFIX)) return undefined\n const [head = '', ...rest] = splitFields(text.slice(STATUS_PREFIX.length), ';')\n const word = head.trim()\n const phase = (Object.keys(PHASE_WORD) as ParkPhase[]).find(candidate => PHASE_WORD[candidate] === word)\n if (phase === undefined) return undefined\n const goal = fieldOf(rest, 'goal')\n return {\n phase,\n safe: phase === 'safe',\n runningTools: decodeNames(fieldOf(rest, 'tools')),\n // A text without the field reports no goal state at all, rather than the\n // product default: only the host's own record may say what it says.\n ...goal === GOAL_ON ? { rearmGoal: true } : goal === GOAL_OFF ? { rearmGoal: false } : {},\n }\n}\n\n/**\n * First token of every encoded whole-app status. A different prefix from the\n * per-session status on purpose: one decoder must never read the other's text.\n */\nexport const GLOBAL_STATUS_PREFIX = 'pause all: '\n\n/** The whole-app answer to \"can I close the lid?\". */\nexport type GlobalVerdict = 'idle' | 'waiting' | 'safe'\n\n/** A surface the whole-app report could not read at all. */\nexport type GlobalBlindSpot = 'trees' | 'jobs'\n\n/**\n * Everything the whole-app control publishes about the park as a whole.\n *\n * The counts and the names are one answer, not two: a report that says\n * \"3 registered, 1 safe\" and then fails to say which two are missing is the\n * kind of silence this control exists to prevent. The verdict is the only\n * summary, and it is derived here so the host and the browser cannot disagree\n * about it.\n */\nexport interface GlobalStatus {\n /** `idle` with nothing registered, `safe` only when every registered session is. */\n readonly verdict: GlobalVerdict\n /** Sessions this park owns — the durable list plus anything pressed since. */\n readonly registered: number\n /** How many of them reached the flushed park point. */\n readonly safe: number\n /**\n * The session this park belongs to — the first registered one this host can\n * hold, whatever phase it reached, so a client can address its reads at the\n * park instead of at whatever session the user happens to be looking at. A\n * park whose every record is unreadable here names the first of those: the\n * name is a fact about the park, and an address that cannot answer is the\n * client's fallback to take.\n *\n * The buckets below name a session only while it has something left to do,\n * and the clean case — every registered session `safe`, which is the ordinary\n * one — leaves all of them empty: without this field a park that finished\n * cleanly names no session at all. `undefined` when the park holds nothing,\n * and absent from a reading written before this field existed; a client must\n * not treat either absence as an address.\n */\n readonly parkedAt?: string\n /**\n * Registered sessions still working: a tool is in flight or a turn is between\n * steps. Their park point is announced by the marker commands the park\n * registers, so a reader has nothing to watch here.\n */\n readonly draining: readonly string[]\n /**\n * How many registered sessions stand at the park point with their flush still\n * pending — the host's `parking` phase. Nothing announces a flush resolving,\n * so this is the one state a reader can only learn by asking again. A count\n * rather than names: it says how long to keep watching, not which transcript\n * a row belongs in, which is what `draining` is for.\n */\n readonly parking: number\n /**\n * Registered sessions that will not reach the park point in this process: a\n * flush that failed, a pause adopted from an earlier process, a press whose\n * registry write failed, or a record whose session this host cannot read and\n * therefore did not hold. Named, never folded into the count and forgotten.\n */\n readonly stalled: readonly string[]\n /** Registered sessions whose tree contains a route a restart cannot resume. */\n readonly unresumable: readonly string[]\n /** Registered sessions whose subagent tree could not be read — unknown, not clean. */\n readonly unreadTrees: readonly string[]\n /** Live background jobs owned by the registered trees; nothing here can stop them. */\n readonly jobs: readonly string[]\n /** Whole surfaces the report could not read, so their emptiness is not a fact. */\n readonly blind: readonly GlobalBlindSpot[]\n}\n\n/** Encode one list field. */\nfunction encodeNames(names: readonly string[]): string {\n return names.length === 0 ? EMPTY_FIELD : names.map(escapeField).join(', ')\n}\n\n/**\n * Encode one optional session id: the empty marker when the park names none.\n * @param id - the session id, as the host holds it.\n * @returns the id as it appears inside an encoded field.\n */\nfunction encodeId(id: string | undefined): string {\n return id === undefined ? EMPTY_FIELD : escapeField(id)\n}\n\n/**\n * Decode one id previously written by {@link encodeId}.\n * @param field - the encoded field, escapes included.\n * @returns the id; `undefined` when the text carries none — an absent field and\n * the empty marker both mean the answer names no session, and a client that\n * read either as one would address a session the host never named.\n */\nfunction decodeId(field: string | undefined): string | undefined {\n const text = field?.trim()\n if (text === undefined || text.length === 0 || text === EMPTY_FIELD) return undefined\n return unescapeField(text)\n}\n\n/** Decode one list field. */\nfunction decodeNames(field: string | undefined): string[] {\n const text = field?.trim()\n if (text === undefined || text.length === 0 || text === EMPTY_FIELD) return []\n return splitFields(text, ',')\n .map(name => unescapeField(name.trim()))\n .filter(name => name.length > 0)\n}\n\n/**\n * Encode one whole-app status for a command result.\n * @param status - the state to publish.\n * @returns the result text, e.g.\n * `pause all: waiting; registered: 3; safe: 1; parked-at: overnight; draining: overnight; parking: 1; stalled: -; unresumable: -; unread-trees: -; jobs: bash-1; blind: -`.\n */\nexport function encodeGlobalStatus(status: GlobalStatus): string {\n return `${GLOBAL_STATUS_PREFIX}${status.verdict}`\n + `; registered: ${status.registered}`\n + `; safe: ${status.safe}`\n + `; parked-at: ${encodeId(status.parkedAt)}`\n + `; draining: ${encodeNames(status.draining)}`\n + `; parking: ${status.parking}`\n + `; stalled: ${encodeNames(status.stalled)}`\n + `; unresumable: ${encodeNames(status.unresumable)}`\n + `; unread-trees: ${encodeNames(status.unreadTrees)}`\n + `; jobs: ${encodeNames(status.jobs)}`\n + `; blind: ${encodeNames(status.blind)}`\n}\n\n/**\n * Decode a whole-app status previously written by {@link encodeGlobalStatus}.\n * @param text - a command result's text.\n * @returns the status, or `undefined` when this text carries none.\n */\nexport function decodeGlobalStatus(text: string): GlobalStatus | undefined {\n if (!text.startsWith(GLOBAL_STATUS_PREFIX)) return undefined\n const [head = '', ...fields] = splitFields(text.slice(GLOBAL_STATUS_PREFIX.length), ';')\n const verdict = head.trim()\n if (verdict !== 'idle' && verdict !== 'waiting' && verdict !== 'safe') return undefined\n const byKey = new Map<string, string>()\n for (const field of fields) {\n const at = field.indexOf(':')\n if (at < 0) continue\n byKey.set(field.slice(0, at).trim(), field.slice(at + 1).trim())\n }\n const count = (key: string): number => {\n const value = Number(byKey.get(key))\n return Number.isSafeInteger(value) && value >= 0 ? value : 0\n }\n const parkedAt = decodeId(byKey.get('parked-at'))\n return {\n verdict,\n registered: count('registered'),\n safe: count('safe'),\n // A text written before this field existed names no parked session, and a\n // park that holds nothing names none either: both decode as no address\n // rather than as a session to look up.\n ...parkedAt === undefined ? {} : { parkedAt },\n draining: decodeNames(byKey.get('draining')),\n // An answer written before this field existed reports no session standing\n // still: the oldest reading is the one with nothing known to watch.\n parking: count('parking'),\n stalled: decodeNames(byKey.get('stalled')),\n unresumable: decodeNames(byKey.get('unresumable')),\n unreadTrees: decodeNames(byKey.get('unread-trees')),\n jobs: decodeNames(byKey.get('jobs')),\n blind: decodeNames(byKey.get('blind')).filter((spot): spot is GlobalBlindSpot => spot === 'trees' || spot === 'jobs'),\n }\n}\n\n/**\n * First token of every encoded decision list. Separate from the per-session and\n * whole-app status prefixes on purpose, for the same reason those two are\n * separate: one decoder must never read another's text.\n */\nexport const TRIAGE_PREFIX = 'pause triage: '\n\n/** How one decision-list row was read out of the session's own stored log. */\nexport type TriageReading = 'read' | 'unreadable'\n\n/**\n * What one stored log says about a tool call whose outcome was never recorded.\n *\n * The call event itself is durable; the harness turns every such call into a\n * synthetic `TOOL_OUTCOME_UNKNOWN` error result when it next reads the log, and\n * this is that same derivation taken without writing anything back. The marker\n * itself is never in the stored log of a session still on the list: the resume\n * that appends it is also the resume that drops the record putting it here.\n */\nexport interface TriageTrace {\n /** The tool's own name, from the call event the model produced. */\n readonly tool: string\n /** The provider-issued id pairing the call with the result that never arrived. */\n readonly callId: string\n}\n\n/**\n * One session that did not stop cleanly, as the decision list presents it.\n *\n * Every field is read from the session's own log or from the durable park\n * record; nothing here is derived from the fact that the session is merely\n * listed. `reading` is the honesty field: a log this host could not read says\n * `unreadable` and carries no facts, rather than an empty row that would read\n * as \"no traces\".\n */\nexport interface TriageRow {\n /** The parked session. */\n readonly sessionId: string\n /** The durable park outcome; never `safe`, which is the clean path's business. */\n readonly phase: Exclude<ParkPhase, 'safe'>\n /** When that outcome was last written. */\n readonly updatedAt: number\n /** Whether the facts below came from the log. */\n readonly reading: TriageReading\n /** What the stored log's last event was, with the step it belongs to when it has one. */\n readonly lastEvent?: string\n /** The turn and step that last event belongs to. */\n readonly turn?: number\n readonly step?: number\n /** Tools whose call event is durable and whose result is not — the unknown outcomes. */\n readonly traces: readonly TriageTrace[]\n /** Whether the stored log ends owing the model an answer. */\n readonly owed: boolean\n /**\n * Whether resuming this session may re-open its goal. Re-arming is what makes\n * a resumed goal spend its owner's rounds, so it is a per-session choice the\n * row shows and the list can flip.\n */\n readonly rearmGoal: boolean\n /**\n * Whether the host could hand this session its owed request at all: a log it\n * could not read, or one whose last request recorded no route, cannot be\n * woken — which is a fact about the entry, not a refusal of the user's choice.\n */\n readonly wakeable: boolean\n}\n\n/** The decision list: what did not stop cleanly, and each row's facts. */\nexport interface TriageList {\n /** Every listed session, in registry order. */\n readonly rows: readonly TriageRow[]\n}\n\n/**\n * The decisions the list offers for one row. The first four carry the row out;\n * `goal` changes what the next resume may spend without touching the park\n * itself.\n */\nexport const TRIAGE_ACTIONS = ['continue', 'note', 'review', 'leave', 'goal'] as const\n\n/** One decision the list offers. */\nexport type TriageAction = (typeof TRIAGE_ACTIONS)[number]\n\n/** How one row is spelled in the published list; the wake field's state when the host can hand over the owed request. */\nconst WAKE_YES = 'yes'\n\n/** One row's traces, as a list of `tool/callId` items. */\nfunction encodeTraces(traces: readonly TriageTrace[]): string {\n if (traces.length === 0) return EMPTY_FIELD\n return traces\n .map(trace => `${escapeField(trace.tool)}/${escapeField(trace.callId)}`)\n .join(', ')\n}\n\n/** Decode {@link encodeTraces}; an item without a tool name is not a trace. */\nfunction decodeTraces(field: string | undefined): TriageTrace[] {\n const text = field?.trim()\n if (text === undefined || text.length === 0 || text === EMPTY_FIELD) return []\n return splitFields(text, ',').flatMap((item) => {\n // Only the separator between the two halves is unescaped, so the split is\n // exact even for a tool name or a call id that carries a slash itself.\n const [tool = '', callId = ''] = splitFields(item.trim(), '/')\n if (tool.length === 0) return []\n return [{ tool: unescapeField(tool), callId: unescapeField(callId) }]\n })\n}\n\n/** One row's stop position, encoded as `<last event>` plus the step it belongs to. */\nfunction encodeStop(row: TriageRow): string {\n if (row.lastEvent === undefined) return EMPTY_FIELD\n const position = row.turn === undefined\n ? ''\n : `/turn ${String(row.turn)}${row.step === undefined ? '' : ` step ${String(row.step)}`}`\n return `${escapeField(row.lastEvent)}${position}`\n}\n\n/**\n * Encode one decision list for a command result.\n * @param list - the rows to publish.\n * @returns the result text, e.g. `pause triage: 1; row: overnight; phase: draining; stopped: tool/call/turn 1 step 1; tools: gated; wake: yes`.\n */\nexport function encodeTriageList(list: TriageList): string {\n const head = `${TRIAGE_PREFIX}${String(list.rows.length)}`\n return list.rows.reduce((text, row) => {\n const stopped = row.reading === 'unreadable' ? 'unreadable' : encodeStop(row)\n return `${text}; row: ${escapeField(row.sessionId)}`\n + `; phase: ${row.phase}`\n + `; stopped: ${stopped}`\n + `; traces: ${row.reading === 'unreadable' ? EMPTY_FIELD : encodeTraces(row.traces)}`\n + `; tools: ${row.reading === 'unreadable' || row.traces.length === 0 ? EMPTY_FIELD : encodeNames(row.traces.map(trace => trace.tool))}`\n + `; owed: ${row.owed ? WAKE_YES : 'no'}`\n + `; wake: ${row.wakeable ? WAKE_YES : 'no'}`\n + `; goal: ${row.rearmGoal ? GOAL_ON : GOAL_OFF}`\n }, head)\n}\n\n/**\n * Decode a decision list previously written by {@link encodeTriageList}.\n * @param text - a command result's text.\n * @returns the list, or `undefined` when this text carries none.\n */\nexport function decodeTriageList(text: string): TriageList | undefined {\n if (!text.startsWith(TRIAGE_PREFIX)) return undefined\n const [head = '', ...rest] = splitFields(text.slice(TRIAGE_PREFIX.length), ';')\n if (!Number.isSafeInteger(Number(head.trim())) || Number(head.trim()) < 0) return undefined\n const rows: TriageRow[] = []\n let fields: string[] = []\n const flush = (): void => {\n if (fields.length === 0) return\n const sessionId = fieldOf(fields, 'row')\n const phase = fieldOf(fields, 'phase')\n if (sessionId === undefined || phase === undefined || phase === 'safe') return\n const stopped = fieldOf(fields, 'stopped')\n const reading: TriageReading = stopped === 'unreadable' ? 'unreadable' : 'read'\n // An encoded event name has every slash escaped, so the `/turn` suffix is\n // the field's only raw slash and the lazy match ends exactly there. A row\n // the host could not read has no event to name, and a row whose log ends on\n // no event at all carries the empty marker instead.\n const position = reading === 'unreadable' || stopped === undefined || stopped === EMPTY_FIELD\n ? null\n : /^(.*?)(?:\\/turn (\\d+)(?: step (\\d+))?)?$/.exec(stopped)\n rows.push({\n sessionId: unescapeField(sessionId),\n phase: phase as TriageRow['phase'],\n updatedAt: 0,\n reading,\n ...position === null\n ? {}\n : {\n lastEvent: unescapeField(position[1] ?? ''),\n ...position[2] === undefined ? {} : { turn: Number(position[2]) },\n ...position[3] === undefined ? {} : { step: Number(position[3]) },\n },\n traces: reading === 'unreadable' ? [] : decodeTraces(fieldOf(fields, 'traces')),\n owed: fieldOf(fields, 'owed') === WAKE_YES,\n wakeable: fieldOf(fields, 'wake') === WAKE_YES,\n // A list published before this field existed reads as the product\n // default: a goal session comes back running its rounds.\n rearmGoal: fieldOf(fields, 'goal') !== GOAL_OFF,\n })\n fields = []\n }\n for (const field of rest) {\n if (field.trim().startsWith('row:')) flush()\n fields.push(field)\n }\n flush()\n return { rows }\n}\n","/**\n * The re-read burst both browser controls share: a handful of reads after\n * something moved, and silence once it settles.\n *\n * What a control reads is published through the host's command channel, and\n * every read is two durable `command/run|done` records in the address session's\n * log plus an aggregate read on the host. Reading is therefore something the\n * host announces and the user asks for, never a heartbeat:\n *\n * - the host announces a transition by registering or unregistering the paused\n * session's marker commands, and — for a phase change, which no registration\n * of its own would show — by registering that session's status verb again.\n * Each of those emits `commands/change`, and that event, plus the user's own\n * press or decision, is what starts a burst;\n * - one transition announces itself several times: a park registers two or three\n * marker commands per session and a phase change re-registers one, and the host\n * emits one `commands/change` per step. The burst therefore waits out a short\n * `windowMs` before its first read and any trigger inside that window\n * postpones it, so the announcements of one transition start one burst rather\n * than one each;\n * - a change that arrives within `ACTION_ABSORB_MS` of the user's own action is\n * that action's own consequence, not news: the action already read the host,\n * so the answer is on screen, and the unregistrations it caused — one event\n * per marker command, measured seconds apart, which the window above cannot\n * merge — would only be read back. Such a change is absorbed, and each one\n * absorbed pushes the window out, because a storm that keeps arriving is\n * still the same storm. The window always closes: the first change after\n * `ACTION_ABSORB_MS` of quiet is news again, and a user action reads on its\n * own whatever the window says;\n * - an absorbed change is deferred rather than lost: a control whose action left\n * a phase still moving asks for one look at the moment the window closes\n * ({@link ReadBurst.recheckAfterEcho}), which is the read that finds a\n * transition the window hid — the park can reach its final phase inside that\n * window and never announce anything again. One look per action, and only\n * while the state it left is still moving;\n * - a burst is a fixed short list of waits, so at most that many reads answer\n * one transition, however long the page stays open;\n * - a read that finds the verdict settled ends the burst at once, and the last\n * reading stays on screen — \"the park is settled\" is not a state change, so\n * it must not be re-asked;\n * - a read that finds a phase still moving ends it just as fast. Every phase\n * transition is announced at its source, the flush that resolves `parking`\n * into `safe` included, and that announcement is the very event that starts\n * the next burst — so watching a moving phase from here spends records on a\n * transition that will speak for itself;\n * - what the burst still owes after a read is delivery: the read one\n * announcement bought, and one more when a change lands while a pass is in\n * flight, which that pass cannot have answered. The bound is a bound, not a\n * heartbeat: a phase that never settles is left on screen unread until\n * something else moves;\n * - the bound is a trade, taken knowingly: a reading left on screen when the\n * burst runs out is the host's own last answer, and the next trigger — a\n * forwarded change, a press, or the shell handing a control its session again\n * — reads the truth again. A reading nobody answered is never rendered as a\n * confident state. A longer watch is a longer list of waits, at a linear price\n * in the address session's log;\n * - while the page is hidden no read is armed at all, and the wait a burst\n * still owed is armed again when the page comes back: hidden time is neither\n * charged to the burst nor lost from it.\n *\n * A burst is not restarted by a trigger that lands while one is running: every\n * read it still owes happens after that trigger, so absorbing it loses nothing\n * and a storm of registry changes costs one burst rather than one each.\n * @module dsh-pause-session/client/burst\n */\n\n/** Whether the page is on screen, as the shell reports it. */\nexport type PageVisibility = () => boolean\n\n/**\n * One control's burst policy.\n *\n * `waitsMs` is the whole bound: a burst spends at most one read per wait, and\n * an empty list turns bursting off entirely — which is how a test or a\n * composition disables it without touching anything else.\n */\nexport interface BurstPolicy {\n /** How long the burst collects triggers before its first wait starts. */\n readonly windowMs: number\n /** Waits between the reads of one burst, in order; the first follows a trigger. */\n readonly waitsMs: readonly number[]\n /** Whether the page is on screen now. */\n readonly visible: PageVisibility\n}\n\n/** The policy every control uses unless a caller says otherwise. */\nexport const DEFAULT_BURST_POLICY: BurstPolicy = {\n windowMs: 400,\n waitsMs: [400, 1_000, 2_400, 4_800],\n visible: defaultVisibility,\n}\n\n/**\n * How long after a client action a registry change is that action's own echo.\n *\n * A press, a release, a decision, a goal switch and an opened panel each read\n * the host as they run, so their answer is already on screen when the host\n * announces what they did — one `commands/change` per marker command, measured\n * 0.44 s to 2.04 s apart, a cadence the coalescing window cannot merge. Reading\n * those back is paying for an answer the user already has.\n *\n * The window is what makes `commands/change` mean \"something moved that this\n * browser did not do\". It is refreshed by every change it absorbs, so a storm\n * that keeps arriving stays absorbed rather than leaking one read per change,\n * and it ends for good `ACTION_ABSORB_MS` after the last change: a change after\n * that is news, and so is the next user action, which reads on its own.\n *\n * The margin over the measured 2.04 s gap is deliberate: a transition's own\n * storm has to fit inside the window for the whole storm to stay absorbed.\n */\nexport const ACTION_ABSORB_MS = 2_500\n\n/**\n * The waits one burst uses when its first wait is `first`: four reads spread\n * over roughly ten times that. Long enough that a read a hidden page or an\n * in-flight pass deferred is still taken, short enough that a state which never\n * settles costs a handful of log records rather than a night of them.\n * @param first - milliseconds before the burst's first read, after its window; `<= 0` disables bursting.\n * @returns the waits, in order; empty when bursting is disabled.\n */\nexport function burstWaits(first: number): readonly number[] {\n return first > 0 ? [first, first * 2.5, first * 6, first * 12] : []\n}\n\n/**\n * Whether the browser reports the page as on screen. A shell without a\n * `document` (a test harness, a non-browser host) counts as visible: a control\n * that could never read would be a worse lie than one that reads.\n * @returns whether the page is visible now.\n */\nfunction defaultVisibility(): boolean {\n const state = typeof document === 'undefined' ? undefined : document.visibilityState\n return state !== 'hidden'\n}\n\n/** Start and clear one timer, spelled so a test can drive the clock. */\nexport interface BurstClock {\n /**\n * @param callback - the read to run.\n * @param ms - how long to wait.\n * @returns the scheduled timer.\n */\n set(callback: () => void, ms: number): ReturnType<typeof setTimeout>\n /**\n * @param timer - the timer to clear.\n */\n clear(timer: ReturnType<typeof setTimeout>): void\n}\n\n/** The real clock: `setTimeout` clearing itself. */\nconst SYSTEM_CLOCK: BurstClock = {\n set: (callback, ms) => setTimeout(callback, ms),\n clear: (timer) => { clearTimeout(timer) },\n}\n\n/**\n * One control's re-read burst. Every method is safe to call in any order and as\n * often as the caller likes: a trigger starts at most one burst, and one burst\n * never arms more than one read at a time.\n */\nexport class ReadBurst {\n /** The armed wait, when one is counting down. */\n private timer: ReturnType<typeof setTimeout> | undefined\n /** Whether the burst is waiting out its collection window rather than a wait between reads. */\n private window = false\n /** Waits this burst has not spent yet, front first. */\n private left: readonly number[] = []\n /** Whether a burst is in progress: it may be collecting, waiting, reading, or reading again. */\n private running = false\n /** The absorption window a client action opened, while one is open. */\n private absorbed: ReturnType<typeof setTimeout> | undefined\n /** Whether a control asked for the one look owed when the echo window closes. */\n private recheck = false\n\n /**\n * @param onRead - one more read. Called only for a read the burst owes and the page can show.\n * @param policy - the collection window, the waits, and the visibility source.\n * @param clock - the timer implementation.\n */\n constructor(\n private readonly onRead: () => void,\n private readonly policy: BurstPolicy,\n private readonly clock: BurstClock = SYSTEM_CLOCK,\n ) {}\n\n /** Whether this control bursts at all. */\n get enabled(): boolean {\n return this.policy.waitsMs.length > 0\n }\n\n /** Whether one more read is already counting down. */\n get armed(): boolean {\n return this.timer !== undefined\n }\n\n /** Whether a burst is in progress, with or without a read counting down. */\n get live(): boolean {\n return this.running\n }\n\n /** Whether a change right now is a client action's own echo. */\n get absorbing(): boolean {\n return this.absorbed !== undefined\n }\n\n /**\n * The user acted on this control and the action reads: a press, a release, a\n * decision, a goal switch, an opened panel. Its read is the answer, and the\n * registry changes it sets off announce themselves for a while afterwards, so\n * until the window closes a trigger is that echo rather than news.\n *\n * Called as the action is taken, before its read lands, because the echo can\n * arrive while the action is still running.\n */\n absorb(): void {\n if (!this.enabled) return\n this.armAbsorb()\n }\n\n /**\n * Ask for one read at the moment the echo window closes, because the action's\n * own read left a state that is still moving.\n *\n * The echo window swallows a transition announced inside it — the forwarded\n * event carries no payload, so nothing distinguishes a marker registration\n * from a phase change — and after the window no change is guaranteed to come:\n * the transition that was hidden may be the last one this park makes. The\n * look owed here is the read that finds it, one per action, and it is not a\n * watch: once the window has closed, every further change is news and buys its\n * own read. A control whose action left a settled state owes nothing and asks\n * for nothing.\n */\n recheckAfterEcho(): void {\n if (!this.enabled) return\n this.recheck = true\n }\n\n /**\n * Something moved — a registry change. The burst first waits out its\n * collection window, and every trigger that lands inside it restarts that\n * window: the two or three registrations of one park start one burst rather\n * than one each. A burst already past its window takes this as covered —\n * every read it still owes happens after the change.\n *\n * @returns whether the change is news rather than a client action's own echo.\n * The echo announces what the action's read already answered, so only a news\n * trigger is a change a control still owes a read for; a change that lands\n * while a burst is under way is covered by the read that burst already owes.\n * `false` also for a disabled burst, which reads nothing at all.\n */\n trigger(): boolean {\n if (!this.enabled) return false\n if (this.absorbing) {\n // The action's own echo: it announces what the action's read already\n // answered. Keeping the window open while the echo lasts is what makes a\n // storm of one-change-per-marker cost nothing at all.\n this.armAbsorb()\n return false\n }\n if (!this.running) {\n this.running = true\n this.left = this.policy.waitsMs\n this.armWindow()\n return true\n }\n // Already running and still collecting: this change belongs to the burst\n // being collected, so it postpones that burst's read rather than adding one.\n if (this.window) this.armWindow()\n return true\n }\n\n /**\n * The control knows it still owes a read: a change that is news has arrived,\n * or a reading just revealed something only a read can answer. A call outside\n * any burst starts one. Absorption never applies here — this is not a change\n * some action caused, it is a read the control already knows it needs.\n */\n next(): void {\n if (this.armed || this.window) return\n if (!this.running) {\n this.running = true\n this.left = this.policy.waitsMs\n }\n this.owe()\n }\n\n /**\n * Nothing is moving: end the burst and forget it. The last reading stays\n * published, and the next trigger starts a fresh burst. Two things outlive\n * this: an action's absorption window, which is about what the action caused\n * rather than about the state, and the one look it may still owe — see\n * {@link recheckAfterEcho}.\n */\n stop(): void {\n this.running = false\n this.left = []\n this.window = false\n this.cancel()\n }\n\n /** Drop the armed wait. A burst that is still owed its read survives it; see {@link checkVisibility}. */\n cancel(): void {\n if (this.timer === undefined) return\n this.clock.clear(this.timer)\n this.timer = undefined\n }\n\n /**\n * Re-evaluate one page-visibility change. Going off screen drops the armed\n * wait — a hidden page is not being read — and coming back arms it again, so\n * the burst resumes where it stopped instead of being spent while nobody was\n * looking. A burst still collecting resumes its window, restarted from the\n * moment the page came back: a trigger that arrived while it was hidden still\n * postpones the read.\n */\n checkVisibility(): void {\n if (this.visibleNow()) {\n this.arm()\n return\n }\n this.cancel()\n }\n\n /** Spend the next wait of the burst, or end it when the bound is reached. */\n private owe(): void {\n const wait = this.left[0]\n if (wait === undefined) {\n // The burst spent its whole bound. The state may still be moving, and\n // that is the point: the bound is what stops a state that never settles\n // from being read about forever.\n this.running = false\n return\n }\n this.armWait(wait)\n }\n\n /**\n * (Re)start the collection window. While the page is hidden nothing is armed\n * — hidden time is not charged to the burst — and a trigger that lands then\n * is still collected, because coming back starts a whole window.\n */\n private armWindow(): void {\n const wait = Math.max(this.policy.windowMs, 0)\n if (!this.visibleNow()) {\n this.window = true\n return\n }\n this.cancel()\n this.window = true\n this.timer = this.clock.set(() => {\n this.timer = undefined\n this.window = false\n this.owe()\n }, wait)\n }\n\n /** Put back whatever the burst still owed: its window, or the wait after a read. */\n private arm(): void {\n if (this.window) {\n this.armWindow()\n return\n }\n const wait = this.left[0]\n if (wait !== undefined) this.armWait(wait)\n }\n\n /**\n * (Re)start the absorption window a client action opened. A hidden page keeps\n * absorbing: this window is about what the action caused, not about what the\n * page is doing. When the window closes, the one look an unsettled action owes\n * is taken; the window is restarted by every change it absorbs, so that look\n * waits for the echo to actually stop.\n */\n private armAbsorb(): void {\n this.cancelAbsorb()\n this.absorbed = this.clock.set(() => {\n this.absorbed = undefined\n if (!this.recheck) return\n this.recheck = false\n // A read may already be owed or counting down; the burst never arms two.\n this.next()\n }, Math.max(ACTION_ABSORB_MS, 0))\n }\n\n /** Close the absorption window: changes are news again. */\n private cancelAbsorb(): void {\n if (this.absorbed === undefined) return\n this.clock.clear(this.absorbed)\n this.absorbed = undefined\n }\n\n /**\n * Arm one wait, spending it when it fires. The wait stays in `left` until\n * then, so a page hidden mid-wait takes the whole wait again: hidden time is\n * neither charged to the burst nor lost from it.\n */\n private armWait(wait: number): void {\n if (this.timer !== undefined || !this.visibleNow()) return\n this.timer = this.clock.set(() => {\n this.timer = undefined\n this.left = this.left.slice(1)\n this.onRead()\n }, Math.max(wait, 0))\n }\n\n /** Whether the page counts as on screen now, with an absent `document` read as on screen. */\n private visibleNow(): boolean {\n try {\n return this.policy.visible()\n } catch {\n // A visibility source that cannot answer is not a reason to stop reading.\n return true\n }\n }\n}\n","/**\n * Browser-side reading and driving of the host's park state for one session.\n *\n * The host publishes no push channel of its own — a third-party plugin cannot\n * add a Client Remote namespace or a forwarded event — so this control reads\n * the host's command registry, which is the only live surface it can publish\n * through: `commands.list(sessionId)` is a Client-callable read, and the\n * forwarded `commands/change` event says when to read it again. The resume\n * definition's presence in that list IS the host's \"this session is paused\"\n * fact, and the companion status definition answers with the park state as of\n * the read; this module never derives either from its own press. That one\n * answer carries the record's goal opt-out as well, so the switch beside the\n * pause button can show it without a second host round-trip — and without it,\n * the control renders no switch at all.\n *\n * Every phase the host reaches announces itself on that channel: a transition\n * re-registers the status verb, so a transition is what buys the next read and a\n * read that finds a phase still moving is the answer to the transition that\n * produced it rather than a reason to keep asking (`./burst.ts`). One transition\n * arrives as several registry changes — parking a session also registers its\n * three commands — so the burst's own collection window is what turns a tick of\n * changes into one read per shown session. A press or a goal switch reads as it\n * runs, and the marker changes it causes are that action's own echo: those are\n * absorbed for a short window (`./burst.ts`) rather than read back — except for\n * the one look an action owes when that window closes, if the reading it left\n * behind is still moving, because a transition announced inside the window would\n * otherwise never be read at all.\n *\n * The whole-app control reads the same park and publishes what it found\n * (`./global.ts`), and a header consults that answer before spending a read of\n * its own: with nothing registered, no session can be paused, so a header that\n * already says \"running\" is left saying it rather than paying a round-trip to\n * hear it again. A header the host last showed on its way to a park is never\n * answered that way — that reading is the newer fact — and the derivation is\n * voided by the next whole-app answer that names a registered session, which\n * the forwarded change a park causes is what brings in.\n * @module dsh-pause-session/client/control\n */\n\nimport type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport {\n decodeParkStatus, GOAL_COMMAND, PAUSE_COMMAND, RESUME_COMMAND, RESUME_DEFINITION_ID, STATUS_COMMAND,\n STATUS_DEFINITION_ID,\n} from '../shared.js'\nimport { burstWaits, DEFAULT_BURST_POLICY, ReadBurst } from './burst.js'\nimport type { BurstPolicy } from './burst.js'\nimport type { GlobalReading } from './global.js'\n\n/**\n * The whole-app answer as the session headers read it, or `undefined` before\n * that control has heard from the host. Reading it costs nothing: it is the\n * published snapshot, not a host round-trip.\n */\nexport type AggregateSource = () => GlobalReading | undefined\n\n/**\n * What the Session-header control knows about one session: what the host\n * reported, or that no answer has arrived (`undefined`) or the last read\n * failed. Neither of the latter claims a state the host never reported.\n *\n * `draining` and `parking` are the host's own two answers on the way to\n * `safe`; the control shows them rather than rounding them up, because only\n * `safe` means the pending input is durably stored.\n */\nexport type PausePhase = 'active' | 'draining' | 'parking' | 'safe' | 'flush-failed' | 'unavailable'\n\n/** One host answer about one session. */\nexport interface PauseReading {\n readonly phase: PausePhase\n /** Tools the host reported in flight; empty unless the phase is `draining`. */\n readonly runningTools: readonly string[]\n /**\n * Whether the host's own record lets the next resume re-open this session's\n * goal. Absent — not `true` — when the host did not say: the control shows no\n * switch rather than the product default, because a default here would tell\n * the user their rounds are safe when nobody has answered.\n */\n readonly rearmGoal?: boolean\n}\n\n/** Per-session control state, keyed by Session id so a slot selector can watch one session. */\nexport interface PauseState {\n /** Reading per Session id; absent means no host answer yet. Writable: the store updates through a draft. */\n bySession: Record<string, PauseReading | undefined>\n}\n\nconst INITIAL: PauseState = { bySession: {} }\n\n/** Phases that name a moment on the way to a park rather than a state it reached. */\nconst MOVING: ReadonlySet<PausePhase> = new Set<PausePhase>(['draining', 'parking'])\n\n/** Owns the published reading per session; nothing else writes that state. */\nexport class PauseControl {\n /** State source the Session-header contribution binds its selector hook to. */\n readonly store: SnapshotStore<PauseState> = createSnapshotStore(INITIAL)\n\n /** Sessions this control has shown; a host registry change re-reads every one of them. */\n private readonly shown = new Set<SessionId>()\n /** Highest read sequence requested per session: an answer older than it is dropped. */\n private readonly latest = new Map<SessionId, number>()\n /**\n * Sessions a change has put back on the list of ones to read once — a\n * transition this browser has been told about and has not answered yet.\n */\n private readonly owed = new Set<SessionId>()\n /**\n * Whether the look owed at the close of an echo window is still to be taken.\n * It reads every shown session, exactly as a change does: the look exists\n * because a change may have been swallowed, and a swallowed change carries no\n * payload either.\n */\n private trailing = false\n /**\n * Sessions whose header was answered from the whole-app \"nothing is\n * registered\" instead of a read of their own, and which therefore have to be\n * read for real if that answer stops holding.\n */\n private readonly derived = new Set<SessionId>()\n /** The bounded burst of reads one transition buys; see `./burst.ts`. */\n private readonly burst: ReadBurst\n private reads = 0\n\n /**\n * @param commands - the Client-callable command namespace.\n * @param lifetime - aborts in-flight calls and the burst when the plugin unloads.\n * @param windowMs - how long the burst collects the registrations of one\n * transition before its first read; also the wait after a press.\n * @param policy - the burst waits and visibility source; defaults to the real page.\n * @param aggregate - the whole-app control's published answer; absent means it is never consulted.\n */\n constructor(\n private readonly commands: ClientRemote['commands'],\n private readonly lifetime: AbortSignal,\n windowMs = 400,\n policy: Omit<BurstPolicy, 'windowMs' | 'waitsMs'> = DEFAULT_BURST_POLICY,\n private readonly aggregate: AggregateSource = () => undefined,\n ) {\n this.burst = new ReadBurst(\n () => { void this.readAll() },\n { ...policy, windowMs, waitsMs: burstWaits(windowMs) },\n )\n }\n\n /**\n * Read the host's state for one session.\n * @param sessionId - session whose control was rendered.\n * @returns after this read's answer is published or superseded by a newer one.\n */\n async refresh(sessionId: SessionId): Promise<void> {\n await this.readOne(sessionId)\n this.follow()\n }\n\n /**\n * The host's command registry changed, so every session this control has\n * shown may have moved — the forwarded event carries no payload, so the only\n * honest answer is to look at all of them once. The reads run on the burst's\n * own schedule rather than here, which is what makes a tick's worth of changes\n * — parking a session registers three commands, and a phase change re-registers\n * one more — one burst instead of one each. A change that is this browser's own\n * action's echo owes nothing: the action read the host as it ran.\n */\n refreshAll(): void {\n if (this.shown.size === 0 || this.lifetime.aborted) return\n if (!this.burst.trigger()) return\n for (const sessionId of this.shown) this.owed.add(sessionId)\n }\n\n /**\n * One pass of the burst: every shown session a change still owes a read. The\n * forwarded event carries no payload, so a change may have touched any of\n * them and each is read once. A session the whole-app answer has already\n * spoken for is answered from it instead of being asked about; see\n * {@link answerIsKnown}.\n */\n private async readAll(): Promise<void> {\n if (this.lifetime.aborted) {\n this.burst.stop()\n return\n }\n const pass = this.owed.size > 0 ? [...this.owed] : this.trailing ? [...this.shown] : []\n // Cleared before the reads, not after: a change that lands while this pass\n // is in flight is one this pass cannot have answered, so it survives into\n // the next read rather than being absorbed into this one.\n this.owed.clear()\n this.trailing = false\n for (const sessionId of pass) {\n if (this.answerIsKnown(sessionId)) {\n this.deriveActive(sessionId)\n continue\n }\n await this.readOne(sessionId)\n }\n this.follow()\n }\n\n /** One session's read, published unless a newer read of that session supersedes it. */\n private async readOne(sessionId: SessionId): Promise<void> {\n this.shown.add(sessionId)\n // A host answer replaces whatever the aggregate said about this header.\n this.derived.delete(sessionId)\n const sequence = ++this.reads\n this.latest.set(sessionId, sequence)\n const reading = await this.read(sessionId)\n // A newer read was requested while this one was in flight; its answer is\n // the current one, so publishing this stale one would show the browser a\n // state the host has already moved past.\n if (this.latest.get(sessionId) !== sequence) return\n this.publish(sessionId, reading)\n }\n\n /**\n * Press the control: pause an active session, or take back a pause that is\n * still on its way in (or already settled). The phase shown afterwards is the\n * host's answer, never an assumed flip.\n * @param sessionId - session whose control was pressed.\n * @returns after the control re-reads the host.\n */\n async toggle(sessionId: SessionId): Promise<void> {\n const current = this.store.getSnapshot().bySession[String(sessionId)]?.phase\n const line = current === undefined || current === 'active'\n ? `/${PAUSE_COMMAND}`\n : `/${RESUME_COMMAND}`\n // The press is a client action: the markers the host registers or\n // unregisters as it runs are its own echo, and the read below is the answer\n // to all of it.\n this.burst.absorb()\n const executed = await this.commands.execute(sessionId, line, [], this.lifetime)\n if (!executed.ok) {\n this.publish(sessionId, { phase: 'unavailable', runningTools: [] })\n this.follow()\n return\n }\n // `undefined` means the host resolved no such command, i.e. this reading\n // was stale; re-reading is the only honest response.\n await this.refresh(sessionId)\n }\n\n /**\n * Flip this session's goal opt-out. The verb belongs to the session it\n * changes — the host registers it there, and it takes no other target — so\n * this press needs no listed row. What the switch shows afterwards is the\n * host's next answer, never a local flip: a refused switch leaves the record\n * exactly as it was, and the re-read is what says so.\n * @param sessionId - session whose switch was pressed.\n * @returns after the control re-reads the host.\n */\n async toggleGoal(sessionId: SessionId): Promise<void> {\n // The switch is a client action too: the record change it causes announces\n // itself through the registry, and the re-read below is the answer.\n this.burst.absorb()\n await this.commands.execute(sessionId, `/${GOAL_COMMAND}`, [], this.lifetime)\n await this.refresh(sessionId)\n }\n\n /** Ask the host what this session's park state is, in the browser's vocabulary. */\n private async read(sessionId: SessionId): Promise<PauseReading> {\n const listed = await this.commands.list(sessionId)\n if (!listed.ok) return { phase: 'unavailable', runningTools: [] }\n const paused = listed.value.some(descriptor => descriptor.definitionId === RESUME_DEFINITION_ID)\n if (!paused) return { phase: 'active', runningTools: [] }\n const status = listed.value.find(descriptor => descriptor.definitionId === STATUS_DEFINITION_ID)\n if (status === undefined) return { phase: 'parking', runningTools: [] }\n // The descriptor is only a marker; the command itself reports the state as\n // of this read, which is what makes a moving phase observable at all.\n const answered = await this.commands.execute(sessionId, `/${STATUS_COMMAND}`, [], this.lifetime)\n if (!answered.ok || answered.value === undefined) return { phase: 'unavailable', runningTools: [] }\n const text = answered.value.result.kind === 'success' ? answered.value.result.text : undefined\n const decoded = text === undefined ? undefined : decodeParkStatus(text)\n if (decoded === undefined) {\n // The marker says paused but the status read did not answer in the shared\n // vocabulary: report the pause without inventing a phase or a goal state.\n return { phase: 'parking', runningTools: [] }\n }\n return {\n phase: decoded.phase,\n runningTools: decoded.runningTools,\n ...decoded.rearmGoal === undefined ? {} : { rearmGoal: decoded.rearmGoal },\n }\n }\n\n private publish(sessionId: SessionId, reading: PauseReading): void {\n this.store.update((state) => {\n state.bySession = { ...state.bySession, [String(sessionId)]: reading }\n })\n }\n\n /**\n * Whether the park has already answered for this header: with nothing\n * registered, no session can be paused, so a header that says this session is\n * running carries the whole fact and the read that would confirm it is a host\n * round-trip plus two records in that session's log for nothing.\n *\n * A header the host last showed on its way to a park is never answered this\n * way: that reading is newer than the aggregate the derivation rests on, and\n * only a fresh host answer may replace it.\n */\n private answerIsKnown(sessionId: SessionId): boolean {\n const phase = this.store.getSnapshot().bySession[String(sessionId)]?.phase\n if (phase !== undefined && phase !== 'active') return false\n return this.nothingParked()\n }\n\n /**\n * Whether the whole-app answer says the park holds nothing at all: the one\n * predicate the derivation below and its revocation share.\n */\n private nothingParked(): boolean {\n const aggregate = this.aggregate()\n return aggregate?.kind === 'reported' && aggregate.status.registered === 0\n }\n\n /**\n * Answer one header from the whole-app verdict, and remember that it was that\n * kind of answer: the sessions to read for real are exactly the ones the\n * aggregate may have been wrong about.\n */\n private deriveActive(sessionId: SessionId): void {\n this.derived.add(sessionId)\n this.publish(sessionId, { phase: 'active', runningTools: [] })\n }\n\n /**\n * The whole-app answer moved. Whatever was answered from \"nothing is\n * registered\" is read for real the moment that stops holding — the session a\n * header stood in for may be the one the park just took, possibly parked from\n * another window whose change this browser only sees as the forwarded event.\n * Called on every published aggregate, so it must do nothing while the\n * derivation still holds.\n */\n reconsider(): void {\n if (this.derived.size === 0 || this.lifetime.aborted || this.nothingParked()) return\n for (const sessionId of this.derived) this.owed.add(sessionId)\n this.derived.clear()\n // What these headers say is known to be wrong now, so this does not wait out\n // anything — least of all the absorption window of an action the user took\n // in the same moment. Absorption is for the changes an action caused, never\n // for what a reading just revealed.\n this.burst.next()\n }\n\n /**\n * Decide what the burst owes after a read: another look while a change this\n * browser has not answered is still outstanding, and nothing once none is.\n *\n * A phase that is still moving is left alone. The host announces every\n * transition on its command channel — the flush that settles a park included,\n * which changes no registration of its own — so a read that finds `draining`\n * or `parking` is the answer to the transition that produced it, and watching\n * that phase from here would spend reads on a move that is going to speak for\n * itself. What the burst still owes is the delivery of an announcement: a read\n * still armed when the change arrived, and a read for a change that landed\n * while the last pass was in flight.\n *\n * The one transition that cannot speak for itself is the one this browser's\n * own echo window swallowed: a press reads the host, the park moves while the\n * window is still open, and the changes that would have announced it look\n * exactly like the press's own registrations. So an action that left a phase\n * still moving asks for one look when that window closes, and only then — the\n * read is owed at a moment, not watched. Once nothing is owed, the last\n * reading stays on screen; the next change, the next user action, or the shell\n * handing this control a session again is what asks next.\n */\n private follow(): void {\n if (this.lifetime.aborted) {\n this.burst.stop()\n return\n }\n if (this.owed.size === 0) {\n // The look is owed only while both hold: the window is still swallowing\n // changes, and the reading the action left is one of the phases whose next\n // step a change would have announced.\n const hidden = this.burst.absorbing && this.moving()\n if (hidden) this.burst.recheckAfterEcho()\n this.trailing = hidden\n this.burst.stop()\n return\n }\n this.burst.next()\n }\n\n /**\n * Whether any shown session's last reading is a phase the host has not\n * finished walking — the only states an action's own echo could be hiding a\n * transition behind.\n */\n private moving(): boolean {\n const shown = this.store.getSnapshot().bySession\n for (const sessionId of this.shown) {\n const phase = shown[String(sessionId)]?.phase\n if (phase !== undefined && MOVING.has(phase)) return true\n }\n return false\n }\n\n /**\n * Follow the page in and out of view. A hidden page is not being read — and\n * the read the burst was about to take is not lost, it waits for the page to\n * come back.\n */\n watchVisibility(): void {\n this.burst.checkVisibility()\n }\n}\n","/**\n * The controls' phase → view mapping.\n *\n * Kept pure functions of the published reading so the rendering decision — what\n * the button says, whether it accepts a press, and which state label is visible\n * — is asserted without a browser renderer. Labels are KEYS, not copy: the\n * visible sentence is localized, and the host's own names join it as data.\n * @module dsh-pause-session/client/view\n */\n\nimport type { GlobalReading, TriageReading } from './global.js'\nimport type { PauseReading } from './control.js'\nimport type { PauseKey } from './locales.js'\nimport type { TriageRow } from '../shared.js'\nimport type { TriageAction } from '../shared.js'\n\n/** What one reading renders as. */\nexport interface PauseView {\n /** Whether the host reported this session paused. */\n readonly paused: boolean\n /** Whether the control accepts a press; an unknown or failed reading does not. */\n readonly enabled: boolean\n /** Accessible name of the button. */\n readonly ariaKey: PauseKey\n /** Label shown beside the button while the control has something to say. */\n readonly labelKey?: PauseKey\n /** Tools in flight, taken verbatim from the host; rendered only with `labelKey`. */\n readonly runningTools: readonly string[]\n /**\n * The host-reported goal opt-out, or `undefined` when the host did not answer\n * it. Absent means the goal switch is not rendered at all: a switch has to\n * state a fact, and the only fact available is the one the record holds.\n */\n readonly rearmGoal?: boolean\n}\n\n/**\n * Whether the pause control renders on one session at all.\n *\n * The control is the user's button for a session's work, and that work includes\n * the subagents under it — which is why the press covers the tree and why the\n * button does not belong inside the tree. The client can tell: a Session\n * snapshot carries a `subagent` field, the same signal the read-only composer\n * reads. A subagent's service is not the user's to pause: its parent's control\n * already speaks for it, and a second button here would offer to stop \"a\n * session\" from inside a child.\n * @param subagent - the session snapshot's own subagent fact, absent when unknown.\n * @returns whether this session's header renders the control.\n */\nexport function showsPauseControl(subagent: { readonly address: unknown } | null | undefined): boolean {\n return subagent === null || subagent === undefined\n}\n\n/**\n * One reading's goal opt-out, as the view carries it. Only an answered flag\n * becomes a field: a missing one must stay missing through the mapping, or the\n * renderer would show the product default as if the host had said it.\n * @param reading - the host's answer.\n * @returns the field to spread into the view, empty when the host said nothing.\n */\nfunction goalOf(reading: PauseReading): { rearmGoal?: boolean } {\n return reading.rearmGoal === undefined ? {} : { rearmGoal: reading.rearmGoal }\n}\n\n/**\n * Map one reading to its view.\n * @param reading - the host-reported reading, or `undefined` before the first answer.\n * @returns the view for that reading.\n */\nexport function pauseView(reading: PauseReading | undefined): PauseView {\n switch (reading?.phase) {\n case 'active':\n // Not parked, so no record exists for a resume to obey: there is nothing\n // to switch, and saying \"on\" would describe a park that is not there.\n return { paused: false, enabled: true, ariaKey: 'pause.aria', runningTools: [] }\n case 'draining':\n // Still working: pressing again is exactly how the user takes the pause\n // back, so the control stays live.\n return {\n paused: true,\n enabled: true,\n ariaKey: 'draining.aria',\n labelKey: 'state.draining',\n runningTools: reading.runningTools,\n ...goalOf(reading),\n }\n case 'parking':\n return {\n paused: true,\n enabled: true,\n ariaKey: 'parking.aria',\n labelKey: 'state.parking',\n runningTools: [],\n ...goalOf(reading),\n }\n case 'safe':\n return {\n paused: true,\n enabled: true,\n ariaKey: 'safe.aria',\n labelKey: 'state.safe',\n runningTools: [],\n ...goalOf(reading),\n }\n case 'flush-failed':\n return {\n paused: true,\n enabled: true,\n ariaKey: 'flushFailed.aria',\n labelKey: 'state.flushFailed',\n runningTools: [],\n ...goalOf(reading),\n }\n case 'unavailable':\n return { paused: false, enabled: false, ariaKey: 'unavailable.aria', runningTools: [] }\n case undefined:\n return { paused: false, enabled: false, ariaKey: 'checking.aria', runningTools: [] }\n }\n}\n\n/** One named fact from the whole-app report, with the copy key that introduces it. */\nexport interface GlobalCaveat {\n /** What this list of names is. */\n readonly key: PauseKey\n /** Host names, rendered verbatim. */\n readonly names: readonly string[]\n}\n\n/** Which verb a whole-app press runs. */\nexport type GlobalAction = 'pause-all' | 'resume-all' | 'none'\n\n/** One decision as the list renders it. */\nexport interface TriageActionView {\n /** The decision this button takes. */\n readonly action: TriageAction\n /** Visible label. */\n readonly labelKey: PauseKey\n /** What it does, for the accessible name. */\n readonly ariaKey: PauseKey\n}\n\n/** The four decisions, in the order a user reads them: least to most cautious. */\nexport const TRIAGE_ACTION_VIEWS: readonly TriageActionView[] = [\n { action: 'continue', labelKey: 'triage.continue', ariaKey: 'triage.continue.aria' },\n { action: 'note', labelKey: 'triage.note', ariaKey: 'triage.note.aria' },\n { action: 'review', labelKey: 'triage.review', ariaKey: 'triage.review.aria' },\n { action: 'leave', labelKey: 'triage.leave', ariaKey: 'triage.leave.aria' },\n]\n\n/**\n * The fifth listed verb, rendered apart from the four decisions: it is a\n * switch on what the NEXT resume may spend, not a way to decide this row.\n */\nexport const TRIAGE_GOAL_VIEW: TriageActionView = {\n action: 'goal',\n labelKey: 'triage.goal',\n ariaKey: 'triage.goal.aria',\n}\n\n/** One listed session, as the panel renders it. */\nexport interface TriageRowView {\n /** Host data, rendered verbatim. */\n readonly sessionId: string\n /** The durable outcome, as a word. */\n readonly phaseKey: PauseKey\n /** Stop position: the event the log ends on and the step it belongs to. */\n readonly stopped?: string\n /** The tools whose outcomes were never recorded. */\n readonly unknownTools: readonly string[]\n /** Whether the host said it can hand this session the request it owes. */\n readonly wakeable: boolean\n /** Whether the log could be read at all. */\n readonly readable: boolean\n /** Whether resuming this session may re-open its goal and spend its rounds. */\n readonly rearmGoal: boolean\n}\n\n/** What the decision-list panel renders. */\nexport interface TriageView {\n /** Whether the panel has anything to show; a host that cannot answer shows nothing. */\n readonly visible: boolean\n /** Whether any listed session can actually be continued. */\n readonly rows: readonly TriageRowView[]\n}\n\n/** The word each non-clean phase is shown as. */\nconst PHASE_KEY: Readonly<Record<TriageRow['phase'], PauseKey>> = {\n draining: 'triage.phase.draining',\n parking: 'triage.phase.parking',\n 'flush-failed': 'triage.phase.flushFailed',\n}\n\n/**\n * Map one decision-list reading to its view.\n * @param reading - the host-reported list, or `undefined` before the panel asked.\n * @returns the rows as the panel renders them.\n */\nexport function triageView(reading: TriageReading | undefined): TriageView {\n if (reading === undefined || reading.kind === 'unavailable') return { visible: false, rows: [] }\n return {\n visible: true,\n rows: reading.rows.map(row => ({\n sessionId: row.sessionId,\n phaseKey: PHASE_KEY[row.phase],\n ...row.lastEvent === undefined\n ? {}\n : {\n stopped: row.turn === undefined\n ? row.lastEvent\n : `${row.lastEvent} · turn ${String(row.turn)}${row.step === undefined ? '' : ` step ${String(row.step)}`}`,\n },\n unknownTools: row.traces.map(trace => trace.tool),\n wakeable: row.wakeable,\n readable: row.reading === 'read',\n rearmGoal: row.rearmGoal,\n })),\n }\n}\n\n/** What the whole-app reading renders as. */\nexport interface GlobalPauseView {\n /** Whether the control renders at all; a control with no host answer claims nothing. */\n readonly visible: boolean\n /** Whether the control accepts a press. */\n readonly enabled: boolean\n /** Accessible name of the button. */\n readonly ariaKey: PauseKey\n /**\n * Headline shown beside the button. Absent while there is nothing to report:\n * an untouched park renders the bare affordance, not a sentence about itself.\n */\n readonly labelKey?: PauseKey\n /** Verb the press runs. */\n readonly action: GlobalAction\n /** Host numbers, rendered verbatim beside the label. */\n readonly detail: string\n /** How many decisions are waiting; shown on the panel's own entry point. */\n readonly unclean: number\n /** Named facts the user must not miss; rendered on the tooltip. */\n readonly caveats: readonly GlobalCaveat[]\n}\n\n/**\n * Map one whole-app reading to its view.\n * @param reading - the host-reported reading, or `undefined` before the first answer.\n * @returns the view for that reading.\n */\nexport function globalPauseView(reading: GlobalReading | undefined): GlobalPauseView {\n if (reading === undefined) {\n return { visible: false, enabled: false, ariaKey: 'all.checking.aria', action: 'none', detail: '', caveats: [], unclean: 0 }\n }\n // A host that cannot answer gets no control: this plugin never renders a\n // button whose state it would have to invent.\n if (reading.kind === 'unavailable') {\n return { visible: false, enabled: false, ariaKey: 'unavailable.aria', action: 'none', detail: '', caveats: [], unclean: 0 }\n }\n const { status } = reading\n const caveats: GlobalCaveat[] = []\n // A missing surface subsumes its per-session names: the absence itself is the\n // fact, and listing every session it blinds would say less.\n if (status.blind.includes('jobs')) caveats.push({ key: 'all.caveat.blindJobs', names: [] })\n else if (status.jobs.length > 0) caveats.push({ key: 'all.caveat.jobs', names: status.jobs })\n if (status.blind.includes('trees')) caveats.push({ key: 'all.caveat.blindTrees', names: [] })\n else if (status.unreadTrees.length > 0) caveats.push({ key: 'all.caveat.unread', names: status.unreadTrees })\n if (status.unresumable.length > 0) caveats.push({ key: 'all.caveat.unresumable', names: status.unresumable })\n const verdict = status.verdict\n // The stalled names are the sessions this process will not wake, so they\n // belong on the tooltip whenever there are any — as the whole story when\n // nothing is still moving, and beside the moving ones when there are.\n if (verdict !== 'safe' && status.stalled.length > 0) {\n caveats.push({ key: 'all.caveat.stalled', names: status.stalled })\n }\n // The whole-app verdict calls these \"not safe\"; the list is where the user\n // decides them, so the panel's entry point counts the same set.\n const unclean = status.stalled.length\n const base = { visible: true, enabled: true, caveats, unclean } as const\n switch (verdict) {\n case 'idle':\n // Nothing parked: the pill is the affordance itself, so it carries no\n // sentence about a park that does not exist.\n return { ...base, ariaKey: 'all.pause.aria', action: 'pause-all', detail: '' }\n case 'safe':\n return {\n ...base,\n ariaKey: 'all.resume.aria',\n action: 'resume-all',\n labelKey: 'all.safe',\n detail: `${String(status.safe)}/${String(status.registered)}`,\n }\n case 'waiting':\n // Only a session still on its way can be released from here. When every\n // registered session is one this process will not wake — an adopted\n // pause, a failed flush, a record it cannot read — the release would be\n // an action the host refuses, so the pill states the fact and hands the\n // user to the list instead of offering it. A session holding the park\n // point with its flush pending is still releasable, so it keeps the verb.\n return status.draining.length > 0 || status.parking > 0\n ? {\n ...base,\n ariaKey: 'all.resume.aria',\n action: 'resume-all',\n labelKey: 'all.finishing',\n detail: `${String(status.safe)}/${String(status.registered)}`,\n }\n : {\n ...base,\n enabled: false,\n ariaKey: 'all.unclean.aria',\n action: 'none',\n labelKey: 'all.unclean',\n detail: String(status.stalled.length),\n }\n }\n}\n","/**\n * Browser-side reading and driving of the host's whole-app park state.\n *\n * The same one live channel the per-session control uses — a session command\n * run through `ctx.remote.commands.execute`, plus the forwarded\n * `commands/change` event as the only notification — carries the aggregate\n * here. There is no push channel a third-party plugin can add, so the browser\n * asks: `pause-state-all` is registered globally and its RESULT carries the\n * whole-app status as of the read.\n *\n * The command channel is session-addressed, so the control needs one session\n * to talk through. It picks a user session the host can serve (never a\n * subagent's own session), preferring the one on screen; with no session at\n * all there is nothing to ask, and the control claims nothing rather than\n * inventing an answer. Once the host's own answer names the session the park\n * belongs to, reads are addressed there instead: a read writes two records into\n * the address session's transcript, and they belong in the session being parked\n * rather than in whatever the user happens to be looking at. That name outlives\n * the phases — a session that drained and parked cleanly names nothing else — so\n * a clean park is addressed like a moving one. Failing that, the session the\n * last answer reported as registered is remembered, then the one a read was last\n * served through — an address change is the user looking somewhere else, not\n * news about the park, and a park that has emptied out is no reason to start\n * writing rows into every session the user opens next. An address that stops\n * answering (its agent is gone) is forgotten and the read falls back to the next\n * one, the session the shell asks with last, so a stale address costs an honest\n * answer rather than a control that can never speak again.\n *\n * Every phase a session reaches announces itself through the registry — a\n * transition re-registers that session's status verb — so a change is what buys\n * the next read, and the flush that settles a park is announced like every other\n * step (`./burst.ts`). A read that finds work still in flight is therefore the\n * answer to the transition that produced it rather than a reason to keep asking;\n * what the burst still owes is the delivery of a change, and the rows of a panel\n * whose own read did not answer. A settled answer is left on screen unread: the\n * panel stays readable and the pill keeps saying what it says, but the browser\n * stops spending host round-trips and log records on a state that has stopped\n * moving. What owes a read is the host's forwarded registry change, the user's\n * own press or decision, and the first read at mount; once an answer is on\n * screen, the shell handing this control another session to ask through is none\n * of those. A press, a decision and an opened panel each read the host as they\n * run, and the changes they set off are that action's own echo: those are\n * absorbed for a short window (`./burst.ts`), so a forwarded change owes a read\n * exactly when something moved that this browser did not do — with one look owed\n * at the close of that window when the reading the action left is still moving,\n * which is how a park that reaches its final phase inside the echo window is\n * still read.\n * @module dsh-pause-session/client/global\n */\n\nimport type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport {\n decodeGlobalStatus, decodeTriageList, RESUME_ALL_COMMAND, STATE_ALL_COMMAND, PAUSE_ALL_COMMAND,\n TRIAGE_COMMAND, TRIAGE_CONTINUE_COMMAND, TRIAGE_GOAL_COMMAND, TRIAGE_LEAVE_COMMAND, TRIAGE_NOTE_COMMAND,\n TRIAGE_REVIEW_COMMAND,\n} from '../shared.js'\nimport type { GlobalStatus, TriageAction, TriageRow } from '../shared.js'\nimport { burstWaits, DEFAULT_BURST_POLICY, ReadBurst } from './burst.js'\nimport type { BurstPolicy } from './burst.js'\nimport { globalPauseView } from './view.js'\n\n/** What the whole-app control knows: the host's answer, or that it could not ask. */\nexport type GlobalReading =\n | { readonly kind: 'reported'; readonly status: GlobalStatus }\n | { readonly kind: 'unavailable' }\n\n/** What the decision-list panel knows: the host's rows, or that it could not ask. */\nexport type TriageReading =\n | { readonly kind: 'reported'; readonly rows: readonly TriageRow[] }\n | { readonly kind: 'unavailable' }\n\n/** Whole-app control state. */\nexport interface GlobalPauseState {\n /** The host's answer; absent until one arrives. Writable: the store updates through a draft. */\n global: GlobalReading | undefined\n /** Whether the user asked for the decision list; the panel reads only when it is open. */\n panelOpen: boolean\n /** The host's decision list; absent until the panel asks for it. */\n triage: TriageReading | undefined\n}\n\nconst INITIAL: GlobalPauseState = { global: undefined, panelOpen: false, triage: undefined }\n\n/** The command one listed verb runs, named from the one vocabulary the host registers. */\nexport const TRIAGE_DECISION_COMMAND: Readonly<Record<TriageAction, string>> = {\n continue: TRIAGE_CONTINUE_COMMAND,\n note: TRIAGE_NOTE_COMMAND,\n review: TRIAGE_REVIEW_COMMAND,\n leave: TRIAGE_LEAVE_COMMAND,\n goal: TRIAGE_GOAL_COMMAND,\n}\n\n/**\n * The session a whole-app command is addressed to.\n *\n * A subagent's own session is never addressed: its agent belongs to a parent\n * and is not the host's business to drive from the shell. Among the rest, the\n * one on screen is the best address (its agent is live by construction), then\n * any session the host reports as running, then any at all.\n * @param state - the session list snapshot the shell provides.\n * @returns the address, or `undefined` when the app has no session to ask through.\n */\nexport function addressingSession(state: {\n readonly ids: readonly SessionId[]\n readonly byId: Readonly<Record<string, {\n readonly running?: boolean\n readonly origin?: string\n readonly parentId?: unknown\n readonly retainedBy?: { readonly mainView?: number }\n } | undefined>>\n}): SessionId | undefined {\n const usable = state.ids.filter((id) => {\n const row = state.byId[String(id)]\n return row !== undefined && row.origin !== 'subagent' && row.parentId === undefined\n })\n return usable.find(id => (state.byId[String(id)]?.retainedBy?.mainView ?? 0) > 0)\n ?? usable.find(id => state.byId[String(id)]?.running === true)\n ?? usable[0]\n}\n\n/**\n * The registered sessions one whole-app answer names, best address first: the\n * one still draining is the one this host is executing against right now, the\n * rest is the host's own vocabulary for a registered session it is not going to\n * move any further.\n *\n * A stalled or unreadable record is deliberately not an address for a read —\n * this host may not hold that session at all, and a read that could not be\n * delivered would report \"unavailable\" for a transport reason rather than a\n * state — but it is still a registered session, which is all the remembered\n * address is for. The host's own answer is JSON: the branded ids arrive as the\n * strings they were encoded as, exactly like the ids the shell hands the pill.\n * @param status - the host's last whole-app answer.\n * @returns the registered sessions the answer names, best first.\n */\nfunction registeredSessions(status: GlobalStatus): string[] {\n return [\n ...status.draining,\n ...status.stalled,\n ...status.unresumable,\n ...status.unreadTrees,\n ]\n}\n\n/**\n * The session a registered park owns, when the host's answer names one that is\n * still moving. This is how a read stops landing in whatever session the user\n * happens to be looking at.\n * @param status - the host's last whole-app answer.\n * @returns a draining session, or `undefined` when the answer names none.\n */\nexport function registeredAddress(status: GlobalStatus): SessionId | undefined {\n const draining = status.draining[0]\n return draining === undefined ? undefined : draining as SessionId\n}\n\n/**\n * The session the host names as the park's own, whatever phase the park\n * reached — the address a whole-app read belongs at.\n *\n * This is the fact {@link registeredAddress} cannot carry once a park is clean:\n * a session that reached `safe` has nothing left to do, so no bucket names it,\n * and a reader in another window would fall back to whatever session it is\n * showing. An answer written before this field existed names no session here:\n * an absent address, never one to guess at.\n * @param status - the host's last whole-app answer.\n * @returns the session the park belongs to, or `undefined` when the answer names none.\n */\nexport function parkedAddress(status: GlobalStatus): SessionId | undefined {\n return status.parkedAt === undefined ? undefined : status.parkedAt as SessionId\n}\n\n/** Owns the published whole-app reading; nothing else writes that state. */\nexport class PauseGlobalControl {\n /** State source the whole-app contribution binds its selector hook to. */\n readonly store: SnapshotStore<GlobalPauseState> = createSnapshotStore(INITIAL)\n\n /** The session the shell asked this control through; reads start here. */\n private address: SessionId | undefined\n /**\n * The last session an answer reported as registered — the address a read\n * falls back to when the host's own answer names no parked session, which is\n * every reading written before that field existed.\n *\n * The buckets that feed it name only what is still moving, so they say\n * nothing about a park that finished cleanly; the park's own name is the live\n * fact, and this memory is what keeps the rows in the park's transcript for an\n * answer that predates it. An empty registry does not clear it: a park whose\n * sessions stopped moving has not moved out of its sessions.\n */\n private lastRegistered: SessionId | undefined\n /**\n * The session an aggregate read was last served through. Later reads keep\n * going there: every read is two records in the address session's log, and\n * the shell's address follows whatever the user is looking at, which is not\n * where a row about the park belongs.\n */\n private served: SessionId | undefined\n /** Highest decision-list read requested: an answer older than it is dropped. */\n private latestTriage = 0\n /** Highest read sequence requested: an answer older than it is dropped. */\n private latest = 0\n /**\n * Whether a registry change has arrived that no completed read pass has\n * answered. Set only for a change that is news — an absorbed echo of this\n * browser's own action is already answered by that action's read.\n */\n private owed = false\n /** The bounded burst of reads one transition buys; see `./burst.ts`. */\n private readonly burst: ReadBurst\n\n /**\n * @param commands - the Client-callable command namespace.\n * @param lifetime - aborts in-flight calls and the burst when the plugin unloads.\n * @param windowMs - how long the burst collects the registrations of one\n * transition before its first read; also the wait after a press.\n * @param policy - the burst waits and visibility source; defaults to the real page.\n */\n constructor(\n private readonly commands: ClientRemote['commands'],\n private readonly lifetime: AbortSignal,\n windowMs = 400,\n policy: Omit<BurstPolicy, 'windowMs' | 'waitsMs'> = DEFAULT_BURST_POLICY,\n ) {\n this.burst = new ReadBurst(\n () => { void this.readAll() },\n { ...policy, windowMs, waitsMs: burstWaits(windowMs) },\n )\n }\n\n /**\n * The shell handed this control a session: the one the pill mounted on, or\n * the one the user has just moved to. Only a control with no answer yet reads\n * here — the view moving is not the park moving, and after the first answer\n * the forwarded registry change and the user's own press are what ask again.\n * A read that came back with nothing is not an answer, so moving to another\n * session is one more chance to reach the host rather than a control that\n * stays mute until something else happens to change. The address is kept\n * either way: it is where this control's presses and its decision list go, and\n * the fallback a remembered address falls back to.\n * @param sessionId - the session the shell asks through.\n * @returns after this read's answer is published or superseded by a newer one.\n */\n async refresh(sessionId: SessionId): Promise<void> {\n this.address = sessionId\n if (this.answered()) return\n await this.readAggregate()\n this.follow()\n }\n\n /** Whether the host has answered: a state, not the absence of one it never sent. */\n private answered(): boolean {\n return this.store.getSnapshot().global?.kind === 'reported'\n }\n\n /**\n * The host's command registry changed: something may have moved, so a burst\n * is owed. The reads run on the burst's own schedule rather than here, which\n * is what coalesces a tick's worth of changes — parking N sessions announces\n * three registrations each, and a phase change re-registers one more — into\n * one burst. A change that is this browser's own action's echo owes nothing:\n * the action read the host as it ran.\n */\n refreshAll(): void {\n if (this.address === undefined || this.lifetime.aborted) return\n if (!this.burst.trigger()) return\n this.owed = true\n }\n\n /**\n * One read of the whole screen: the aggregate, plus the decision list while\n * the user has it open. A closed panel spends no command calls on rows nobody\n * is reading.\n */\n private async readAll(): Promise<void> {\n if (this.target() === undefined || this.lifetime.aborted) {\n this.owed = false\n this.burst.stop()\n return\n }\n // Cleared before the reads, not after: a change that lands while this pass\n // is in flight is one this pass cannot have answered, so it survives into\n // the next read rather than being absorbed into this one.\n this.owed = false\n await this.readAggregate()\n if (this.store.getSnapshot().panelOpen) await this.readTriage()\n // One burst decision per pass: taken per read, two reads that disagree\n // about what is moving would end the burst and start it again, which is a\n // heartbeat with extra steps.\n this.follow()\n }\n\n /**\n * The session a read goes through: the one the park is still draining, else\n * the one the host names as the park's own, else the one a read was last\n * served through, else the session the shell is on now. The park's own address\n * comes first because a read's rows land in the transcript it was taken\n * through, and the park — not the reader — is what they are about; its name\n * survives the clean case, where every bucket above is empty and a park that\n * finished would otherwise have no address at all.\n * @param skip - an address this read already tried and could not reach. The\n * fallback is the next candidate rather than the same one again.\n */\n private target(skip?: SessionId): SessionId | undefined {\n const current = this.store.getSnapshot().global\n const named = current?.kind === 'reported' ? current.status : undefined\n const candidates: (SessionId | undefined)[] = [\n named === undefined ? undefined : registeredAddress(named),\n named === undefined ? undefined : parkedAddress(named),\n this.lastRegistered,\n this.served,\n this.address,\n ]\n return candidates.find(candidate => candidate !== undefined && candidate !== skip)\n }\n\n /**\n * Ask the host for the aggregate through the session a read goes through,\n * with the one fallback an address that stopped answering is worth: the\n * remembered address is forgotten, and the read is taken again through the\n * next one — the session the shell is on at the latest. A memory of a session\n * this host no longer holds would otherwise leave the control unable to say\n * anything at all, and a single retry is the whole price of keeping it honest.\n */\n private async readAggregate(): Promise<void> {\n const address = this.target()\n if (address === undefined) return\n if (!await this.readAt(address)) return\n const fallback = this.target(address)\n if (fallback === undefined) return\n await this.readAt(fallback)\n }\n\n /**\n * Publish one aggregate answer, unless a newer read was requested while this\n * one was in flight: that answer would show a state the host has moved past.\n * A served address is remembered, and one that did not answer is forgotten —\n * that is what makes the read fall back to the next address rather than ask\n * it again.\n * @param sessionId - the session this read goes through.\n * @returns whether this read came back without an answer; a superseded read is\n * not one, because the newer read already owns the outcome.\n */\n private async readAt(sessionId: SessionId): Promise<boolean> {\n const sequence = ++this.latest\n const reading = await this.read(sessionId)\n if (this.latest !== sequence) return false\n if (reading.kind === 'reported') {\n this.served = sessionId\n const registered = registeredSessions(reading.status)[0]\n if (registered !== undefined) this.lastRegistered = registered as SessionId\n } else {\n // An address this host could not reach is not one to ask again: forgetting\n // the served address and the remembered park address is what sends the\n // retry to the next candidate. The park's own name is not forgotten — it\n // is the host's fact, and the next read may well reach it.\n if (this.served === sessionId) this.served = undefined\n if (this.lastRegistered === sessionId) this.lastRegistered = undefined\n }\n this.publish(reading)\n return reading.kind === 'unavailable'\n }\n\n /**\n * Open or close the decision list. Opening asks the host for the rows; there\n * is no state to show without that answer. Opening is also a client action\n * that reads, so the changes it sets off are absorbed rather than read back,\n * and whether the burst owes anything is what its own read decides.\n * @param open - whether the panel should be showing.\n */\n setPanel(open: boolean): void {\n this.store.update((state) => { state.panelOpen = open })\n if (open) {\n this.burst.absorb()\n void this.refreshTriage()\n return\n }\n this.follow()\n }\n\n /**\n * Ask the host for the decision list through the session the user is on. The\n * list is recorded per calling session and the decisions act only on rows\n * that session was shown, so this read — unlike the aggregate — stays on the\n * shell's own address.\n */\n async refreshTriage(): Promise<void> {\n await this.readTriage()\n this.follow()\n }\n\n /**\n * Read the aggregate again because the user asked this control to act: the\n * address is the session the shell is on, since a press is a user action on\n * the session the user is looking at, and the host's answer is what the\n * control shows next.\n * @param sessionId - the session the shell is on.\n */\n private async reread(sessionId: SessionId): Promise<void> {\n this.address = sessionId\n await this.readAggregate()\n this.follow()\n }\n\n /** One decision-list read, addressed at the session the user is on. */\n private async readTriage(): Promise<void> {\n const address = this.address\n if (address === undefined) return\n const sequence = ++this.latestTriage\n const reading = await this.readTriageRows(address)\n // A newer read was requested while this one was in flight; its answer is\n // the current one.\n if (this.latestTriage !== sequence) return\n this.store.update((state) => { state.triage = reading })\n }\n\n /**\n * Take one listed verb on one listed session, addressed through the session\n * the user is on. The list is read first, because the host acts only on rows\n * it has shown the calling session, and read again afterwards because the\n * host's answer — not this control's assumption — is what the next render\n * shows: a decision can release a session, and the goal switch changes what\n * the row itself says.\n * @param sessionId - the listed session to act on.\n * @param action - which listed verb was taken.\n */\n async decide(sessionId: string, action: TriageAction): Promise<void> {\n const address = this.address\n if (address === undefined) return\n // A decision reads as it runs, so the marker changes it causes are its own\n // echo; the state shown afterwards is the host's answers below.\n this.burst.absorb()\n await this.refreshTriage()\n await this.commands.execute(address, `/${TRIAGE_DECISION_COMMAND[action]} ${sessionId}`, [], this.lifetime)\n await this.refreshTriage()\n // A decision can release a session, which the whole-app verdict reports.\n await this.reread(address)\n }\n\n /**\n * Press the pill: park everything, or release everything the park owns.\n * Which one it is comes from the host's own last answer, never from a local\n * flip; the state shown afterwards is the host's answer again. A reading the\n * view does not turn into a verb — every registered session is one the host\n * will not wake — is not pressed at all.\n * @param sessionId - the session to address the verb to.\n * @returns after the control re-reads the host.\n */\n async toggle(sessionId: SessionId): Promise<void> {\n const current = this.store.getSnapshot().global\n // Nothing to press against: a state that could not be read is not a state\n // this control may act on.\n if (current === undefined || current.kind !== 'reported') return\n const action = globalPauseView(current).action\n if (action === 'none') return\n const line = action === 'pause-all' ? `/${PAUSE_ALL_COMMAND}` : `/${RESUME_ALL_COMMAND}`\n // The press is a client action: the host announces the markers it registers\n // or unregisters as it runs, and the read below is the answer to all of it.\n this.burst.absorb()\n const executed = await this.commands.execute(sessionId, line, [], this.lifetime)\n if (!executed.ok) {\n this.publish({ kind: 'unavailable' })\n this.follow()\n return\n }\n await this.reread(sessionId)\n }\n\n /** Ask the host for the decision list, in the browser's vocabulary. */\n private async readTriageRows(sessionId: SessionId): Promise<TriageReading> {\n const answered = await this.commands.execute(sessionId, `/${TRIAGE_COMMAND}`, [], this.lifetime)\n if (!answered.ok || answered.value === undefined) return { kind: 'unavailable' }\n const text = answered.value.result.kind === 'success' ? answered.value.result.text : undefined\n const list = text === undefined ? undefined : decodeTriageList(text)\n // An answer this control cannot read is not a state it may report.\n return list === undefined ? { kind: 'unavailable' } : { kind: 'reported', rows: list.rows }\n }\n\n /** Ask the host what the whole-app park state is, in the browser's vocabulary. */\n private async read(sessionId: SessionId): Promise<GlobalReading> {\n const answered = await this.commands.execute(sessionId, `/${STATE_ALL_COMMAND}`, [], this.lifetime)\n if (!answered.ok || answered.value === undefined) return { kind: 'unavailable' }\n const text = answered.value.result.kind === 'success' ? answered.value.result.text : undefined\n const status = text === undefined ? undefined : decodeGlobalStatus(text)\n // An answer this control cannot read is not a state it may report.\n if (status === undefined) return { kind: 'unavailable' }\n return { kind: 'reported', status }\n }\n\n private publish(reading: GlobalReading): void {\n this.store.update((state) => { state.global = reading })\n }\n\n /**\n * Decide what the burst owes after a read: another look while a change this\n * browser has not answered is outstanding, or while the open panel is still\n * owed rows, and nothing once neither holds.\n *\n * A registered session standing at the park point is left alone: the flush\n * that settles it announces itself on the command channel like every other\n * transition, so watching it from here would spend reads on a move that is\n * going to speak for itself. A verdict that stays `waiting` because a record\n * is stalled, unresumable or unread is not settling either: nothing in this\n * process will change it, and a session that starts moving again registers its\n * markers, which is what asks next.\n *\n * The one transition that cannot speak for itself is the one this browser's\n * own echo window swallowed: a press reads the host, the park reaches its\n * final phase while the window is still open, and the changes that would have\n * announced it look exactly like the press's own registrations. An action that\n * left the park moving therefore asks for one look when that window closes,\n * and only then. Once nothing is owed, the last reading stays published — the\n * verdict, the pill and the rows are exactly what the host last reported.\n */\n private follow(): void {\n if (this.lifetime.aborted) {\n this.burst.stop()\n return\n }\n if (!this.owed && !this.unansweredPanel()) {\n if (this.burst.absorbing && this.moving()) this.burst.recheckAfterEcho()\n this.burst.stop()\n return\n }\n this.burst.next()\n }\n\n /**\n * Whether the park this control last read still has work on its way — the only\n * state an action's own echo could be hiding a transition behind. A record\n * that is stalled, unresumable or unread is not moving: nothing in this\n * process will finish it.\n */\n private moving(): boolean {\n const current = this.store.getSnapshot().global\n return current?.kind === 'reported'\n && (current.status.draining.length > 0 || current.status.parking > 0)\n }\n\n /**\n * Whether the open panel is still owed its rows. Opening the panel reads them,\n * so this holds only after a read that did not answer — the one debt no change\n * created, and the one place a retry is the difference between an empty panel\n * and the facts the host has. It is bounded by the burst like everything else.\n */\n private unansweredPanel(): boolean {\n const state = this.store.getSnapshot()\n return state.panelOpen && (state.triage === undefined || state.triage.kind === 'unavailable')\n }\n\n /**\n * Follow the page in and out of view. A hidden page is not being read — and\n * the read the burst was about to take is not lost, it waits for the page to\n * come back.\n */\n watchVisibility(): void {\n this.burst.checkVisibility()\n }\n}\n","/**\n * The glyphs both pause controls are drawn with, so the session-header\n * control and the whole-app pill cannot drift apart.\n * @module dsh-pause-session/client/icons\n */\n\nimport type { ReactNode } from 'react'\n\n/** 16px stroke icon (currentColor): the two bars of a pause. */\nexport function PauseIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width={16}\n height={16}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M9 5v14\" />\n <path d=\"M15 5v14\" />\n </svg>\n )\n}\n\n/** 16px stroke icon (currentColor): the triangle of a resume. */\nexport function ResumeIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width={16}\n height={16}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M7 4.5v15l12-7.5z\" />\n </svg>\n )\n}\n\n/**\n * 16px stroke icon (currentColor): the rings of a target, for the goal switch\n * on the session header. Drawn at 16px like the pause and resume glyphs it sits\n * beside rather than the 14px marks the two reports use.\n */\nexport function GoalIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width={16}\n height={16}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"8\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\n/**\n * 14px stroke icon (currentColor): the mark that a report carries a caveat.\n * A glyph rather than copy, so the facts it stands for stay in the locale\n * dictionaries beside everything else the user reads.\n */\nexport function CaveatIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width={14}\n height={14}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 7.5v5.5\" />\n <path d=\"M12 16.5v.5\" />\n </svg>\n )\n}\n\n/** 14px stroke icon (currentColor): the chevron that opens the decision list. */\nexport function ChevronIcon(): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n width={14}\n height={14}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M6 15l6-6 6 6\" />\n </svg>\n )\n}\n","/**\n * The whole-app control: one pill in the conversation header's utilities row\n * that answers \"can I close the lid?\", presses the park or the release, and —\n * when something did not stop cleanly — opens the morning decision list.\n *\n * The header's right-aligned utilities row is inside the conversation column,\n * which is where this control belongs: it answers for every session tree, but\n * it is read while looking at one, and a surface inside the layout is covered\n * by whatever covers that column — a sidebar or a drawer — instead of floating\n * above the whole frame. The decision list hangs from the control into the\n * conversation area below.\n *\n * The pill reads the host through the command channel only (`./global.ts`):\n * with no answer, or with no session to ask through, it renders nothing rather\n * than a state it would have to invent. The decision list is deliberately NOT\n * part of that first read — it costs two more command calls, and a user who\n * never opens it pays nothing; the panel asks for its own rows when it opens.\n * @module dsh-pause-session/client/pill\n */\n\nimport { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: pulls the ui-conversation SlotMap merge (header.utilities).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the ui-session GlobalStandardProps merge (useSessions).\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport type { TriageAction } from '../shared.js'\nimport { addressingSession } from './global.js'\nimport type { GlobalPauseState } from './global.js'\nimport { globalPauseView, showsPauseControl, triageView, TRIAGE_ACTION_VIEWS, TRIAGE_GOAL_VIEW } from './view.js'\nimport type { TriageRowView } from './view.js'\nimport { CaveatIcon, ChevronIcon, PauseIcon, ResumeIcon } from './icons.js'\nimport styles from './button.module.css'\n\n/** Browser state and operations injected into the whole-app pill. */\nexport interface PausePillInjected {\n hooks: { pauseGlobal: ObservableSnapshot<GlobalPauseState> }\n /** Hand the control the session the shell is on; it reads there until it has an answer. */\n refresh: (sessionId: SessionId) => void\n /** Press the pill for one session. */\n toggle: (sessionId: SessionId) => void\n /** Show or hide the decision list; opening it also reads its rows. */\n setPanel: (open: boolean) => void\n /** Take one of the four decisions on one listed session. */\n decide: (sessionId: string, action: TriageAction) => void\n}\n\n/** Full props of the whole-app pause control. */\nexport type PausePillProps = PropsRuntime<'conversation.session.header.utilities'>\n & InjectFace<PausePillInjected>\n & PropsLocale<'pause'>\n\n/** One listed session's row, with the four decisions. */\nfunction TriageSessionRow(props: {\n readonly row: TriageRowView\n readonly decide: (sessionId: string, action: TriageAction) => void\n readonly t: PausePillProps['t']\n}): ReactNode {\n const { row, decide, t } = props\n return (\n <li className={styles.dspPillRow}>\n <div className={styles.dspPillRowHead}>\n <span className={styles.dspPillRowId}>{row.sessionId}</span>\n <span className={styles.dspPillRowPhase}>{t(row.phaseKey)}</span>\n </div>\n <div className={styles.dspPillRowFacts}>\n {row.readable\n ? (\n <>\n <span>{t('triage.stopped')}: {row.stopped ?? '-'}</span>\n <span>\n {row.unknownTools.length === 0\n ? t('triage.toolsNone')\n : `${t('triage.tools')}: ${row.unknownTools.join(', ')}`}\n </span>\n </>\n )\n : <span>{t('triage.stoppedUnknown')}</span>}\n {row.readable && !row.wakeable ? <span>{t('triage.noWake')}</span> : null}\n </div>\n <div className={styles.dspPillRowActions}>\n {TRIAGE_ACTION_VIEWS.map(action => (\n <button\n key={action.action}\n type=\"button\"\n className={styles.dspPillAction}\n aria-label={t(action.ariaKey)}\n onClick={() => { decide(row.sessionId, action.action) }}\n >\n {t(action.labelKey)}\n </button>\n ))}\n {/* A switch, not a decision: it says what the next resume of this\n session may spend, and the host's own reading is its state. */}\n <button\n type=\"button\"\n className={styles.dspPillAction}\n aria-label={t(TRIAGE_GOAL_VIEW.ariaKey)}\n aria-pressed={row.rearmGoal}\n onClick={() => { decide(row.sessionId, TRIAGE_GOAL_VIEW.action) }}\n >\n {t(TRIAGE_GOAL_VIEW.labelKey)}\n </button>\n </div>\n </li>\n )\n}\n\n/**\n * The whole-app control: what the host says about the whole park, the one\n * press that parks or releases all of it, and the decision list for whatever\n * did not stop cleanly.\n * @param props - session-header runtime, host operations, and localized copy.\n * @returns the control and its panel, or nothing while the host has not answered.\n */\nexport function PausePill(props: PausePillProps): ReactNode {\n const { refresh, toggle, setPanel, decide, t, useSessions, useSession } = props\n const reading = props.usePauseGlobal(state => state.global)\n const triage = props.usePauseGlobal(state => state.triage)\n const open = props.usePauseGlobal(state => state.panelOpen)\n // The command channel is session-addressed: one user session is the address\n // every whole-app press goes through, and the one the control's very first\n // read goes through.\n const address = useSessions(addressingSession)\n const view = globalPauseView(reading)\n const list = triageView(triage)\n // A subagent's own header gets nothing, exactly like the per-session control:\n // its work is its parent's, and the parent's header already speaks for the\n // whole tree. The mount read below waits for the same gate, so a header that\n // renders nothing spends no host round-trip either.\n const visible = showsPauseControl(useSession(snapshot => snapshot.subagent))\n // Hand the control the session the shell is on. It reads there once, when it\n // has no answer yet; afterwards the address follows the view while the reads\n // follow the park (see `./global.ts`).\n useEffect(() => {\n if (visible && address !== undefined) refresh(address)\n }, [refresh, address, visible])\n if (!visible || !view.visible || address === undefined) return null\n const tip = view.caveats\n .map(caveat => caveat.names.length === 0\n ? t(caveat.key)\n : `${t(caveat.key)} (${caveat.names.join(', ')})`)\n .join('\\n')\n return (\n <span className={styles.dspPill}>\n {!open\n ? null\n : (\n <div className={styles.dspPillPanel} role=\"dialog\" aria-label={t('triage.title')}>\n <div className={styles.dspPillPanelHead}>\n <span className={styles.dspPillText}>{t('triage.title')}</span>\n <button\n type=\"button\"\n className={styles.dspPillAction}\n aria-label={t('triage.refresh.aria')}\n onClick={() => { setPanel(true) }}\n >\n {t('triage.refresh.aria')}\n </button>\n </div>\n {!list.visible\n ? <p className={styles.dspPillPanelNote}>{t('triage.reading')}</p>\n : list.rows.length === 0\n ? <p className={styles.dspPillPanelNote}>{t('triage.empty')}</p>\n : (\n <ul className={styles.dspPillRows}>\n {list.rows.map(row => (\n <TriageSessionRow key={row.sessionId} row={row} decide={decide} t={t} />\n ))}\n </ul>\n )}\n </div>\n )}\n {view.unclean === 0\n ? null\n : (\n <button\n type=\"button\"\n className={styles.dspPillTriage}\n aria-label={t('triage.open.aria')}\n aria-expanded={open}\n title={`${String(view.unclean)} ${t('all.triage')}`}\n onClick={() => { setPanel(!open) }}\n >\n <ChevronIcon />\n <span className={styles.dspPillDetail}>{view.unclean}</span>\n </button>\n )}\n <button\n type=\"button\"\n className={view.labelKey === undefined && view.detail.length === 0\n ? `${styles.dspPillBtn} ${styles.dspPillIcon}`\n : styles.dspPillBtn}\n aria-label={t(view.ariaKey)}\n aria-pressed={view.action === 'resume-all'}\n disabled={!view.enabled}\n {...tip.length === 0 ? {} : { title: tip }}\n onClick={() => { toggle(address) }}\n >\n {view.action === 'resume-all' ? <ResumeIcon /> : <PauseIcon />}\n {view.labelKey === undefined ? null : <span className={styles.dspPillText}>{t(view.labelKey)}</span>}\n {view.detail.length === 0 ? null : <span className={styles.dspPillDetail}>{view.detail}</span>}\n {view.caveats.length === 0 ? null : <span className={styles.dspPillWarn}><CaveatIcon /></span>}\n </button>\n </span>\n )\n}\n","/** `pause` namespace dictionaries. */\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'pause.aria': '暂停这个会话及其子代理:下一条模型请求不会发出',\n 'pause.tip': '暂停这个会话及其子代理',\n 'resume.aria': '恢复这个会话及其子代理',\n 'resume.tip': '恢复这个会话及其子代理',\n 'draining.aria': '正在暂停:还有工具在运行,工具运行完之后、下一条模型请求发出之前,会话会停住;按下可取消这次暂停',\n 'parking.aria': '正在暂停:正在保存待发输入;按下可取消这次暂停',\n 'safe.aria': '已暂停:待发输入已保存;按下可恢复',\n 'flushFailed.aria': '已暂停,但保存待发输入失败;按下可恢复',\n 'state.paused': '已暂停',\n 'state.draining': '正在暂停',\n 'state.parking': '正在保存',\n 'state.safe': '已暂停',\n 'state.flushFailed': '保存失败',\n 'goal.aria': '恢复时重开目标:恢复这个会话时是否重新打开目标并让它继续运行;按下切换',\n 'goal.on.tip': '恢复这个会话会重新打开目标,它会自己继续运行;按下改为不重开',\n 'goal.off.tip': '恢复这个会话不会重新打开目标;按下改为重开',\n 'checking.aria': '正在读取暂停状态',\n 'checking.tip': '正在读取暂停状态',\n 'unavailable.aria': '暂停状态不可用',\n 'unavailable.tip': '暂停状态不可用',\n 'all.pause.aria': '暂停所有运行中的会话及其子代理',\n 'all.resume.aria': '恢复所有已暂停的会话',\n 'all.idle': '暂停所有会话',\n 'all.finishing': '正在暂停',\n 'all.unclean': '有会话未暂停',\n 'all.unclean.aria': '有会话未暂停,不能在这里恢复;打开清单逐行处理',\n 'all.safe': '全部已暂停',\n 'all.checking.aria': '正在读取暂停状态',\n 'all.caveat.jobs': '后台任务仍在运行',\n 'all.caveat.unresumable': '含无法恢复的子代理',\n 'all.caveat.stalled': '未暂停,需要你决定',\n 'all.caveat.unread': '子代理列表读不出来',\n 'all.caveat.blindTrees': '子代理服务未加载,无法检查子代理',\n 'all.caveat.blindJobs': '作业服务未加载,无法检查后台任务',\n 'triage.open.aria': '查看未暂停的会话清单',\n 'triage.title': '未暂停的会话',\n 'triage.empty': '没有需要决定的会话',\n 'triage.reading': '正在读取清单',\n 'triage.unavailable': '清单暂不可读',\n 'triage.refresh.aria': '重新读取清单',\n 'triage.session.aria': '会话',\n 'triage.stopped': '停在',\n 'triage.stoppedUnknown': '读不到它的日志,停止位置未知',\n 'triage.tools': '未知的工具结果',\n 'triage.toolsNone': '没有缺少结果的工具调用',\n 'triage.noWake': '这个会话没有待发的模型请求,继续不会发出请求',\n 'triage.phase.draining': '工具还在运行时中断',\n 'triage.phase.parking': '已暂停,但待发输入未保存',\n 'triage.phase.flushFailed': '已暂停,但保存失败',\n 'triage.continue': '继续',\n 'triage.continue.aria': '继续:不添加消息,直接恢复',\n 'triage.note': '继续并附说明',\n 'triage.note.aria': '继续并附说明:告诉模型工具结果未知,不要盲目重试',\n 'triage.note.hint': '会加入这句话:',\n 'triage.review': '只看不运行',\n 'triage.review.aria': '只看不运行:不启动会话、不发请求;需要查看时在会话历史里打开',\n 'triage.leave': '保持暂停',\n 'triage.leave.aria': '保持暂停:不恢复,也不写入任何内容',\n 'triage.goal': '恢复时重开目标',\n 'triage.goal.aria': '恢复时重开目标:恢复这个会话时是否重新打开目标并让它自己继续运行;按下切换',\n 'all.triage': '有待决定的会话',\n} satisfies Record<string, string>\n\n/** The pause namespace key union. */\nexport type PauseKey = keyof typeof zh\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: Record<PauseKey, string> = {\n 'pause.aria': 'Pause this session and its subagents: their next model request will not be sent',\n 'pause.tip': 'Pause this session and its subagents',\n 'resume.aria': 'Resume this session and its subagents',\n 'resume.tip': 'Resume this session and its subagents',\n 'draining.aria': 'Pausing: a tool is still running. The session pauses after the tool finishes and before its next model request; press to cancel the pause',\n 'parking.aria': 'Pausing: saving pending input; press to cancel the pause',\n 'safe.aria': 'Paused: pending input is saved; press to resume',\n 'flushFailed.aria': 'Paused, but saving pending input failed; press to resume',\n 'state.paused': 'Paused',\n 'state.draining': 'Pausing',\n 'state.parking': 'Saving',\n 'state.safe': 'Paused',\n 'state.flushFailed': 'Save failed',\n 'goal.aria': 'Re-open goal on resume: whether resuming this session re-opens its goal and lets it run on its own; press to switch',\n 'goal.on.tip': 'Resuming re-opens the goal, which runs on its own; press to keep it closed',\n 'goal.off.tip': 'Resuming will not re-open the goal; press to re-open it',\n 'checking.aria': 'Reading pause state',\n 'checking.tip': 'Reading pause state',\n 'unavailable.aria': 'Pause state unavailable',\n 'unavailable.tip': 'Pause state unavailable',\n 'all.pause.aria': 'Pause all running sessions and their subagents',\n 'all.resume.aria': 'Resume all paused sessions',\n 'all.idle': 'Pause all sessions',\n 'all.finishing': 'Pausing',\n 'all.unclean': 'Some sessions are not paused',\n 'all.unclean.aria': 'Some sessions are not paused and cannot be resumed here; open the list to decide',\n 'all.safe': 'All Paused',\n 'all.checking.aria': 'Reading pause state',\n 'all.caveat.jobs': 'background jobs are still running',\n 'all.caveat.unresumable': 'contains a subagent that cannot be resumed',\n 'all.caveat.stalled': 'did not pause; needs your decision',\n 'all.caveat.unread': 'subagent list could not be read',\n 'all.caveat.blindTrees': 'subagent service is not loaded, so subagents cannot be checked',\n 'all.caveat.blindJobs': 'job service is not loaded, so background jobs cannot be checked',\n 'triage.open.aria': 'Review sessions that did not pause',\n 'triage.title': 'Sessions that did not pause',\n 'triage.empty': 'No sessions need a decision',\n 'triage.reading': 'Reading list',\n 'triage.unavailable': 'List unavailable',\n 'triage.refresh.aria': 'Read the list again',\n 'triage.session.aria': 'Session',\n 'triage.stopped': 'Stopped at',\n 'triage.stoppedUnknown': 'Its log cannot be read, so where it stopped is unknown',\n 'triage.tools': 'Unknown tool results',\n 'triage.toolsNone': 'No tool call is missing a result',\n 'triage.noWake': 'This session has no pending model request; continuing sends nothing',\n 'triage.phase.draining': 'stopped while a tool was still running',\n 'triage.phase.parking': 'paused but pending input was not saved',\n 'triage.phase.flushFailed': 'paused but saving failed',\n 'triage.continue': 'Continue',\n 'triage.continue.aria': 'Continue: resume without adding a message',\n 'triage.note': 'Continue with a note',\n 'triage.note.aria': 'Continue with a note: tell the model the tool result is unknown and not to retry blindly',\n 'triage.note.hint': 'This text is added:',\n 'triage.review': 'View only',\n 'triage.review.aria': 'View only: do not start the session or send a request; open it from your session history to read it',\n 'triage.leave': 'Leave it paused',\n 'triage.leave.aria': 'Leave it paused: keep the pause and write nothing',\n 'triage.goal': 'Re-open goal on resume',\n 'triage.goal.aria': 'Re-open goal on resume: whether resuming this session re-opens its goal, which then runs on its own; press to switch',\n 'all.triage': 'sessions need a decision',\n}\n","/**\n * Browser half of dsh-pause-session.\n *\n * Registers two controls: one in the session header's title-adjacent action\n * group (`conversation.session.header.actions`), which pauses or resumes the\n * session it sits on, and one in the header's right-aligned utilities row\n * (`conversation.session.header.utilities`), which answers for the whole app.\n * Both seats are session-scoped and their props carry `sessionId`, so every\n * open session header renders its own pair and switching sessions follows\n * automatically; the whole-app control speaks for every session from the\n * header of the one on screen.\n *\n * The whole-app control sits inside the conversation column rather than in the\n * frame-wide overlay layer: it is read while looking at one conversation, and\n * a sidebar or drawer that covers that area covers the control with it.\n *\n * Both controls are host-authoritative: they read state from the host's command\n * registry and drive it through the host's session commands (`./control.ts` and\n * `./global.ts` document why the command registry is the only live channel a\n * third-party plugin can use). Each stays inert wherever that round-trip is\n * unavailable rather than rendering a control that cannot tell the truth. The\n * header control also reads the whole-app answer the whole-app control\n * publishes, so a park that holds nothing does not cost a host round-trip per\n * header.\n * @module dsh-pause-session/client\n */\n\nimport { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: pulls the api-remotes Client face and its forwarded-event key set.\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\n// Type-only: pulls the ui-conversation SlotMap merge (header.actions, header.utilities).\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the renderer-owned slots service (ctx.slots).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport { PauseControl } from './control.js'\nimport type { PauseState } from './control.js'\nimport { PauseGlobalControl } from './global.js'\nimport { GoalIcon, PauseIcon, ResumeIcon } from './icons.js'\nimport { PausePill } from './pill.js'\nimport type { PausePillInjected } from './pill.js'\nimport { pauseView, showsPauseControl } from './view.js'\nimport { en, zh, type PauseKey } from './locales.js'\nimport styles from './button.module.css'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The pause control's copy. */\n 'pause': PauseKey\n }\n}\n\n/** Dictionary namespace owned by this plugin. */\nconst NS = 'pause'\n\n/** Required services: the slot registry, the locale registry, and the host round-trip. */\nexport const inject = ['slots', 'locale', 'remote', 'remote.commands']\n\n/** Browser state and operations injected into the Session-header contribution. */\nexport interface PauseInjected {\n hooks: { pause: ObservableSnapshot<PauseState> }\n /** Read the host's state for one session (the control calls this once per mount). */\n refresh: (sessionId: SessionId) => void\n /** Press the control for one session. */\n toggle: (sessionId: SessionId) => void\n /** Flip one session's own goal opt-out. */\n toggleGoal: (sessionId: SessionId) => void\n}\n\n/** Full props of the Session-header pause control. */\nexport type PauseButtonProps = PropsRuntime<'conversation.session.header.actions'>\n & InjectFace<PauseInjected>\n & PropsLocale<'pause'>\n\n/**\n * The session's pause control: pause the session and its subagents before\n * their next model request, or resume them, with the host's own state on screen.\n * Once the host reports the park's goal opt-out, a second switch sits beside it\n * — the only place a session that stopped CLEANLY can be told not to re-open its\n * goal on the next resume, because the decision list shows only the sessions\n * that did not stop cleanly.\n *\n * A subagent session renders nothing: its work is its parent's, the parent's\n * control already covers it, and a button here would offer to pause \"a session\"\n * from inside a child.\n * @param props - session runtime, host operations, and localized copy.\n * @returns the header action, or nothing on a subagent session.\n */\nexport function PauseButton(props: PauseButtonProps): ReactNode {\n const { sessionId, refresh, toggle, toggleGoal, t, useSession } = props\n const reading = props.usePause(state => state.bySession[String(sessionId)])\n const view = pauseView(reading)\n const visible = showsPauseControl(useSession(snapshot => snapshot.subagent))\n // Read once per mounted control: the host is the source of truth, and the\n // forwarded registry change keeps it current afterwards.\n useEffect(() => { refresh(sessionId) }, [refresh, sessionId])\n if (!visible) return null\n return (\n <span className={styles.dsp}>\n <button\n type=\"button\"\n className={styles.dspBtn}\n aria-label={t(view.ariaKey)}\n aria-pressed={view.paused}\n disabled={!view.enabled}\n onClick={() => { toggle(sessionId) }}\n >\n {view.paused ? <ResumeIcon /> : <PauseIcon />}\n </button>\n {/* No host answer, no switch: the state has to come from the record, and\n a switch sitting in the default state would claim its own answer. */}\n {view.rearmGoal === undefined\n ? null\n : (\n <button\n type=\"button\"\n className={styles.dspGoal}\n aria-label={t('goal.aria')}\n aria-pressed={view.rearmGoal}\n title={t(view.rearmGoal ? 'goal.on.tip' : 'goal.off.tip')}\n onClick={() => { toggleGoal(sessionId) }}\n >\n <GoalIcon />\n </button>\n )}\n {view.labelKey === undefined\n ? null\n : (\n <span className={styles.dspState}>\n {t(view.labelKey)}\n {view.runningTools.length === 0 ? null : `: ${view.runningTools.join(', ')}`}\n </span>\n )}\n </span>\n )\n}\n\n/**\n * Client plugin body: register the locale dictionaries, own both controllers,\n * follow the host's registry changes, and contribute the per-session control\n * and the whole-app control into the session header.\n * @param ctx - client context carrying slots, locale, and the Remote face.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pause: dictionaries')\n const lifetime = new AbortController()\n ctx.effect(() => () => { lifetime.abort(new Error('pause: client unloaded')) }, 'pause: lifetime')\n const globalControl = new PauseGlobalControl(ctx.remote.commands, lifetime.signal)\n const control = new PauseControl(\n ctx.remote.commands,\n lifetime.signal,\n undefined,\n undefined,\n () => globalControl.store.getSnapshot().global,\n )\n // A header answered from the whole-app \"nothing is registered\" is read for\n // real the moment that stops holding: the session it stood in for may be the\n // one the park just took.\n ctx.effect(\n () => globalControl.store.subscribe(() => { control.reconsider() }),\n 'pause: the whole-app answer serves the session headers',\n )\n // The host publishes no push channel of its own, so the forwarded command\n // registry change is what makes these controls live: every one of them is a\n // transition worth looking at — a park's registrations, a resume's\n // unregistrations, and the re-registration that carries a phase change.\n //\n // The host announces one event per registration step, so parking N sessions\n // arrives as several events per session. Both controls coalesce that\n // themselves, over the burst's own collection window (`./burst.ts`): a change\n // that lands inside the window postpones the single read it buys rather than\n // starting one of its own. A change that follows one of the user's own\n // actions is that action's echo instead, and the burst absorbs it for a short\n // window rather than reading back an answer the action already put on screen.\n ctx.effect(\n () => ctx.remote.$on('commands/change', () => {\n control.refreshAll()\n globalControl.refreshAll()\n }),\n 'pause: host command registry changes',\n )\n // A page that is not on screen is not being read: the controls stop their\n // re-read timers while it is hidden and continue them when it comes back.\n ctx.effect(() => {\n if (typeof document === 'undefined') return () => {}\n const onVisibility = (): void => {\n control.watchVisibility()\n globalControl.watchVisibility()\n }\n document.addEventListener('visibilitychange', onVisibility)\n return () => { document.removeEventListener('visibilitychange', onVisibility) }\n }, 'pause: page visibility')\n ctx.slots.inject('conversation.session.header.actions', () =>\n ctx.slots.register(\n {\n name: 'conversation.session.header.actions',\n id: NS,\n order: 40,\n locale: NS,\n inject: (): PauseInjected => ({\n hooks: { pause: control.store },\n refresh: (sessionId) => { void control.refresh(sessionId) },\n toggle: (sessionId) => { void control.toggle(sessionId) },\n toggleGoal: (sessionId) => { void control.toggleGoal(sessionId) },\n }),\n },\n PauseButton,\n ),\n )\n // The whole-app control goes in the header's right-aligned utilities row:\n // the conversation column's own top-right corner, additive beside the\n // entries already there, and inside the layout rather than above it. Its\n // order puts it past the shipped utilities, which is the row's right end.\n ctx.slots.inject('conversation.session.header.utilities', () =>\n ctx.slots.register(\n {\n name: 'conversation.session.header.utilities',\n id: 'pause-all',\n order: 60,\n locale: NS,\n inject: (): PausePillInjected => ({\n hooks: { pauseGlobal: globalControl.store },\n refresh: (sessionId) => { void globalControl.refresh(sessionId) },\n toggle: (sessionId) => { void globalControl.toggle(sessionId) },\n setPanel: (open) => { globalControl.setPanel(open) },\n decide: (sessionId, action) => { void globalControl.decide(sessionId, action) },\n }),\n },\n PausePill,\n ),\n )\n}\n"],"mappings":"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"}