dsh-pause-session 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +51 -20
  2. package/DEVELOPMENT.md +25 -2
  3. package/README.md +41 -31
  4. package/README.zh.md +31 -21
  5. package/REHEARSAL.md +6 -6
  6. package/lib/client.js +1125 -1375
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +1107 -1081
  9. package/lib/index.js.map +1 -1
  10. package/lib/typert.host.d.ts +3 -0
  11. package/lib/typert.host.js +339 -0
  12. package/lib/typert.remote-client.d.ts +38 -0
  13. package/lib/typert.remote-client.d.ts.map +1 -0
  14. package/lib/typert.remote-client.js +244 -0
  15. package/lib/types/client/control.d.ts +25 -158
  16. package/lib/types/client/global.d.ts +39 -239
  17. package/lib/types/client/index.d.ts +6 -5
  18. package/lib/types/client/locales.d.ts +3 -4
  19. package/lib/types/client/pill.d.ts +5 -5
  20. package/lib/types/client/remote.d.ts +77 -0
  21. package/lib/types/client/view.d.ts +19 -5
  22. package/lib/types/coldlog.d.ts +1 -1
  23. package/lib/types/composition.d.ts +35 -61
  24. package/lib/types/control.d.ts +41 -60
  25. package/lib/types/global.d.ts +6 -4
  26. package/lib/types/index.d.ts +2 -2
  27. package/lib/types/park-state.d.ts +51 -0
  28. package/lib/types/registry.d.ts +1 -1
  29. package/lib/types/release-order.d.ts +25 -0
  30. package/lib/types/remote-adapter.d.ts +22 -0
  31. package/lib/types/remote.d.ts +158 -0
  32. package/lib/types/resume-activation.d.ts +36 -0
  33. package/lib/types/resume-text.d.ts +67 -0
  34. package/lib/types/resume.d.ts +7 -104
  35. package/lib/types/shared.d.ts +20 -123
  36. package/lib/types/snapshot.d.ts +29 -0
  37. package/lib/types/triage.d.ts +8 -9
  38. package/package.json +38 -19
  39. package/lib/types/client/burst.d.ts +0 -253
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/history.ts","../src/shared.ts","../src/coldlog.ts","../src/composition.ts","../src/tree.ts","../src/global.ts","../src/resume.ts","../src/triage.ts","../src/control.ts","../src/gate.ts","../src/park.ts","../src/registry.ts","../src/index.ts"],"sourcesContent":["/**\n * The message history a session's log derives to, and the one rule that reads\n * whether it still owes the model an answer.\n *\n * That question is asked twice and must have one answer: the clean wake asks it\n * before it hands a session an input (an input nobody owes an answer to would\n * open a turn of its own), and the decision list publishes it as the row's\n * `wake` field. A published promise that the wake then declines to keep is the\n * report lying, so both read the same derivation and the same rule.\n *\n * The derivation is the harness's own: the surface fold the loop reads its\n * history through, with the same message projections this process has\n * registered, and an interrupted tail closed exactly as the harness closes it\n * on the next read. `resume.ts` reads the live form of it\n * (`Session.deriveMessages()`); {@link derivedHistory} is the cold form over a\n * stored log, which is all the decision list may claim — it opens a log to read\n * it and never activates a session.\n * @module dsh-pause-session/history\n */\n\nimport type { Message } from '@deepseek-ai/dsh-llm'\nimport {\n deriveEventMessage, foldSurface, foldRequestHeader, interruptedTurnClosers,\n} from '@deepseek-ai/dsh-session'\nimport type { SessionEvent, SessionMessageProjection } from '@deepseek-ai/dsh-session'\n\n/**\n * Whether one derived history ends owing the model an answer: the last thing\n * said on the model-visible surface is not an assistant message.\n * @param messages - a derived history, in model-visible order.\n * @returns whether a model still owes that history a reply.\n */\nexport function owesReply(messages: readonly Message[]): boolean {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]\n // System and developer entries are request framing, not conversation.\n if (message === undefined || message.role === 'system' || message.role === 'developer') continue\n return message.role !== 'assistant'\n }\n return false\n}\n\n/**\n * The messages one stored log derives to, as the harness would read them on the\n * session's next activation: the open tail turn is closed with the harness's\n * own synthetic results first, then the log is folded into its surface.\n * @param events - the stored events, exactly as they are stored and in sequence order.\n * @param projections - this process's registered message projections.\n * @returns the derived history, in model-visible order.\n * @throws when the log cannot be folded — an event this build has no interpreter\n * for, or surface metadata that does not apply to this history.\n */\nexport function derivedHistory(\n events: readonly SessionEvent[],\n projections: readonly SessionMessageProjection[],\n): Message[] {\n const stored = [...events, ...interruptedTurnClosers(events)]\n const fold = foldSurface(stored, projections)\n return fold.nodes.flatMap((seq) => {\n const event = stored[seq]\n if (event === undefined) return []\n const message = deriveEventMessage(event, fold.projectedMessages)\n return message === null ? [] : [message]\n })\n}\n\n/**\n * Whether one stored log names the route a wake would be addressed to. The wake\n * needs a provider and a model to build a request at all, and the request\n * header the session itself persisted is the only place that names them.\n * @param events - the stored events, in sequence order.\n * @returns whether the log's last request header carries both route fields.\n */\nexport function routeNamed(events: readonly SessionEvent[]): boolean {\n const config = foldRequestHeader(events)?.config\n return config !== undefined && config.provider.length > 0 && config.model.length > 0\n}\n","/**\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 * Reading one parked session's stored log through a bound, and the facts the\n * morning list may claim from what it read.\n *\n * The decision list reads the same log the wake reads, but for the opposite\n * reason: the wake has to reproduce the session, so it reads everything. The\n * list only has to name where the session stopped, which tool call the log\n * never heard back from, and whether an answer is still owed — all of them\n * facts about the log's END. Reading from seq 0 to the end for every row, on\n * every command-registry change while the panel is open, would make a long\n * session's cost grow with a log nobody is asking about.\n *\n * WHAT THE BOUND BUYS. A stored log is append-only, so the tail is stable: the\n * read takes the last {@link TRIAGE_WINDOW_EVENTS} events, or the whole log when\n * it is shorter, and the answer is derived from that. The window is complete\n * whenever the log fits inside it, which is the common case; the fold the rest\n * of this plugin reads history through is then the harness's own, over exactly\n * the events the harness would fold.\n *\n * WHAT THE TAIL ALONE PROVES. A truncated window still answers the list's\n * questions, for one reason the harness guarantees: a dangling tool call exists\n * only inside the step that is still open, because a step's end is written\n * after its results. So an unanswered call is always inside the final open\n * span, and the tail that contains that span contains every one of them. What\n * the window cannot prove — a log a replacement event rewrote from further back\n * — is not guessed at: {@link logFacts} reports `undefined` and the row says\n * `unreadable`, because a list that invents a stop position is worse than a\n * list that says it could not read one.\n * @module dsh-pause-session/coldlog\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { deriveEventMessage, foldSurface } from '@deepseek-ai/dsh-session'\nimport type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` read seam and its handle.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport { derivedHistory, owesReply, routeNamed } from './history.js'\nimport { describeError } from './shared.js'\nimport type { TriageTrace } from './shared.js'\n\n/**\n * Events the read looks at before it gives up. Sized to hold a whole final\n * step — the model exchange, its tool calls, and their results — with room to\n * spare, at a cost that does not grow with the session.\n */\nexport const TRIAGE_WINDOW_EVENTS = 2000\n\n/**\n * The longest log the read will walk while locating where its tail starts.\n *\n * The window's start is found by probing for the log's length, so a log longer\n * than this cannot be located from its end at a cost worth paying for a panel\n * left open. Such a log — a million stored events — is reported unreadable\n * rather than read from the front, which is the read this module exists to\n * avoid.\n */\nexport const TRIAGE_LOG_MAX_EVENTS = 1_000_000\n\n/**\n * Events that are not the session's own story: the bookkeeping of the pause\n * command the user pressed, and the pending-input splices the inbox writes.\n * They are real and durable, but \"it stopped right after `/pause` finished\"\n * is not what \"where did it stop\" means.\n */\nconst BOOKKEEPING_EVENTS: ReadonlySet<string> = new Set(['command/run', 'command/done', 'agent/inbox/spliced'])\n\n/** How one stored log was seen by the bounded read. */\nexport type ColdLog =\n /** The events the read returned, and whether they are the whole stored log. */\n | { readonly kind: 'read'; readonly events: readonly SessionEvent[]; readonly complete: boolean }\n /** The log could not be opened or read, or it is longer than this read will walk. */\n | { readonly kind: 'unreadable' }\n\n/** The facts the decision list claims, all of them read off the log's end. */\nexport interface ColdFacts {\n /** What the stored log's last event was, and the turn/step it belongs to. */\n readonly last: SessionEvent | undefined\n /** Tool calls the stored events record and no stored result answers. */\n readonly traces: readonly TriageTrace[]\n /** Whether the stored log ends owing the model an answer. */\n readonly owed: boolean\n /** Whether the log names the route a wake would be addressed to. */\n readonly wakeable: boolean\n}\n\n/**\n * The turn and step an event belongs to, when it carries them.\n * @param event - one stored event.\n * @returns the position fields the event has, and nothing for the fields it does not.\n */\nexport function positionOf(event: SessionEvent): { readonly turn?: number; readonly step?: number } {\n const data = event.data\n if (typeof data !== 'object' || data === null) return {}\n const turn = 'turn' in data && typeof data.turn === 'number' ? data.turn : undefined\n const step = 'step' in data && typeof data.step === 'number' ? data.step : undefined\n return { ...turn === undefined ? {} : { turn }, ...step === undefined ? {} : { step } }\n}\n\n/**\n * Every tool call the STORED events record and no stored result answers.\n *\n * Deliberately the stored prefix rather than the repaired view: what a row\n * reports is the state of the session's own log, and the harness's synthetic\n * unknown-outcome results are exactly what that log is missing. Taking the\n * repaired view here would subtract the very traces the row exists to show.\n *\n * A call is answered only by a result the SAME window holds, which is what the\n * harness's own repair rule reads too: an unanswered call lives in the step\n * that is still open, so it is inside this window or it is answered.\n * @param events - the stored events the read returned, in sequence order.\n * @returns one trace per call with no result, in call order.\n */\nfunction unansweredCalls(events: readonly SessionEvent[]): TriageTrace[] {\n const names = new Map<string, string>()\n const answered = new Set<string>()\n for (const event of events) {\n if (event.type === 'tool/call') {\n names.set(String(event.data.callId), event.data.name)\n continue\n }\n if (event.type !== 'tool/result') continue\n answered.add(String(event.data.message.toolCallId))\n }\n return [...names].flatMap(([callId, tool]) => answered.has(callId) ? [] : [{ tool, callId }])\n}\n\n/**\n * Whether a log ends owing the model an answer, read from the tail alone.\n *\n * This is the harness's own rule — the last thing on the model-visible surface\n * is not an assistant message — read the way the harness reads it: the surface\n * fold over the events at hand, then the message the last node derives to. The\n * fold is handed a window rather than the whole log, so it refuses rather than\n * guesses when it meets a replacement it cannot resolve; a log it cannot fold\n * falls back to the last event that produces a message at all, which is the\n * same answer by a narrower route.\n * @param events - the stored events the read returned, in sequence order.\n * @param traces - the unanswered calls, as the row reports them.\n * @returns whether the session still owes the model a reply.\n */\nfunction tailOwesReply(events: readonly SessionEvent[], traces: readonly TriageTrace[]): boolean {\n // A log whose last step is open closes with a synthetic tool result for\n // every unanswered call, and a tool result is a message.\n if (traces.length > 0) return true\n const bySequence = new Map(events.map(event => [event.seq as number, event]))\n const lastNode = (): SessionEvent | undefined => {\n for (const seq of [...foldSurface(events).nodes].reverse()) {\n const event = bySequence.get(seq as number)\n if (event !== undefined) return event\n }\n return undefined\n }\n let anchor: SessionEvent | undefined\n try {\n anchor = lastNode()\n } catch {\n // The window cannot be folded as a log — an event it holds was rewritten\n // by something it does not. The narrower route still reads the same fact\n // off the end: the last event that derives a message.\n for (let at = events.length - 1; at >= 0; at -= 1) {\n const event = events[at]\n if (event === undefined || deriveEventMessage(event) === null) continue\n anchor = event\n break\n }\n }\n if (anchor === undefined) return false\n const message = deriveEventMessage(anchor)\n if (message === null) return false\n // System and developer entries are request framing, not conversation.\n if (message.role === 'system' || message.role === 'developer') return false\n return message.role !== 'assistant'\n}\n\n/**\n * The facts one bounded read of a stored log supports.\n *\n * A complete read is folded by the harness's own derivation, so the list and\n * the wake answer the \"does it still owe a reply\" question through one rule.\n * A truncated read answers from the tail, and only where the tail is proof: a\n * replacement event that rewrote a surface range from outside the window is\n * not visible here, so a log whose tail a later event could have rewritten is\n * reported unreadable rather than guessed at.\n * @param events - the stored events the read returned, in sequence order.\n * @param complete - whether those events are the whole stored log.\n * @param projections - this process's registered message projections.\n * @returns the row's facts, or `undefined` when this read cannot support them.\n */\nexport function logFacts(\n events: readonly SessionEvent[],\n complete: boolean,\n projections: Context['sessions']['messageProjections'],\n): ColdFacts | undefined {\n let last: SessionEvent | undefined\n for (let at = events.length - 1; at >= 0; at -= 1) {\n const event = events[at]\n if (event === undefined || BOOKKEEPING_EVENTS.has(event.type)) continue\n last = event\n break\n }\n const traces = unansweredCalls(events)\n const wakeableRoute = routeNamed(events)\n if (!complete) {\n // A truncated window has to hold the stop position, or it does not hold\n // the answer this row exists to publish: the log continued before what the\n // window saw, so \"no event to name\" would claim a session that left no\n // trace. That window is reported unreadable, not summarized.\n if (last === undefined) return undefined\n const owed = tailOwesReply(events, traces)\n return { last, traces, owed, wakeable: owed && wakeableRoute }\n }\n let owed: boolean\n try {\n owed = owesReply(derivedHistory(events, projections))\n } catch {\n return undefined\n }\n return { last, traces, owed, wakeable: owed && wakeableRoute }\n}\n\n/**\n * The one seam the length probe needs from an open handle: `read` at an offset\n * with a one-event length. It is the handle's own signature, narrowed here so a\n * reader can see that the probe never asks for a prefix it does not use.\n */\ninterface OffsetReader {\n /**\n * @param offset - first logical event seq to include.\n * @param length - maximum number of events to return.\n * @param options - cancellation for this read.\n * @returns a slice of the log, empty at or past its end.\n */\n read(\n offset?: number,\n length?: number,\n options?: { readonly signal?: AbortSignal },\n ): Promise<{ readonly events: readonly SessionEvent[] }>\n}\n\n/**\n * Whether one handle holds at least `count` events, asked without materializing\n * more than one of them.\n * @param handle - an open read handle onto the log.\n * @param count - the event count to test for.\n * @param signal - cancellation for the probe.\n * @returns whether the log reaches that far.\n */\nasync function reaches(handle: OffsetReader, count: number, signal?: AbortSignal): Promise<boolean> {\n return (await handle.read(count, 1, { ...signal === undefined ? {} : { signal } })).events.length > 0\n}\n\n/**\n * The stored log's own length, found by probing rather than by reading it.\n *\n * The handle's read seam is the only way to ask a backend anything, and it\n * takes an offset — so the count has to be probed for. The probe doubles until\n * it passes the end and then bisects, so a log of any length costs a handful\n * of one-event reads, and none of them materializes the prefix it skips.\n * @param handle - an open read handle onto the log.\n * @param signal - cancellation for the probe.\n * @returns the event count, or `undefined` when the log is longer than this read will walk.\n */\nasync function eventCount(handle: OffsetReader, signal?: AbortSignal): Promise<number | undefined> {\n let high = 1\n while (await reaches(handle, high, signal)) {\n if (high >= TRIAGE_LOG_MAX_EVENTS) return undefined\n high = Math.min(high * 2, TRIAGE_LOG_MAX_EVENTS)\n }\n let low = 0\n while (high - low > 1) {\n const mid = low + Math.floor((high - low) / 2)\n if (await reaches(handle, mid, signal)) low = mid\n else high = mid\n }\n return low\n}\n\n/**\n * Read one session's stored log, bounded to the events the list's facts need.\n *\n * The window is the last `windowEvents` events, found by probing for the log's\n * length and then reading exactly that slice. A short log is returned whole and\n * marked complete; a log longer than {@link TRIAGE_LOG_MAX_EVENTS} is reported\n * unreadable without being read at all.\n * @param ctx - host context carrying the optional persistence service.\n * @param sessionId - the stored session to read.\n * @param windowEvents - how many events of the log's end one row may fold.\n * @param signal - cancellation for the open, probe, and read.\n * @returns the bounded read, or `unreadable` when no read seam exists, it refused, or the log is too long to walk.\n * @throws the cancellation reason when `signal` aborts the read.\n */\nexport async function coldRead(\n ctx: Context,\n sessionId: SessionId,\n windowEvents: number = TRIAGE_WINDOW_EVENTS,\n signal?: AbortSignal,\n): Promise<ColdLog> {\n const persistence = ctx.get('sessionPersistence')\n if (persistence === undefined) return { kind: 'unreadable' }\n let handle\n try {\n handle = await persistence.open(sessionId, 'read', {\n ...signal === undefined ? {} : { signal },\n })\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not open the stored log of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'unreadable' }\n }\n try {\n const window = Math.max(1, Math.floor(windowEvents))\n const count = await eventCount(handle, signal)\n if (count === undefined) {\n ctx.logger.warn(`pause: the stored log of \"${sessionId}\" is longer than this read will walk`)\n return { kind: 'unreadable' }\n }\n const start = Math.max(0, count - window)\n const stored = await handle.read(start, window, {\n ...signal === undefined ? {} : { signal },\n })\n return { kind: 'read', events: stored.events, complete: start === 0 }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the stored log of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'unreadable' }\n } finally {\n await handle.close()\n }\n}\n","/**\n * Rebuild the scoped Agent composition a parked Session ran under.\n *\n * Activation is not only a persisted Session: the Agent preset supplies the\n * scoped tools, prompt sections, skills, and — for a subagent child — the\n * delegation context, persona, and tool restriction that were in force before\n * the park. A resume that loads the log without that setup starts a different\n * Agent on the same history.\n *\n * The preset a Session runs is the `agentPreset` projection, never the\n * creation-time header alone: a blank Session may select another preset before\n * its first turn, and the projection records the selection that actually ran.\n * A child with no independent selection joins its live parent's preset\n * generation; one that selected independently mounts the projected preset.\n * The durable child descriptor supplies the persona, tool filter, and route\n * that are reapplied only on cold resume.\n *\n * Both services are optional. A profile without `agentPresets` has no scoped\n * preset composition to restore, and this module leaves the resume unchanged.\n * A profile that mounts one but cannot read the Session projection cannot\n * prove what composition the Session ran, so it fails the resume instead of\n * silently starting a different Agent.\n * @module dsh-pause-session/composition\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Agent, AgentOptions, AgentSetup } from '@deepseek-ai/dsh-agent'\nimport type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: pulls the optional session projection read and preset roster faces.\nimport type {} from '@deepseek-ai/dsh-session-query'\nimport type {} from '@deepseek-ai/dsh-agent-preset-registry'\nimport { applyChildComposition, foldSubagentDescriptor } from '@deepseek-ai/dsh-subagent'\nimport type { ChildComposition } from '@deepseek-ai/dsh-subagent'\n\n/** The setup and route options a resumed Agent receives from its stored composition. */\nexport interface RestoredComposition {\n /** The durable child route, when the descriptor recorded one. */\n readonly agentOptions?: AgentOptions\n /** The scoped Agent world to mount before the Agent is published. */\n readonly setup?: AgentSetup\n}\n\n/**\n * A stored subagent route that no resume may reactivate.\n *\n * A one-shot child is terminal by contract; activating it as a root Agent\n * would turn a finished delegation into an ordinary conversation that happens\n * to share its Session id.\n */\nexport class UnresumableSessionError extends Error {\n constructor(sessionId: SessionId) {\n super(`subagent session \"${String(sessionId)}\" has a one-shot route and cannot be resumed`)\n this.name = 'UnresumableSessionError'\n }\n}\n\n/**\n * Whether the child's own log selected a preset before its descriptor.\n *\n * The creation-time selection precedes the first `subagent/descriptor`; a\n * selection after the descriptor belongs to the child's own later history and\n * does not decide which composition cold resume joins. This mirrors the\n * subagent runtime's own cold-resume rule.\n * @param events - the child's own log suffix, without the fork-inherited prefix.\n * @returns whether the child carries an independent preset selection.\n */\nexport function hasIndependentPresetSelection(events: readonly SessionEvent[]): boolean {\n for (const event of events) {\n if (event.type === 'subagent/descriptor') return false\n if (event.type === 'agent-preset/selected') return true\n }\n return false\n}\n\n/**\n * Whether a local Session may be parked under a promise of a later resume.\n *\n * A normal root Session has no child descriptor and is resumable. A local\n * subagent child is resumable only when its own durable descriptor is\n * `continuable`; a historical `one-shot` child is terminal and must never be\n * registered as a park the morning path would later have to refuse. External\n * executions have no local Session, so callers never reach this check.\n *\n * A profile without the optional Session-query service cannot prove the\n * descriptor's mode; it keeps the previous permissive behavior rather than\n * dropping every subagent. The web profile mounts the query service.\n * @param ctx - host context carrying the optional Session-query service.\n * @param sessionId - the local Session to inspect.\n * @param signal - cancellation for the observation read.\n * @returns whether a park of this Session may be resumed.\n * @throws the cancellation reason when `signal` aborts the read.\n */\nexport async function isResumableSession(\n ctx: Context,\n sessionId: SessionId,\n signal?: AbortSignal,\n): Promise<boolean> {\n const query = ctx.get('sessionQuery')\n if (query === undefined) return true\n const observation = await query.observeSession(sessionId, {\n ...signal === undefined ? {} : { signal },\n })\n try {\n const ownEvents = observation.events.slice(observation.inheritedEventCount)\n return foldSubagentDescriptor(ownEvents)?.mode !== 'one-shot'\n } finally {\n observation[Symbol.dispose]()\n }\n}\n\n/**\n * Read one parked Session's stored composition and build the resume options.\n *\n * The returned `setup` belongs to the unpublished Agent scope, so all\n * registrations exist before `agent/created`. For a root Session the setup\n * mounts the Session's projected preset, or the profile's default when the\n * Session never selected one. For a child under a live parent it applies the\n * child composition: an independent preset mounts separately, while an\n * inherited composition joins the parent's current generation; the historical\n * descriptor's persona and tool filter are reapplied in both cases.\n * @param ctx - host context carrying the optional preset and Session-query services.\n * @param sessionId - the parked Session being resumed.\n * @param parent - the live direct parent, when a child is resumed under it.\n * @param signal - cancellation for the observation read.\n * @returns the Agent options and setup to pass to `agents.resume`.\n * @throws {UnresumableSessionError} when the stored child route is one-shot.\n * @throws when a mounted preset roster cannot read the Session projection, or\n * a recorded preset cannot be restored.\n */\nexport async function restoreComposition(\n ctx: Context,\n sessionId: SessionId,\n parent: Agent | undefined,\n signal?: AbortSignal,\n): Promise<RestoredComposition> {\n const presets = ctx.get('agentPresets')\n const query = ctx.get('sessionQuery')\n if (query === undefined) {\n if (presets !== undefined) {\n throw new Error('resuming a composed Agent requires the session-query service')\n }\n return {}\n }\n const observation = await query.observeSession(sessionId, {\n ...(signal === undefined ? {} : { signal }),\n })\n try {\n const projections = observation.projections\n const ownEvents = observation.events.slice(observation.inheritedEventCount)\n const descriptor = foldSubagentDescriptor(ownEvents)\n if (descriptor?.mode === 'one-shot') throw new UnresumableSessionError(sessionId)\n if (presets === undefined || projections === undefined) {\n if (presets !== undefined) {\n throw new Error('resuming a composed Agent requires projected Session state')\n }\n return {}\n }\n if (parent === undefined) {\n // A root names its own preset; a Session that never selected one follows\n // the roster's default, exactly as the official Session controller does.\n const resolved = await presets.resolve(projections.values.agentPreset ?? undefined)\n return {\n setup: async (agentCtx) => { await presets.mount(agentCtx, resolved.id) },\n }\n }\n const independentlySelected = hasIndependentPresetSelection(ownEvents)\n const presetId = independentlySelected ? projections.values.agentPreset : undefined\n if (independentlySelected && typeof presetId !== 'string') {\n throw new Error(`subagent \"${String(sessionId)}\" has no projected agent preset to restore`)\n }\n const composition: ChildComposition = {\n ...typeof presetId === 'string' ? { agentPreset: presetId } : {},\n ...descriptor?.mode === 'continuable' && descriptor.persona !== undefined\n ? { persona: descriptor.persona }\n : {},\n ...descriptor?.mode === 'continuable' && descriptor.toolFilter !== undefined\n ? { toolFilter: descriptor.toolFilter }\n : {},\n }\n const agentOptions: AgentOptions | undefined = descriptor?.mode === 'continuable'\n ? {\n ...descriptor.agentProvider === undefined ? {} : { provider: descriptor.agentProvider },\n ...descriptor.agentModel === undefined ? {} : { model: descriptor.agentModel },\n ...descriptor.agentReasoningEffort === undefined\n ? {}\n : { reasoningEffort: descriptor.agentReasoningEffort },\n }\n : undefined\n return {\n ...agentOptions === undefined ? {} : { agentOptions },\n setup: async (agentCtx) => { await applyChildComposition(agentCtx, parent, composition) },\n }\n } finally {\n observation[Symbol.dispose]()\n }\n}\n","/**\n * One session's subagent tree, read from the parent catalogs.\n *\n * \"This session's work\" includes the subagents it delegated to, nested ones\n * included, so the session-header press and the status the header shows both\n * need the same answer to \"which sessions hang under this one\".\n * `listDescendants` is the harness's own recursive walk of the parent-owned\n * catalogs, and it is the only source: the tree is read from the parents that\n * record it, never guessed from session ids.\n *\n * A walk is read, not remembered. The tree is a fact that grows — a delegation\n * in flight publishes its child after the parent's catalog was read — so a\n * press walks the tree at press time and a status read walks it at read time.\n * `@module dsh-pause-session/tree`\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.subagents` service and its descendant rows.\nimport type {} from '@deepseek-ai/dsh-subagent'\nimport { describeError } from './shared.js'\n\n/** One session a walk found under another, with the catalog's own mode for it. */\nexport interface TreeMember {\n readonly id: SessionId\n /**\n * The catalog's durable mode for this child. `external` is a child with no\n * local session to park, and `unsupported` is a mode this build cannot read;\n * the other two are the harness's own split between a route a restart can\n * resume (`continuable`) and one it cannot.\n */\n readonly mode: 'one-shot' | 'external' | 'continuable' | 'unsupported'\n}\n\n/** One branch a walk reached and could not read. */\nexport interface UnreadBranch {\n readonly id: SessionId\n /**\n * `corrupt` and `unavailable` mean the branch's own catalog was not read, so\n * the members under it are unknown. `unsupported` means the parent's catalog\n * records a mode this build cannot read; that branch was still traversed, and\n * what it holds is known — but no restart can resume through it.\n */\n readonly reason: 'corrupt' | 'unsupported' | 'unavailable'\n}\n\n/** What one session's tree walk found. */\nexport interface TreeWalk {\n /**\n * Catalog descendants in pre-order, direct children first and each child's\n * own children after it. A missing subagent service leaves this empty.\n */\n readonly members: readonly TreeMember[]\n /**\n * Branches the walk reached and could not fully read. A branch named here may\n * hold members nobody can see, so it is never read as \"no members\".\n */\n readonly unread: readonly UnreadBranch[]\n /**\n * Whether the walk itself failed, so even the branches are unknown. Named\n * separately from {@link unread}, which lists the branches of a walk that ran.\n */\n readonly unreadWhole: boolean\n}\n\n/**\n * Read the subagent tree under one session.\n *\n * A failure to read a branch is reported as unread rather than as an empty\n * tree: the difference is what keeps a press from claiming it registered a tree\n * it never saw, and keeps a status answer from calling itself safe behind an\n * unknown branch.\n * @param ctx - host context carrying the optional subagent service.\n * @param rootSessionId - the session whose descendants are requested.\n * @param signal - cancellation for the catalog walk.\n * @returns the descendants found and what of the walk could not be read.\n * @throws the cancellation reason when `signal` aborts the walk.\n */\nexport async function walkDescendants(\n ctx: Context,\n rootSessionId: SessionId,\n signal?: AbortSignal,\n): Promise<TreeWalk> {\n const subagents = ctx.get('subagents')\n if (subagents === undefined) return { members: [], unread: [], unreadWhole: false }\n try {\n const rows = await subagents.listDescendants(rootSessionId, signal)\n const members: TreeMember[] = []\n const unread: UnreadBranch[] = []\n for (const row of rows) {\n if (row.kind === 'child') {\n members.push({ id: row.id, mode: row.mode })\n continue\n }\n unread.push({ id: row.id, reason: row.reason })\n }\n return { members, unread, unreadWhole: false }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the subagent tree of \"${rootSessionId}\": ${describeError(error)}`)\n return { members: [], unread: [], unreadWhole: true }\n }\n}\n","/**\n * The whole-app park: one action that registers every running session tree,\n * and the aggregate answer to \"can I close the lid?\".\n *\n * The per-session control owns one session's park; this module owns the set.\n * It reads three real sources and never infers what they can say themselves:\n *\n * - `ctx.agents.list()` plus the park activity fold decide which sessions are\n * running. A session with no live agent is not running, and a session with\n * nothing unfinished — no tool in flight, no turn open, no delivery the loop\n * has not claimed — is left alone.\n * - The subagent runtime's descendant rows decide the tree and the one-shot\n * fact. A row's `mode` is the harness's own durable record of whether that\n * child can be resumed after a restart; a branch the walk could not read is\n * reported as unread rather than assumed clean.\n * - The job registry decides which background jobs belong to the registered\n * trees. Those run outside the agent loop: this plugin cannot stop them, so\n * it names them.\n *\n * The aggregate is derived in one place so the host's answer and the browser's\n * reading of it cannot disagree, and it is a promise about the durable\n * registry: a session is `safe` only when its recorded phase says so.\n * @module dsh-pause-session/global\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { CommandResult } from '@deepseek-ai/dsh-commands'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.jobs` service and its read-only projection.\nimport type {} from '@deepseek-ai/dsh-jobs'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport { encodeGlobalStatus } from './shared.js'\nimport type { GlobalBlindSpot, GlobalStatus, GlobalVerdict, ParkPhase } from './shared.js'\nimport { walkDescendants } from './tree.js'\nimport type { TreeWalk } from './tree.js'\n\n/**\n * How many times one press walks the trees again. A tree can grow while the\n * walk runs — a delegation already in flight publishes its child after the\n * parent's catalog was read — so the walk repeats until it stops finding a\n * running session this action has not pressed yet.\n */\nconst PRESS_ROUNDS = 4\n\n/** One session's live park state, as the per-session control sees it. */\nexport interface LivePark {\n readonly phase: ParkPhase\n /** Whether this process can still walk the session to a flushed park. */\n readonly moving: boolean\n}\n\n/** What one press of the per-session control did. */\nexport type PressResult = 'registered' | 'unrecordable' | 'not-live' | 'not-resumable' | 'already-paused'\n\n/** What the whole-app control needs from the per-session control. */\nexport interface GlobalPauseDeps {\n /**\n * @param sessionId - session to inspect.\n * @returns whether a press already owns this session.\n */\n isPaused(sessionId: SessionId): boolean\n /**\n * Press one session's pause and make it durable.\n * @param sessionId - session to register.\n * @param signal - cancellation for the resumability and registry reads.\n * @returns what the press did; only `registered` created a new park.\n */\n press(sessionId: SessionId, signal?: AbortSignal): Promise<PressResult>\n /**\n * Look at one session's mark now that it is paused.\n * @param sessionId - the session that was just pressed.\n */\n look(sessionId: SessionId): void\n /**\n * @returns every session a live mark owns, with the phase it reports now.\n */\n live(): ReadonlyMap<SessionId, LivePark>\n /**\n * @returns every registered session this process could not hold again — a\n * record whose session cannot be read here, so nothing in this process can\n * park it, release it, or act on it.\n */\n unheld(): ReadonlySet<SessionId>\n}\n\n/** The whole-app park, as the plugin entry drives it. */\nexport interface GlobalPause {\n /**\n * Register and park every running session tree in one action. Sessions this\n * pause already owns are left where they are; an idle session is not touched.\n * @param signal - cancellation for the tree reads this action performs.\n * @returns the aggregate as of this action's end.\n */\n pressAll(signal?: AbortSignal): Promise<CommandResult>\n /**\n * @param signal - cancellation for the tree reads this read performs.\n * @returns the aggregate as of this read.\n */\n readStatus(signal?: AbortSignal): Promise<CommandResult>\n /**\n * Report one session a press could not register, so the aggregate names it.\n * The whole-app action records that fact for its own targets; a press of a\n * session's tree reports the members the registry refused through here.\n * @param sessionId - the session a press tried and failed to register.\n * @param unrecorded - whether it is now known to be unparked for that reason.\n */\n markUnrecorded(sessionId: SessionId, unrecorded: boolean): void\n}\n\n/** One tree walk as this aggregate reads it: the members, and the two ways a walk can be doubted. */\ninterface TreeReading {\n /** Catalog descendants of the walked session, direct children and deeper. */\n readonly members: readonly SessionId[]\n /** Whether any branch holds a route a restart cannot resume. */\n readonly unresumable: boolean\n /** Whether any branch could not be read, so the tree is not vouched for. */\n readonly unread: boolean\n}\n\n/** Fold one shared tree walk into the two doubts this report publishes. */\nfunction readingOf(walk: TreeWalk): TreeReading {\n return {\n members: walk.members.map(member => member.id),\n // A route no restart can resume, either because the catalog says so or\n // because it holds a mode this build cannot read.\n unresumable: walk.members.some(member => member.mode !== 'continuable')\n || walk.unread.some(branch => branch.reason === 'unsupported'),\n // `unsupported` was traversed, so its members are known; the other reasons\n // mean that branch was not read at all.\n unread: walk.unreadWhole || walk.unread.some(branch => branch.reason !== 'unsupported'),\n }\n}\n\n/**\n * Mount the whole-app park.\n * @param ctx - host context carrying the agent store and the optional subagent\n * and job services.\n * @param watch - the activity fold that says whether a session has unfinished work.\n * @param registry - the durable list this aggregate is a promise about.\n * @param deps - the per-session press and the live phase of every mark.\n * @returns the one-press action and the aggregate read.\n */\nexport function mountGlobalPause(\n ctx: Context,\n watch: ParkWatch,\n registry: ParkRegistry,\n deps: GlobalPauseDeps,\n): GlobalPause {\n /**\n * Sessions this action tried to press and could not record. They are NOT\n * parked and they keep running, so the aggregate counts them and names them\n * instead of quietly leaving them out of a park it then calls safe.\n */\n const unrecorded = new Set<SessionId>()\n /** Record or clear one session's \"pressed and the registry refused\" state. */\n const markUnrecorded = (sessionId: SessionId, flag: boolean): void => {\n if (flag) unrecorded.add(sessionId)\n else unrecorded.delete(sessionId)\n }\n /** Per-episode tree walks; an entry lives exactly as long as the park owns it. */\n const walks = new Map<SessionId, TreeReading>()\n\n /**\n * Whether a session has unfinished work right now. Three real answers, any\n * of which is enough: the loop says it is driving the session, the activity\n * fold sees a tool in flight or a turn open, or the inbox holds a delivery\n * the loop has not claimed yet.\n */\n const running = (agent: Agent): boolean => {\n if (agent.status === 'running') return true\n const activity = watch.activity(agent.id)\n if (activity.turnOpen || activity.runningTools.length > 0) return true\n return agent.inbox.nextTurn.length + agent.inbox.nextStep.length > 0\n }\n\n /**\n * Read one session's subagent tree through the shared walker and remember the\n * reading for this park episode. The tree is a fact about the episode, not\n * about the moment it was asked, and the aggregate reads several sessions per\n * answer; the cache keeps that read from repeating a catalog round-trip it\n * has already paid for.\n */\n const walkTree = async (sessionId: SessionId, signal?: AbortSignal): Promise<TreeReading> => {\n signal?.throwIfAborted()\n const cached = walks.get(sessionId)\n if (cached !== undefined) return cached\n // No subagent service at all: the walk would answer \"no members\", which is\n // not the same claim as \"no children\", and this report may not make it.\n const reading = ctx.get('subagents') === undefined\n ? { members: [], unresumable: false, unread: true }\n : readingOf(await walkDescendants(ctx, sessionId, signal))\n walks.set(sessionId, reading)\n return reading\n }\n\n /**\n * Every running session and every descendant of one, minus the sessions this\n * pause already owns. Descendants are walked whether or not they are live\n * right now: the tree is the unit the user asked to stop. A member this host\n * has no live session for has nothing to hold and nothing to flush, and the\n * press leaves it alone rather than record a park that could never finish\n * (see `./control.ts`).\n */\n const runningTargets = async (signal?: AbortSignal): Promise<readonly SessionId[]> => {\n const found = new Set<SessionId>()\n for (const agent of ctx.agents.list()) if (running(agent)) found.add(agent.id)\n // The roots are fixed before the walk: a descendant discovered here is a\n // member of a tree, not a second tree to walk from.\n for (const root of [...found]) {\n for (const member of (await walkTree(root, signal)).members) found.add(member)\n }\n return [...found].filter(sessionId => !deps.isPaused(sessionId))\n }\n\n /**\n * Live jobs of the registered trees, plus the unowned ones every caller sees.\n *\n * One registry walk per owner is the whole answer the jobs contract allows:\n * `list()` with no caller answers with unowned jobs only, and `list(owner)`\n * answers for exactly one owner, because the registry preflights ownership\n * instead of letting a reader enumerate another session's work.\n */\n const liveJobs = (owners: Iterable<SessionId>): readonly string[] => {\n const jobs = ctx.get('jobs')\n if (jobs === undefined) return []\n const seen = new Set<string>()\n const collect = (owner?: SessionId): void => {\n for (const view of owner === undefined ? jobs.list() : jobs.list(owner)) {\n if (view.status !== 'running' && view.status !== 'stopping') continue\n seen.add(String(view.id))\n }\n }\n collect()\n for (const owner of owners) collect(owner)\n return [...seen]\n }\n\n /**\n * The aggregate. Every registered session is accounted for: the ones on\n * their way, the ones that will not get there in this process, the trees\n * that hold work a restart cannot resume, and the jobs nothing here can stop.\n * The park also names the session it belongs to, so a client can address its\n * reads there rather than at whatever session it is showing.\n */\n const status = async (signal?: AbortSignal): Promise<GlobalStatus> => {\n const phases = new Map<SessionId, LivePark>()\n // The durable list is the promise; a live mark is the same episode's\n // fresher phase, and a press that could not be recorded is neither.\n for (const [sessionId, record] of registry.parked) phases.set(sessionId, { phase: record.phase, moving: false })\n // Read out of the accessor's own snapshot rather than through it per mark,\n // so this read asks the live state once however many sessions it holds.\n const live = deps.live()\n for (const [sessionId, park] of live) phases.set(sessionId, park)\n for (const sessionId of unrecorded) phases.set(sessionId, { phase: 'draining', moving: false })\n const unheld = deps.unheld()\n // Walks are per episode: a session the park no longer owns drops its entry,\n // so a later press reads the tree again instead of reporting a stale one.\n for (const sessionId of [...walks.keys()]) if (!phases.has(sessionId)) walks.delete(sessionId)\n\n let safe = 0\n let parking = 0\n const draining: string[] = []\n const stalled: string[] = []\n for (const [sessionId, park] of phases) {\n // A record this process could not hold is not a park here, whatever its\n // durable phase says: nothing is standing at the gate for it, so counting\n // it as safe would promise a hold that does not exist.\n if (unheld.has(sessionId)) {\n stalled.push(String(sessionId))\n continue\n }\n if (park.phase === 'safe') {\n safe += 1\n continue\n }\n const bucket = park.moving ? draining : stalled\n bucket.push(String(sessionId))\n // Standing at the park point with the flush still pending is counted\n // apart from the sessions still draining: both are unfinished, and the\n // pill's sentence is about how much of the wait is left rather than about\n // what is still running.\n if (park.moving && park.phase === 'parking') parking += 1\n }\n\n const unresumable: string[] = []\n const unreadTrees: string[] = []\n const owners = [...phases.keys()]\n // The session this park belongs to: the first registered session this\n // process holds, so a client can address its reads at the park rather than\n // at whatever session the user is looking at. The buckets above name a\n // session only while it has work left, so a park that reached `safe` — the\n // clean, ordinary case — would otherwise name no session at all. A record\n // this process cannot hold is named only when there is nothing else to\n // name: it is a true fact about the park, but it cannot answer a read.\n const parkedAt = owners.find(sessionId => !unheld.has(sessionId)) ?? owners[0]\n // One walk is one catalog read, and the walks do not depend on each other:\n // a read of N registered sessions must not take N catalog round-trips end\n // to end. `walkTree` answers from its per-episode cache when it has one.\n const trees = await Promise.all(owners.map(async sessionId => [sessionId, await walkTree(sessionId, signal)] as const))\n for (const [sessionId, tree] of trees) {\n if (tree.unresumable) unresumable.push(String(sessionId))\n if (tree.unread) unreadTrees.push(String(sessionId))\n }\n\n const blind: GlobalBlindSpot[] = []\n if (ctx.get('subagents') === undefined) blind.push('trees')\n if (ctx.get('jobs') === undefined) blind.push('jobs')\n\n const registered = phases.size\n // A tree nobody could read might hold a running descendant this park never\n // registered, so an unread tree is not a clean bill even when the session\n // itself is flushed.\n const verdict: GlobalVerdict = registered === 0\n ? 'idle'\n : safe === registered && unreadTrees.length === 0\n ? 'safe'\n : 'waiting'\n\n return {\n verdict,\n registered,\n safe,\n ...parkedAt === undefined ? {} : { parkedAt: String(parkedAt) },\n draining: draining.sort(),\n parking,\n stalled: stalled.sort(),\n unresumable: unresumable.sort(),\n unreadTrees: unreadTrees.sort(),\n jobs: liveJobs(owners),\n blind,\n }\n }\n\n return {\n pressAll: async (signal) => {\n for (let round = 0; round < PRESS_ROUNDS; round += 1) {\n const targets = await runningTargets(signal)\n if (targets.length === 0) break\n for (const sessionId of targets) {\n signal?.throwIfAborted()\n // The press is a durable promise per session: one that cannot be\n // recorded is rolled back there and named here. A session whose\n // route is not resumable, or has no live log, is left out of the\n // registry rather than promised a wake the morning path must refuse.\n const pressed = await deps.press(sessionId, signal)\n switch (pressed) {\n case 'registered':\n markUnrecorded(sessionId, false)\n deps.look(sessionId)\n break\n case 'unrecordable':\n markUnrecorded(sessionId, true)\n break\n default:\n break\n }\n }\n }\n const answer = encodeGlobalStatus(await status(signal))\n return unrecorded.size === 0\n ? { kind: 'success', text: answer }\n : { kind: 'error', text: answer }\n },\n readStatus: async (signal) => ({ kind: 'success', text: encodeGlobalStatus(await status(signal)) }),\n markUnrecorded,\n }\n}\n","/**\n * The clean resume path: bring back a session a previous process parked, and\n * let it answer the input that was already pending when it stopped.\n *\n * A resume invents no turn. At the park point the loop had claimed the\n * session's pending input onto the surface and built the request the gate then\n * held; after a restart that input is still the last thing the session says and\n * the model still owes it an answer. What is missing is only a reason for the\n * loop to build a request again, and the documented way to ask for one is a\n * wake input — a user message the loop claims.\n *\n * The wake is invisible only on the routes the composition declares in\n * `silentWakeRoutes`, whose conversion drops an empty user message. On every\n * other route the loop's request carries one visible marker instead, and the\n * marker reaches the model; that is an accepted cost of resuming at all.\n * Whether an empty user message survives is a property of the route's\n * conversion rather than of the loop, so the route is resolved per session —\n * from the request header the session itself persisted.\n *\n * The wake message is attributed to this plugin, never to the user: it is text\n * this module synthesized on the session's behalf, in the session's own log and\n * in front of the model, and a downstream consumer has to be able to tell it\n * apart from something the human typed.\n *\n * Only a cleanly parked session takes this path. A record that never reached\n * the park point, or whose flush did not finish, describes work whose durable\n * state nobody knows; waking it would spend its owner's money on a guess, and\n * deciding those sessions is the morning list's job, not this path's. That\n * holds whether or not the recorded session happens to be live in this process:\n * liveness is not the park outcome, and a session can be live without ever\n * having stopped.\n * @module dsh-pause-session/resume\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { CommandResult } from '@deepseek-ai/dsh-commands'\nimport { createUserMessage } from '@deepseek-ai/dsh-llm'\nimport type { ContentBlock, LlmCallConfig } from '@deepseek-ai/dsh-llm'\nimport type { Session, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: brings `ctx.get('goals')` and the goal view this path reads.\nimport type {} from '@deepseek-ai/dsh-goal'\nimport type { PauseGate } from './gate.js'\nimport { UnresumableSessionError, restoreComposition } from './composition.js'\nimport { owesReply } from './history.js'\nimport type { ParkRegistry } from './registry.js'\nimport { describeError } from './shared.js'\nimport { walkDescendants } from './tree.js'\n\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n /**\n * Text dsh-pause-session synthesized on the session's behalf: the wake this\n * path delivers and the unknown-outcome note a decision adds. Declared so\n * the log, and anything reading it, can tell plugin-authored input from the\n * user's own.\n */\n 'pause': { kind: 'pause' }\n }\n}\n\n/**\n * The one wake content a route that keeps an empty user message is given. It\n * names its producer, because it lands in the same user turn as the input the\n * model owes an answer to and must not read as part of it.\n */\nexport const WAKE_MARKER = '[pause: resumed from a pause]'\n\n/**\n * How one resume asks the loop for the request it owes the session:\n *\n * - `silent`: the clean path's wake — an empty input on a route the\n * composition declared silent, and the visible marker on any other route.\n * - `message`: the decision list's \"continue, and say the tool outcome is\n * unknown\" — user-visible text on the wire by construction, and the only\n * resume that is not invisible.\n * - `none`: no wake at all. This is what \"leave it for me to read\" costs: the\n * session is not activated, nothing is asked of the model, and the park is\n * exactly as it was.\n */\nexport type WakeRequest =\n | { readonly kind: 'silent' }\n | { readonly kind: 'message'; readonly text: string }\n | { readonly kind: 'none' }\n\n/** Result copy for a resumed session; the model never sees it. */\nexport const RESUMED_TEXT = 'This session resumed from where it paused.'\n\n/**\n * Result copy for a registered session this path refuses: its durable state is\n * not the outcome of a completed park, so waking it would be a guess.\n */\nexport const UNCLEAN_TEXT = 'pause did not resume this session: its pause did not finish cleanly,'\n + ' so it needs your decision.'\n\n/** Result copy when nothing is parked under that session. */\nexport const NOT_PARKED_TEXT = 'pause has no pause recorded for this session.'\n\n/** Result copy for a session that is parked but could not be loaded back. */\nexport const ACTIVATION_FAILED_TEXT = 'pause could not reopen this session, so it stayed paused.'\n\n/** Result copy for a one-shot subagent route: its Session exists, but no resume may reactivate it. */\nexport const UNRESUMABLE_TEXT = 'pause did not resume this session: it is a one-shot subagent route and cannot be resumed.'\n\n/**\n * Result copy for the review decision. It is the one answer that promises\n * nothing happened, because opening a session to read it is the user's own\n * action in their session list — and activating its agent here would start a\n * session they asked only to look at.\n */\nexport const REVIEW_TEXT = 'pause did not activate this session: it is still parked, and nothing was sent to a model.'\n + ' Open it from your session list when you want to read it.'\n\n/** Result copy for a change the durable registry refused to record. */\nexport const UNRECORDED_TEXT = 'pause could not write the pause registry, so it did not change the pause state.'\n\n/** Copy for one goal opt-out change. */\nexport const REARM_GOAL_ON_TEXT = 'pause will re-open this session\\'s goal when it resumes it.'\nexport const REARM_GOAL_OFF_TEXT = 'pause will leave this session\\'s goal closed when it resumes it.'\n\n/** Why one record did not produce a running session. */\nexport type ResumeRefusal = 'not-parked' | 'not-clean' | 'activation-failed' | 'unrecorded' | 'not-activated' | 'unresumable'\n\n/** What one resume attempt did. */\nexport interface ResumeOutcome {\n readonly sessionId: SessionId\n /** Whether the session is now running from the point it stopped. */\n readonly resumed: boolean\n /** Present exactly when `resumed` is false. */\n readonly refusal?: ResumeRefusal\n}\n\n/** One resume attempt plus the answer the command that asked for it receives. */\nexport interface ResumeAttempt extends ResumeOutcome {\n readonly answer: CommandResult\n}\n\n/** How this path is wired into the session control that owns the pause state. */\nexport interface ResumeDeps {\n /**\n * Take a live session's pause back: the durable record goes first, then the\n * gate. Owned by the session control so there is one release order.\n */\n release(sessionId: SessionId, text: string): Promise<CommandResult>\n /**\n * Whether the pause this process holds was adopted from the registry rather\n * than pressed here. Only an adopted pause is a restart: a press this process\n * made and took back must never wake a model.\n */\n adopted(sessionId: SessionId): boolean\n}\n\n/** Route knowledge this path needs from the composition. */\nexport interface ResumeOptions {\n /**\n * Provider routes whose request conversion drops an empty user message, so an\n * empty wake never reaches the model. Deployment-owned because it is a\n * property of the adapter behind each route, and a third-party plugin cannot\n * introspect an adapter.\n */\n readonly silentWakeRoutes: readonly string[]\n}\n\n/** The clean resume path, as the session control drives it. */\nexport interface ResumePath {\n /**\n * Resume one session the registry remembers.\n * @param sessionId - the session to resume.\n * @param request - which wake this resume delivers; see {@link WakeRequest}.\n * @param parentId - the direct parent a batch release already brought back, when there is one.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n resumeSession(\n sessionId: SessionId,\n request: WakeRequest,\n parentId?: SessionId,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt>\n /**\n * Resume one session the decision list officially owns — a record that is\n * known NOT to be clean, which the clean path above must refuse and this entry\n * exists to carry out. Same release order, same wake vocabulary.\n * @param sessionId - the listed session to bring back.\n * @param request - which wake the decision delivers; see {@link WakeRequest}.\n * @param finalize - whether the decision continues the session (`'release'`)\n * or only answers it (`'review'`). A review activates nothing and writes\n * nothing: opening a session to read it is the user's own action, not this\n * plugin's.\n * @param parentId - the direct parent a batch release already brought back, when there is one.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n decideUnclean(\n sessionId: SessionId,\n request: WakeRequest,\n finalize: 'release' | 'review',\n parentId?: SessionId,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt>\n /**\n * Resume every session the registry remembers, clean ones only. One session's\n * failure never stops the others.\n * @param signal - cancellation checked between release levels and forwarded to each load.\n * @returns one attempt per recorded session, in registry order.\n */\n resumeParked(signal?: AbortSignal): Promise<readonly ResumeAttempt[]>\n /**\n * Record or clear one session's goal opt-out.\n * @param sessionId - the parked session to change.\n * @param rearmGoal - whether the clean resume path may re-arm its goal.\n * @returns the copy for whoever asked.\n */\n setRearmGoal(sessionId: SessionId, rearmGoal: boolean): Promise<CommandResult>\n /**\n * Release one parked session and the registered members of its tree: the\n * counterpart of a press that registered a whole tree.\n * @param sessionId - the session whose tree the user released.\n * @param signal - cancellation for the tree read and each member's load.\n * @returns what happened to the tree, and the copy for whoever asked.\n */\n releaseTree(sessionId: SessionId, signal?: AbortSignal): Promise<ReleaseTreeOutcome>\n}\n\n/** What releasing one session's registered tree did. */\nexport interface ReleaseTreeOutcome {\n /** How many sessions this release covered: the session plus its registered descendants. */\n readonly members: number\n /** How many of them are running again. */\n readonly resumed: number\n /** How many could not be resumed, and why the release did not take them back. */\n readonly refusals: readonly { readonly sessionId: SessionId; readonly refusal: ResumeRefusal }[]\n /** The copy for whoever pressed the release. */\n readonly answer: CommandResult\n}\n\n/**\n * How one tree-wide release answers the person who pressed it. One release, one\n * sentence about all of it: what came back, and — by name, because those rows\n * are what the morning list is for — what did not.\n * @param released - the sessions this release covered, in release order.\n * @returns the result text, e.g. `pause resumed 2 sessions from where they stopped; \"child\" did not pause completely`.\n */\nexport function summarizeTreeRelease(released: readonly ResumeAttempt[]): string {\n const resumed = released.filter(attempt => attempt.resumed).length\n const head = resumed === 0\n ? 'pause resumed nothing from this tree'\n : resumed === 1\n ? 'pause resumed 1 session from where it stopped'\n : `pause resumed ${resumed} sessions from where they stopped`\n const refused = released.filter(attempt => !attempt.resumed)\n if (refused.length === 0) return `${head}.`\n const named = refused\n .filter(attempt => attempt.refusal === 'not-clean')\n .map(attempt => `\"${String(attempt.sessionId)}\"`)\n const rest = named.length === 0\n ? `${String(refused.length)} could not be resumed`\n : refused.length === named.length\n ? `${named.join(', ')} did not pause completely`\n : `${named.join(', ')} did not pause completely, and ${String(refused.length - named.length)} more could not be resumed`\n return `${head}; ${rest}.`\n}\n\n/**\n * How a batch release is sequenced: the tree edge each child was found under,\n * and the depth levels a release walks, shallowest first.\n */\ninterface ReleaseOrder {\n /** Direct parent inside the recorded set, for the sessions that have one. */\n readonly parents: ReadonlyMap<SessionId, SessionId>\n readonly levels: readonly (readonly SessionId[])[]\n}\n\n/**\n * Build the clean resume path.\n * @param ctx - host context carrying the agent store, the session store, and the goal service when one is mounted.\n * @param gate - the pause gate the resume releases.\n * @param registry - the durable list this path reads and forgets from.\n * @param options - the route knowledge the wake content depends on.\n * @param deps - the control-owned release order and the adopted-pause fact.\n * @returns the path the session control exposes.\n */\nexport function installResumePath(\n ctx: Context,\n gate: PauseGate,\n registry: ParkRegistry,\n options: ResumeOptions,\n deps: ResumeDeps,\n): ResumePath {\n /** The route the session's next request will use, as its own log recorded it. */\n const routeOf = (session: Session): LlmCallConfig | undefined => session.requestHeader()?.config\n\n /**\n * Give an agent that was loaded back without one the route its own log\n * recorded. The loop refuses to build a request without a provider and model,\n * and the persisted header is what the app's own activation path uses too, so\n * this resumes onto the route the session stopped on.\n *\n * 0.2.2 replaced the installed route selection with the durable model-selection\n * fold: `agent.model.current()` already returns a route replayed from the\n * Session's own request headers, and `select()` records the header's route when\n * that fold and the Agent options are both empty. The old per-agent\n * `installModelSelection(agentCtx, selection)` no longer accepts a selection.\n */\n const ensureRoute = (agent: Agent): void => {\n if (agent.model.current() !== undefined) return\n const config = routeOf(agent.session)\n if (config === undefined) return\n agent.model.select({\n provider: config.provider,\n model: config.model,\n ...config.reasoningEffort === undefined ? {} : { reasoningEffort: config.reasoningEffort },\n })\n }\n\n /** What one wake input carries for the route in use. */\n const wakeContent = (provider: string): ContentBlock[] =>\n options.silentWakeRoutes.includes(provider) ? [] : [{ type: 'text', text: WAKE_MARKER }]\n\n /**\n * Ask the loop for the request it owes this session. Nothing is woken when a\n * request is already standing at the gate or the loop is already running: the\n * continuation is in flight, and a second wake would duplicate it. The content\n * is the route's business only for the silent wake; a note is the user's\n * decision and goes on the wire as written, on every route. Either way the\n * message is attributed to this plugin: the wake is this path speaking on the\n * session's behalf, not the user's own input.\n * @param agent - the live agent to wake.\n * @param request - which wake this resume delivers.\n */\n const wake = (agent: Agent, request: WakeRequest): void => {\n if (request.kind === 'none') return\n if (gate.isHeld(agent.id) || agent.status !== 'idle') return\n if (!owesReply(agent.session.deriveMessages())) return\n const provider = routeOf(agent.session)?.provider\n if (provider === undefined) return\n const content: ContentBlock[] = request.kind === 'message'\n ? [{ type: 'text', text: request.text }]\n : wakeContent(provider)\n agent.followup(createUserMessage({ content, source: { kind: 'pause' } }))\n }\n\n /**\n * Re-open one session's goal. Activation is process-local and is not part of\n * the durable goal, so a restart leaves a healthy goal disarmed; only that\n * case is repaired here. A goal the owner paused, blocked, or completed is a\n * decision, and this path does not take decisions back. The wake (when there\n * is one) runs first on purpose: the goal round driver queues the next round\n * as soon as the agent is idle, and that round must not join the request that\n * is answering the input already pending.\n * @param agent - the agent whose goal may be re-armed.\n */\n const rearmGoal = (agent: Agent): void => {\n const goals = ctx.get('goals')\n if (goals === undefined) return\n try {\n const goal = goals.get(agent)\n if (goal === undefined || goal.phase !== 'active' || goal.activation !== 'disarmed') return\n if (goal.roundsStarted >= goal.maxGoalRounds) return\n goals.resume(agent, { id: goal.id, revision: goal.revision })\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not re-open the goal of \"${agent.id}\": ${describeError(error)}`)\n }\n }\n\n /**\n * Load one parked session back from its stored log and hand it the request it\n * stopped in front of. The pause is not released until the durable record is\n * gone, so a record that cannot be dropped leaves the session parked rather\n * than silently running.\n *\n * Activation is what makes a session run again, so nothing but a resume does\n * it: the review answer never reaches this function.\n * @param sessionId - the parked session to bring back.\n * @param request - which wake the decision delivers; see {@link WakeRequest}.\n * @param rearm - whether the record's goal opt-out allows re-opening its goal.\n * @param parentId - the direct parent a batch release already brought back,\n * when there is one. A subagent child resumed under its live parent is the\n * shape the harness's own cold resume uses; with no live parent the session\n * comes back on its own, which is all that can be done with it.\n * @param signal - cancellation for the stored composition read and load.\n */\n const activate = async (\n sessionId: SessionId,\n request: WakeRequest,\n rearm: boolean,\n parentId?: SessionId,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const parent = parentId === undefined ? undefined : ctx.agents.get(parentId)\n let agent: Agent\n try {\n const composition = await restoreComposition(ctx, sessionId, parent, signal)\n // Owned by the root, not by this plugin's fiber: the session is the\n // user's, and it must outlive a plugin reload.\n agent = (await ctx.root.agents.resume({\n resumeSessionId: sessionId,\n ...parent === undefined ? {} : { parentAgent: parent },\n ...composition,\n ...signal === undefined ? {} : { signal },\n })).agent\n } catch (error: unknown) {\n if (error instanceof UnresumableSessionError) {\n return {\n sessionId,\n resumed: false,\n refusal: 'unresumable',\n answer: { kind: 'error', text: UNRESUMABLE_TEXT },\n }\n }\n ctx.logger.warn(`pause: could not open \"${sessionId}\" from its stored log: ${describeError(error)}`)\n return {\n sessionId,\n resumed: false,\n refusal: 'activation-failed',\n answer: { kind: 'error', text: ACTIVATION_FAILED_TEXT },\n }\n }\n ensureRoute(agent)\n // Delivered while the pause still holds the gate, so the request it builds\n // waits there and leaves only once the durable record does.\n // Whether that produced a wake says nothing about the release. A wake can\n // be refused because the log owes no answer at all (a session parked while\n // idle) or because the loop was already running; the user's decision to\n // continue is what releases the session in every one of those cases.\n wake(agent, request)\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: { kind: 'error', text: UNRECORDED_TEXT } }\n }\n gate.resume(sessionId)\n if (rearm) rearmGoal(agent)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n\n /**\n * Load one unclean session back from its stored log.\n *\n * This is the morning list's own entry point, and it exists precisely because\n * the clean path must keep refusing these records: the refusal is the clean\n * path's guarantee, not a guard this entry can borrow. A user who has read the\n * row and chosen one of the decisions that resume it has made the decision the\n * clean path is not allowed to make for them. The release order is the same\n * one: the durable record goes first, then the gate — so a record that cannot\n * be dropped leaves the session parked.\n *\n * A record this process holds no gate for is still this list's to clear: the\n * session is live, so the decision's own wake is what asks for its request,\n * and dropping the record is what stops a later process from re-holding a\n * session the user has already decided.\n * @param sessionId - the listed session to bring back.\n * @param request - which wake the decision delivers; see {@link WakeRequest}.\n * @param finalize - whether the decision continues the session (`'release'`)\n * or only answers it (`'review'`); a review activates nothing and writes\n * nothing.\n * @param parentId - the direct parent a batch release already brought back, when there is one.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n const decideUnclean = async (\n sessionId: SessionId,\n request: WakeRequest,\n finalize: 'release' | 'review',\n parentId?: SessionId,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const record = registry.get(sessionId)\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // Opening a session to read it is the user's own action in their session\n // list; this decision only answers, so it never activates an agent and\n // never writes. That is what makes it the safe choice on a session whose\n // side effects may already have happened.\n if (finalize === 'review') {\n return { sessionId, resumed: false, refusal: 'not-activated', answer: { kind: 'success', text: REVIEW_TEXT } }\n }\n const live = ctx.agents.get(sessionId)\n if (live === undefined) return await activate(sessionId, request, record.rearmGoal, parentId, signal)\n if (!gate.isPaused(sessionId)) {\n // This process holds nothing for the session — its record was found\n // unreadable at mount, or something else already released it. The session\n // is live, so the decision's own wake is what asks for the request it is\n // owed, and the record goes on the release path's own order.\n ensureRoute(live)\n wake(live, request)\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: { kind: 'error', text: UNRECORDED_TEXT } }\n }\n if (record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n const adopted = deps.adopted(sessionId)\n // An adopted pause with nothing standing at the gate has no continuation to\n // release: the process that built that request is gone, so the decision's\n // own wake is what asks for one.\n if (adopted) {\n ensureRoute(live)\n wake(live, request)\n }\n const released = await deps.release(sessionId, RESUMED_TEXT)\n if (released.kind === 'error') {\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: released }\n }\n if (adopted && record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: released }\n }\n\n /**\n * Resume one recorded session: the clean path's only entry point.\n *\n * The clean-only rule holds on every branch, live or not: a record that is\n * not `safe` is not a park that finished, so this path neither wakes it nor\n * drops it — whoever holds it stays held, and the row reaches the morning\n * list. A pause this process pressed itself is not a record at all: taking it\n * back is the press's own undo, and it delivers no wake.\n * @param sessionId - the recorded session to resume.\n * @param request - which wake this resume delivers; see {@link WakeRequest}.\n * @param parentId - the direct parent a batch release already brought back, when there is one.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n const resumeSession = async (\n sessionId: SessionId,\n request: WakeRequest,\n parentId?: SessionId,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const record = registry.get(sessionId)\n const live = ctx.agents.get(sessionId)\n\n if (live !== undefined) {\n if (!gate.isPaused(sessionId)) {\n // This process holds nothing here. An unclean record belongs to the\n // morning list, and refusing it here is what keeps it there.\n if (record !== undefined && record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // A clean record nothing is holding any more: the session is already\n // back (it became readable after this mount could not hold it), so the\n // park is over. The record goes first — a park this process cannot drop\n // is not one it may continue — and then the session is handed the\n // request it stopped in front of, which is this path's own job.\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: { kind: 'error', text: UNRECORDED_TEXT } }\n }\n ensureRoute(live)\n wake(live, request)\n if (record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n // A pause this process did NOT press is another process's verdict: only a\n // finished park may be woken from it. A press of this process's own is\n // the pause the user is taking back, and it delivers no wake either way.\n const adopted = deps.adopted(sessionId)\n if (adopted && record !== undefined && record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n // An adopted pause with nothing standing at the gate has no continuation\n // to release: the process that built that request is gone.\n if (adopted) {\n ensureRoute(live)\n wake(live, request)\n }\n const released = await deps.release(sessionId, RESUMED_TEXT)\n if (released.kind === 'error') {\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: released }\n }\n // Re-arming spends money on the session's own, so the record's opt-out\n // decides it — and only an adopted pause can have lost the activation.\n if (adopted && (record?.rearmGoal ?? true)) rearmGoal(live)\n return { sessionId, resumed: true, answer: released }\n }\n\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // The one rule of this path: a record that is not `safe` is not a park that\n // finished, and waking it would act on a durable state nobody knows.\n if (record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n return await activate(sessionId, request, record.rearmGoal, parentId, signal)\n }\n\n /**\n * Order the recorded sessions so a session is released after the parent it\n * hangs from: waking a child needs its direct parent to be live first. The\n * relation is read from the parent catalogs themselves (which children this\n * session actually has), never guessed from ids; a branch whose catalog\n * cannot be read simply has no known parent and is released with the roots.\n * @param sessionIds - every recorded session, in registry order.\n * @param signal - cancellation for each catalog read.\n * @returns the parent of each child, and one list per depth level, shallowest first.\n */\n const releaseOrder = async (sessionIds: readonly SessionId[], signal?: AbortSignal): Promise<ReleaseOrder> => {\n const owned = new Set(sessionIds)\n const parentOf = new Map<SessionId, SessionId>()\n const subagents = ctx.get('subagents')\n if (subagents !== undefined) {\n for (const sessionId of sessionIds) {\n signal?.throwIfAborted()\n try {\n for (const child of await subagents.listChildren(sessionId, signal)) {\n if (owned.has(child.id)) parentOf.set(child.id, sessionId)\n }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the children of \"${sessionId}\": ${describeError(error)}`)\n }\n }\n }\n const depthOf = (sessionId: SessionId): number => {\n let steps = 0\n const seen = new Set<SessionId>([sessionId])\n for (let at = parentOf.get(sessionId); at !== undefined; at = parentOf.get(at)) {\n // A damaged catalog could describe a cycle; a session in one is a root.\n if (seen.has(at)) break\n seen.add(at)\n steps += 1\n }\n return steps\n }\n const levels = new Map<number, SessionId[]>()\n for (const sessionId of sessionIds) {\n const level = depthOf(sessionId)\n const bucket = levels.get(level)\n if (bucket === undefined) levels.set(level, [sessionId])\n else bucket.push(sessionId)\n }\n return {\n parents: parentOf,\n levels: [...levels.keys()].sort((left, right) => left - right)\n .map(level => levels.get(level) ?? []),\n }\n }\n\n return {\n resumeSession,\n decideUnclean,\n releaseTree: async (sessionId, signal) => {\n // The tree is read here, not taken from the press: the members that were\n // registered are exactly the members whose records exist, and a member\n // the walk cannot reach has no record to release either.\n const rooted = [sessionId, ...(await walkDescendants(ctx, sessionId, signal)).members.map(member => member.id)]\n const registered = rooted.filter(member => registry.get(member) !== undefined)\n if (registered.length === 0) {\n return { members: 0, resumed: 0, refusals: [], answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // One control, two jobs. A tree with nothing clean in it is a press being\n // taken back: the user is undoing the pause they just asked for, and the\n // clean-only rule must not stand in the way of that — it would leave a\n // session the user cancelled still parked, which is the one thing the\n // press-again button must never do. A tree that holds anything clean is\n // the release, and there the rule holds per record: the members that did\n // not stop cleanly keep their pause and are never silently woken.\n const cancelling = registered.every(member => registry.get(member)?.phase !== 'safe')\n const released: ResumeAttempt[] = []\n if (cancelling) {\n for (const member of registered) {\n signal?.throwIfAborted()\n const answer = await deps.release(member, RESUMED_TEXT)\n released.push(answer.kind === 'error'\n ? { sessionId: member, resumed: false, refusal: 'unrecorded', answer }\n : { sessionId: member, resumed: true, answer })\n }\n } else {\n const release = await releaseOrder(registered, signal)\n for (const level of release.levels) {\n signal?.throwIfAborted()\n for (const member of level) {\n // The rule, applied here rather than left to the entry points\n // below: a pause this process pressed is normally the user's own\n // to take back, so the clean path releases it without asking its\n // phase. In a tree release it is not — a member that did not stop\n // cleanly keeps its pause, and the caller names it.\n const record = registry.get(member)\n if (record !== undefined && record.phase !== 'safe') {\n released.push({\n sessionId: member,\n resumed: false,\n refusal: 'not-clean',\n answer: { kind: 'error', text: UNCLEAN_TEXT },\n })\n continue\n }\n released.push(await resumeSession(member, { kind: 'silent' }, release.parents.get(member), signal))\n }\n }\n }\n const root = released.find(attempt => attempt.sessionId === sessionId)\n const refusals = released\n .filter((attempt): attempt is ResumeAttempt & { refusal: ResumeRefusal } => !attempt.resumed && attempt.refusal !== undefined)\n .map(attempt => ({ sessionId: attempt.sessionId, refusal: attempt.refusal }))\n return {\n members: released.length,\n resumed: released.filter(attempt => attempt.resumed).length,\n refusals,\n // The root's own answer is what the person who pressed sees: their\n // session either came back or it did not, and the tree's other members\n // are named in the count and in the refusals.\n answer: root === undefined\n ? { kind: 'success', text: NOT_PARKED_TEXT }\n : { kind: refusals.length === 0 && root.resumed ? 'success' : 'error', text: summarizeTreeRelease(released) },\n }\n },\n resumeParked: async (signal) => {\n const release = await releaseOrder(registry.list().map(entry => entry.sessionId), signal)\n const attempts: ResumeAttempt[] = []\n for (const level of release.levels) {\n signal?.throwIfAborted()\n // One release per level and no concurrency cap inside it: the levels\n // are the milliseconds a child's wake needs, not a queue.\n attempts.push(...await Promise.all(level.map(sessionId =>\n resumeSession(sessionId, { kind: 'silent' }, release.parents.get(sessionId), signal))))\n }\n return attempts\n },\n setRearmGoal: async (sessionId, rearmGoal) => {\n try {\n await registry.setRearmGoal(sessionId, rearmGoal)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not record the goal opt-out of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'error', text: UNRECORDED_TEXT }\n }\n return { kind: 'success', text: rearmGoal ? REARM_GOAL_ON_TEXT : REARM_GOAL_OFF_TEXT }\n },\n }\n}\n","/**\n * The morning decision list: every session that did not stop cleanly, the facts\n * its own log carries about where it stopped, and the listed verbs the user can\n * take on it.\n *\n * A session gets here by NOT being on the clean path. A record whose phase is\n * `safe` is woken silently by `./resume.ts` and never appears here; the other\n * three phases describe work nobody can vouch for, and guessing on them would\n * spend the owner's money on a guess. This module is the other half of that\n * rule, not a second copy of it: the reading is taken from the session's own\n * stored log through a read handle that claims no ownership and writes nothing,\n * so asking what happened cannot itself change what happened.\n *\n * WHAT ONE READ COSTS. The list is read again on every command-registry change\n * while the panel is open, and what it publishes is about the end of a log that\n * only grows, so `./coldlog.ts` bounds each row's read to that end. A row whose\n * facts the bound cannot hold reports itself unreadable rather than publishing\n * a fragment.\n *\n * WHAT A ROW REPORTS. \"Where it stopped\" is the last event of the stored log\n * with the turn and step it belongs to. \"Which tool was in flight\" is the\n * `tool/call` events that no durable `tool/result` answers — a call event is\n * only written once the loop reached the dispatch, so an unanswered one is\n * exactly a tool the real world ran and the log never heard back from. The\n * harness turns each of those into a synthetic `TOOL_OUTCOME_UNKNOWN` error\n * result when it next reads the log; this module derives the same answer from\n * the stored events instead of writing it back. Nothing here is inferred from\n * the fact that the session is listed.\n *\n * WHAT THE ROW PROMISES. `wake` says the host can hand this session the request\n * it owes, and the wake that keeps that promise is `./resume.ts`'s. Both read\n * one rule from `./history.ts` over the same derivation the loop reads, so a\n * promise this list publishes is a promise that wake keeps. When the log cannot\n * be folded into that derivation, the promise is withheld and the reading is\n * reported; a withheld promise costs a user nothing, a broken one spends their\n * money.\n *\n * WHO MAY DECIDE. A decision names its target as command input, and the command\n * channel carries no origin of its own, so this list acts only on rows its most\n * recent read published to the calling session: the caller's own read is what\n * makes a target actionable. Anything else is refused, not looked up.\n *\n * TWO OF THE FIVE VERBS PUT THE SESSION BACK ON THE CLEAN PATH. Continue and\n * continue-with-the-note go through `ResumePath.decideUnclean`, which activates\n * the session, wakes it and drops the durable record before the gate opens.\n *\n * THE OTHER THREE TOUCH NOTHING. Leave writes nothing at all: the record stays,\n * the session stays held, and the row is still on tomorrow's list. Review\n * answers without activating anything or writing anything — opening a session\n * to read it is the user's own action in their session history, and starting an\n * agent on their behalf is precisely what they declined. Goal flips the row's\n * own re-arm opt-out: it is the user path for the one thing a resume can spend\n * without the user watching it, and it takes effect on whatever resume comes\n * next. All five carry the same binding — the row must be one this caller was\n * shown — because each changes, or declines to change, durable state of a\n * parked session.\n * @module dsh-pause-session/triage\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` read seam and its handle.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport type { CommandResult } from '@deepseek-ai/dsh-commands'\nimport { coldRead, logFacts, positionOf, TRIAGE_WINDOW_EVENTS } from './coldlog.js'\nimport type { ParkRegistry, ParkedSession } from './registry.js'\nimport type { ResumePath } from './resume.js'\nimport type { TriageAction, TriageList, TriageRow } from './shared.js'\n\n/** The route a wake input has to be addressed to; see `./resume.ts`. */\ntype WakeOf = Parameters<ResumePath['resumeSession']>[1]\n\n/** Result copy for a decision the list itself could not carry out. */\nexport const TRIAGE_FAILED_TEXT = 'pause could not carry out that decision; the session is exactly as it was.'\n\n/** Result copy for leaving one listed session alone. */\nexport const TRIAGE_LEFT_TEXT = 'pause left this session paused; it is still on the list.'\n\n/** Result copy for a decision aimed at a session that is not on the list. */\nexport const TRIAGE_NOT_LISTED_TEXT = 'pause has no unfinished pause for this session.'\n\n/**\n * Result copy for a decision aimed at a row the calling session was not shown.\n * The list is the only thing that legitimizes a target, so a target nobody read\n * is refused rather than acted on.\n */\nexport const TRIAGE_UNSHOWN_TEXT = 'pause did not act: this Session has not been shown that row.'\n + ' Read the list first (/pause-triage).'\n\n/**\n * Model-facing text that goes with a wake when the user chooses to continue\n * with a note.\n *\n * It is written the way the harness writes its own recovery text: the same two\n * facts the synthetic tool result states (the call's outcome was never recorded;\n * verify before retrying anything with side effects), in the same voice, with no\n * mention of the stop mechanism. The harness's own version lives in the tool\n * result for that call, which is why this one does not repeat the call's name —\n * it warns about the step as a whole, and the specific result sits directly\n * above it in the history.\n */\nexport const UNKNOWN_OUTCOME_NOTE = 'A tool call from this step was interrupted: its call was recorded but no '\n + 'result was durably recorded, so its outcome is unknown. If it had side effects, verify the external state '\n + 'before doing anything else, and do not retry it blindly. Then continue with what I asked for.'\n\n/**\n * One registry entry this list owns: every park outcome but `safe`, which is\n * the silent path's business and never appears here.\n */\ntype ListedPark = Omit<ParkedSession, 'phase'> & { readonly phase: TriageRow['phase'] }\n\n/**\n * One row's facts, as the session's own log reports them.\n * @param ctx - host context carrying the registered message projections.\n * @param entry - the durable park record this row is built from.\n * @param events - the events the bounded read returned, in sequence order.\n * @param complete - whether those events are the whole stored log.\n * @returns the row, ready to publish, or `undefined` when the read cannot support it.\n */\nfunction rowOf(\n ctx: Context,\n entry: ListedPark,\n events: readonly SessionEvent[],\n complete: boolean,\n): TriageRow | undefined {\n const { sessionId, phase, updatedAt, rearmGoal } = entry\n const facts = logFacts(events, complete, ctx.sessions.messageProjections)\n // A log this read cannot fold into those facts says nothing rather than\n // something: the row reports itself unreadable, which is what a list that\n // must not lie does when it does not know.\n if (facts === undefined) return undefined\n const { last, traces, owed, wakeable } = facts\n return {\n sessionId: String(sessionId),\n phase,\n updatedAt,\n reading: 'read',\n ...last === undefined ? {} : { lastEvent: last.type, ...positionOf(last) },\n traces,\n owed,\n rearmGoal,\n // Two facts have to hold before this host can hand the session its owed\n // request: the log must name the route the wake would be addressed to, and\n // there must be an answer still owed for the wake to ask for.\n wakeable,\n }\n}\n\n/** What mounting the decision list hands back. */\nexport interface TriageListService {\n /**\n * Read the list for one calling session, and record the rows it was shown.\n * @param caller - the session the read was addressed to.\n * @param signal - cancellation for the stored-log reads.\n * @returns every session that did not stop cleanly, with the facts its own log carries.\n */\n read(caller: SessionId, signal?: AbortSignal): Promise<TriageList>\n /**\n * Carry out one decision on one listed session, for the caller that read it.\n * @param caller - the session the decision was addressed to.\n * @param sessionId - the listed session to act on.\n * @param action - which of the listed decisions was taken.\n * @param signal - cancellation for the cold load and setup.\n * @returns the outcome for whoever asked.\n */\n decide(\n caller: SessionId,\n sessionId: SessionId,\n action: TriageAction,\n signal?: AbortSignal,\n ): Promise<CommandResult>\n}\n\n/**\n * Mount the decision list over the durable registry and the clean resume path.\n *\n * The list is the registry's own complement: it asks for every record whose\n * phase is not `safe`, so a session that cleaned up is off the list by the same\n * fact that puts it on the silent path, and nothing has to be reconciled.\n * @param ctx - host context carrying the optional persistence service.\n * @param registry - the durable list of park outcomes.\n * @param resumePath - the path all three resuming decisions go through.\n * @param windowEvents - how many events of each stored log one read may fold.\n * @returns the list read and the decision verbs.\n */\nexport function mountTriageList(\n ctx: Context,\n registry: ParkRegistry,\n resumePath: ResumePath,\n windowEvents: number = TRIAGE_WINDOW_EVENTS,\n): TriageListService {\n /**\n * The rows each calling session was last shown by its own read. A decision\n * names its target as command input, and the command channel carries no\n * origin of its own, so this is what ties a target to the list the user read:\n * only a row this caller has been shown is acted on.\n */\n const shown = new Map<SessionId, ReadonlySet<string>>()\n\n const unreadable = (entry: ListedPark): TriageRow => ({\n sessionId: String(entry.sessionId),\n phase: entry.phase,\n updatedAt: entry.updatedAt,\n reading: 'unreadable',\n traces: [],\n owed: false,\n wakeable: false,\n rearmGoal: entry.rearmGoal,\n })\n\n const readable = async (entry: ListedPark, signal?: AbortSignal): Promise<TriageRow> => {\n const read = await coldRead(ctx, entry.sessionId, windowEvents, signal)\n // A log this host could not read says so. An empty row would claim the\n // session left no traces, which is precisely the verdict nobody has.\n if (read.kind === 'unreadable') return unreadable(entry)\n // Facts this bounded read cannot support say the same thing: the row is\n // reported unreadable rather than filled with a guess.\n return rowOf(ctx, entry, read.events, read.complete) ?? unreadable(entry)\n }\n\n /**\n * One decision, as the resume path's own verbs plus the two local verbs that\n * change nothing but the record. Review is the list's own \"do not run it\"\n * answer: it activates nothing and writes nothing, so the park is exactly as\n * it was. The goal toggle is the user path for the one thing a resume can\n * spend unasked.\n */\n const decide = async (\n caller: SessionId,\n sessionId: SessionId,\n action: TriageAction,\n signal?: AbortSignal,\n ): Promise<CommandResult> => {\n if (!(shown.get(caller)?.has(String(sessionId)) ?? false)) return { kind: 'error', text: TRIAGE_UNSHOWN_TEXT }\n const record = registry.get(sessionId)\n // `safe` is not a decision this list owns: that session belongs to the\n // silent path, and acting here would be a second resume.\n if (record === undefined || record.phase === 'safe') {\n return { kind: 'error', text: TRIAGE_NOT_LISTED_TEXT }\n }\n if (action === 'leave') return { kind: 'success', text: TRIAGE_LEFT_TEXT }\n if (action === 'goal') return await resumePath.setRearmGoal(sessionId, !record.rearmGoal)\n // Review is its own kind of decision, not a wake-less resume: it leaves the\n // park exactly as it was, so the row is still there for tomorrow and\n // nothing has been continued — or even activated — on the user's behalf.\n if (action === 'review') {\n return (await resumePath.decideUnclean(sessionId, { kind: 'none' }, 'review', undefined, signal)).answer\n }\n const wake: WakeOf = action === 'continue' ? { kind: 'silent' } : { kind: 'message', text: UNKNOWN_OUTCOME_NOTE }\n return (await resumePath.decideUnclean(sessionId, wake, 'release', undefined, signal)).answer\n }\n\n return {\n read: async (caller, signal) => {\n signal?.throwIfAborted()\n const rows = await Promise.all([...registry.parked.values()]\n .flatMap(entry => entry.phase === 'safe' ? [] : [readable({ ...entry, phase: entry.phase }, signal)]))\n shown.set(caller, new Set(rows.map(row => row.sessionId)))\n return { rows }\n },\n decide,\n }\n}\n","/**\n * The session control the browser half drives: the pause verb, the park state\n * machine behind it, and the live state channel that accompanies both.\n *\n * A third-party plugin can add neither a `ctx.remote` namespace nor a forwarded\n * event: the Client's Remote namespaces and the forwarded-event allowlist are\n * compiled into the harness's own API assembly. The documented Client-callable\n * Host entry point is therefore a session command run through\n * `ctx.remote.commands.execute`, and the one live channel such a plugin can\n * publish through is the command registry itself — `commands/change` is a\n * forwarded event, and `commands.list(sessionId)` is a Client-callable read.\n *\n * That same registry is how a phase change leaves this process: a transition\n * re-registers the status verb, which emits the change a client needs to read\n * the new phase. It is the only way a third-party plugin can announce anything,\n * and it is what makes the flush that resolves `parking` into `safe` observable\n * at all — that step changes no registration of its own.\n *\n * So the resume definition IS the paused marker: it is registered on a paused\n * session's own agent context and removed on resume, and the browser half reads\n * \"paused\" from that registration rather than inventing it. A descriptor cannot\n * change after registration, so the park state rides a companion status\n * definition (registered beside the marker) whose answer is derived per call\n * rather than frozen into a descriptor — see `./shared.ts`. That one read\n * carries the record's goal opt-out too, so the session-header control can show\n * and switch it while the session is parked and clean — the state in which a\n * silent morning resume would otherwise spend the user's rounds unasked. The\n * switch is addressed to the session it changes, so it needs no listed row.\n *\n * The header control is the user's button for ONE session's work, and that work\n * includes the subagents under it: the press registers the session and its\n * descendant tree, the release takes the same tree back, and the status the\n * header shows is the worst phase among the members the press registered. The\n * whole-app control covers every running tree and answers for all of them; this\n * one answers for the tree it owns.\n *\n * A mark is not a park. A session is parked only once nothing is left to drain:\n * no call in flight AND either its next model request is held at the gate or no\n * turn is open at all (there will be no next request to hold). Only then is the\n * session flushed, at the session level — `SessionHandle.flush()` does not\n * drain the routed live buffer, so it would record a park that a crash erases.\n * The \"safe to close\" phase appears only after that flush resolves.\n *\n * Every phase a pause reaches is written to the durable registry before it is\n * published — including the move into `parking`, which the status command may\n * therefore never serve ahead of the record. A restart re-holds exactly the\n * sessions the last process left parked, and a phase that could not be recorded\n * is never shown as cleaner than the record says.\n *\n * A park verb changes state, so it acts only on a person's command: a command\n * dispatched from inside a model turn is refused. The listed verbs add the\n * one binding their target needs — the row must be one the calling session was\n * shown — and that rule lives with the list (`./triage.ts`).\n * @module dsh-pause-session/control\n */\n\nimport type { Context, Fiber } from '@deepseek-ai/cordis'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { CommandDefinitionId } from '@deepseek-ai/dsh-commands/brand'\nimport type { CommandDefinition, CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'\nimport { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` observation seam.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport type { PauseGate } from './gate.js'\nimport { isResumableSession } from './composition.js'\nimport { mountGlobalPause } from './global.js'\nimport type { GlobalPause, LivePark, PressResult } from './global.js'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport { installResumePath, NOT_PARKED_TEXT } from './resume.js'\nimport type { ResumeAttempt, ResumeOptions, ResumePath } from './resume.js'\nimport { mountTriageList } from './triage.js'\nimport type { TriageListService } from './triage.js'\nimport { TRIAGE_NOT_LISTED_TEXT } from './triage.js'\nimport type { TriageAction } from './shared.js'\nimport {\n describeError, encodeParkStatus, encodeTriageList, foldTreePhases, GOAL_COMMAND, GOAL_DEFINITION_ID,\n PAUSE_COMMAND, PAUSE_DEFINITION_ID, RESUME_ALL_COMMAND, RESUME_ALL_DEFINITION_ID, RESUME_COMMAND,\n RESUME_DEFINITION_ID, STATE_ALL_COMMAND, STATE_ALL_DEFINITION_ID, STATUS_COMMAND, STATUS_DEFINITION_ID,\n PAUSE_ALL_COMMAND, PAUSE_ALL_DEFINITION_ID, TRIAGE_COMMAND, TRIAGE_CONTINUE_COMMAND,\n TRIAGE_CONTINUE_DEFINITION_ID, TRIAGE_DEFINITION_ID, TRIAGE_GOAL_COMMAND, TRIAGE_GOAL_DEFINITION_ID,\n TRIAGE_LEAVE_COMMAND, TRIAGE_LEAVE_DEFINITION_ID, TRIAGE_NOTE_COMMAND, TRIAGE_NOTE_DEFINITION_ID,\n TRIAGE_REVIEW_COMMAND, TRIAGE_REVIEW_DEFINITION_ID,\n} from './shared.js'\nimport type { ParkPhase, ParkStatus } from './shared.js'\nimport { walkDescendants } from './tree.js'\n\n/**\n * Result copy for a session that just reached the pause point; the model never\n * sees it. The count is the point: the header control is the user's button for\n * one session's work, and that work includes the subagents under it, so the\n * press must not read as having stopped a single thing.\n * @param agents - how many subagents this press registered alongside the session.\n * @returns the copy for the person who pressed.\n */\nfunction pausedText(agents: number): string {\n const registered = agents === 0\n ? 'this session'\n : agents === 1\n ? 'this session and 1 subagent'\n : `this session and ${String(agents)} subagents`\n return `Paused ${registered}. A running tool will finish first; no new model request will be sent.`\n + ' Press again to resume.'\n}\n\n/** Result copy for a press or a release the durable registry refused to record. */\nconst UNRECORDED_TEXT = 'pause could not write the pause registry, so it did not change the pause state.'\n\n/**\n * What a tree-wide press reports when it could not read the tree. The session\n * itself is paused and says so; its subagents are neither registered nor\n * vouched for, and the answer names that limit rather than letting the session's\n * own park stand for a tree nobody read.\n */\nconst TREE_UNREAD_TEXT = 'pause could not read this session\\'s subagent list, so it did not pause its subagents.'\n\n/** Result copy for a session whose own subagent route is terminal. */\nconst NOT_RESUMABLE_PARK_TEXT = 'pause did not register this session: its subagent route is one-shot and cannot be resumed.'\n\n/**\n * Result copy for a tree-wide press that could not record part of the tree.\n * The members it names are still running; the ones it does not are paused.\n * @param names - the members whose park record could not be written.\n * @returns the copy for the person who pressed.\n */\nfunction unrecordedTreeText(names: readonly SessionId[]): string {\n return `pause could not save the pause for ${names.map(name => `\"${String(name)}\"`).join(', ')};`\n + ' they are still running. Everything else in the tree is paused.'\n}\n\n/**\n * Result copy for a state-changing park verb issued from inside a model turn.\n * The invocation carries no origin of its own, so this refusal is the plugin's\n * own reading of the one signal that separates a person's command from a\n * model's.\n */\nexport const UNATTENDED_TEXT = 'pause did not act: this command came from inside a model turn,'\n + ' and the park verbs act only on a person\\'s command.'\n\n/**\n * Whether a command is being dispatched from inside a model turn.\n *\n * A person's slash command reaches the registry through the Client's command\n * Remote with no model turn around it; the loop runs every model-driven step —\n * tool calls included — under its own initiator boundary. The invocation itself\n * carries no origin: `command/run` records a constant `{ kind: 'user' }` source\n * for every executor caller. This is therefore the one signal available, and a\n * state-changing verb refuses when it says a model is driving.\n * @param ctx - host context carrying the agent store.\n * @returns whether a model turn is the initiator of this call.\n */\nfunction modelIssued(ctx: Context): boolean {\n try {\n return ctx.agents.currentInitiator() !== undefined\n } catch {\n // The initiator scope is already disposed: this call belongs to teardown,\n // where refusing is the direction that cannot spend anyone's money.\n return true\n }\n}\n\n/**\n * Whether this host can read one recorded session at all — a live session, or a\n * stored log the persistence backend can observe.\n * @param ctx - host context carrying the session store and the optional persistence service.\n * @param sessionId - the recorded session to look for.\n * @returns whether the session can be acted on here.\n */\nasync function sessionReadable(ctx: Context, sessionId: SessionId): Promise<boolean> {\n if (ctx.sessions.get(sessionId) !== undefined) return true\n const persistence = ctx.get('sessionPersistence')\n // No persistence service means no stored session is readable here, which is\n // the same answer the decision list's own cold read gives.\n if (persistence === undefined) return false\n try {\n return await persistence.stat(sessionId) !== undefined\n } catch (error: unknown) {\n // A failing observation is not proof of absence, and dropping a park on a\n // guess is the one direction this control must not take.\n ctx.logger.warn(`pause: could not look for the stored log of \"${sessionId}\": ${describeError(error)}`)\n return true\n }\n}\n\n/** What one tree-wide press registered, and what it could not. */\nexport interface PressTreeOutcome {\n /** How many sessions this press covered: the session plus its descendants. */\n readonly members: number\n /** How many of them now have a durable park record behind them. */\n readonly registered: number\n /** Whether the pressed session itself is one of them. */\n readonly rootRegistered: boolean\n /** The members whose record could not be written; they are not paused. */\n readonly unrecorded: readonly SessionId[]\n /** Whether the tree walk itself failed, so the session's subagents were not covered. */\n readonly unread: boolean\n}\n\n/** One session's park in progress. */\ninterface ParkMark {\n /**\n * The phase the durable registry confirms for this session — the only phase\n * ever published. `draining` is the state a press creates; the first moment\n * nothing is left to drain moves it to `parking`, and the session-level flush\n * resolves it to `safe` — or to `flush-failed`, which must never be reported\n * as clean. It is written by `commit` and by nothing else, so the answer the\n * status command serves can never run ahead of what is on disk.\n */\n phase: ParkPhase\n /**\n * Whether this attempt still owns its mark. Cancelling during the drain bumps\n * the generation, so the flush that lands afterwards cannot publish a state\n * for a session the user has already returned to normal.\n */\n generation: number\n /**\n * Where this mark came from. A `press` mark walks the park machine forward in\n * this process. A `registry` mark is the durable outcome of an earlier park\n * episode, adopted at mount so the session is held again: it never advances,\n * because a flush this process runs cannot testify about how the session\n * stopped before it started, and re-deriving one would turn last night's\n * unclean entry into a false \"clean\".\n */\n origin: 'press' | 'registry'\n /**\n * Whether a move out of `draining` is already in flight. The mark keeps\n * answering with its last durable phase until that write lands, and the flag\n * keeps a second look from starting a second flush for the same park.\n */\n advancing: boolean\n}\n\n/**\n * The commands one paused session publishes: the marker set a client reads the\n * pause from, plus the one way this process can say that its answer changed.\n *\n * A third-party plugin can add neither a Client Remote namespace nor a forwarded\n * event, so the command registry is its only live channel — and the only thing\n * that makes the registry speak is a registration or an unregistration, which is\n * what emits `commands/change`. {@link ParkMarkers.announce} therefore refreshes\n * the status verb's own registration; the three commands a client sees are the\n * same before and after, and the pair of events is that client's cue to read\n * once and learn the new phase.\n */\ninterface ParkMarkers {\n /** The fiber owning all three registrations; disposing it removes every one of them. */\n readonly fiber: Fiber\n /** Announce that this session's park phase changed; a no-op until the markers are actually registered. */\n announce(): void\n}\n\n/** What mounting the session control hands back to the plugin entry. */\nexport interface SessionControl {\n /**\n * Hold every session the registry remembers and can read, and publish the\n * marker for the ones that already have a live agent. Called once, right\n * after mount.\n */\n adopt(): Promise<void>\n}\n\n/** How resuming all parked sessions answers the command that asked for it. */\nexport function summarizeResume(attempts: readonly ResumeAttempt[]): string {\n const resumed = attempts.filter(attempt => attempt.resumed).length\n const refused = attempts.length - resumed\n const head = resumed === 0\n ? 'pause resumed no session'\n : resumed === 1\n ? 'pause resumed 1 session from where it stopped'\n : `pause resumed ${resumed} sessions from where they stopped`\n if (refused === 0) return `${head}.`\n const unclean = attempts.filter(attempt => attempt.refusal === 'not-clean').length\n const rest = unclean === refused\n ? `${refused} did not pause completely and need your decision`\n : `${refused} could not be resumed`\n return `${head}; ${rest}.`\n}\n\n/**\n * Everything the session control needs from the composition: the clean resume\n * path's route knowledge, plus the bound the decision list reads each parked\n * log under.\n */\nexport interface SessionControlOptions extends ResumeOptions {\n /** Events of each stored log the decision list may fold per row; see `./coldlog.ts`. */\n readonly maxListedLogEvents?: number\n}\n\n/**\n * Contribute the session control: the pause verb, the per-session status read,\n * the global resume verb, and — for every paused session that has a live agent\n * — the resume marker.\n * @param ctx - host context carrying the command registry, the agent store, the\n * session store, and the projection registry.\n * @param gate - the pause gate this control drives and reflects.\n * @param watch - the per-session activity fold the park predicate reads.\n * @param registry - the durable pause list every phase change is written to.\n * @param options - route knowledge the clean resume path needs, and the decision list's read bound.\n * @returns the mount-time adoption step and the morning resume path.\n */\nexport function installSessionControl(\n ctx: Context,\n gate: PauseGate,\n watch: ParkWatch,\n registry: ParkRegistry,\n options: SessionControlOptions,\n): SessionControl {\n /** Marks per paused session; presence here mirrors the gate's own membership. */\n const marks = new Map<SessionId, ParkMark>()\n /** Published marker sets per paused session; disposing one removes its three commands. */\n const markers = new Map<SessionId, ParkMarkers>()\n /**\n * Records this process did not hold again because the session behind them\n * cannot be read here. Nothing can be parked, released, or decided for them\n * from this process, so they are not marks — and the aggregate names them\n * instead of counting them among the sessions this park owns.\n */\n const unheld = new Set<SessionId>()\n /**\n * Sessions a press of this process tried to register and the registry\n * refused. They are running, unmarked, and invisible to the live accessor,\n * so this is the only thing that keeps the aggregate from quietly omitting\n * them; see {@link GlobalPause.markUnrecorded}.\n */\n const unrecorded = new Set<SessionId>()\n /** Bumped by every mark and every cancel; a stale generation publishes nothing. */\n let generation = 0\n\n const drop = (sessionId: SessionId): void => {\n marks.delete(sessionId)\n const marker = markers.get(sessionId)\n if (marker === undefined) return\n markers.delete(sessionId)\n void Promise.resolve(marker.fiber.dispose()).catch((error: unknown) => {\n ctx.logger.warn(`pause: session control cleanup failed: ${describeError(error)}`)\n })\n }\n\n /**\n * Write one phase to the durable registry, then publish it on the mark and\n * announce it on the command channel.\n *\n * The order is the whole point: a phase is published only once a later\n * process would read it back. A failed write leaves the mark on its previous\n * phase, which is never cleaner than what the registry holds — the direction\n * that matters, because \"safe to close\" is a promise about surviving the\n * process, not about this process's memory.\n *\n * A phase that actually changed is also the moment every client is owed a\n * fresh read: the announcement is what carries `parking` → `safe` out of this\n * process, since the flush that resolves it changes no registration by\n * itself. Announcing only on a change is also what keeps the channel quiet —\n * a write that repeats the phase a mark already holds tells a reader nothing,\n * so it must not cost one.\n * @param sessionId - the paused session.\n * @param mark - its live mark, updated in place on success.\n * @param phase - the phase to record and publish.\n * @returns whether the phase became durable.\n */\n const commit = async (sessionId: SessionId, mark: ParkMark, phase: ParkPhase): Promise<boolean> => {\n try {\n await registry.record(sessionId, phase)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not record the park state of \"${sessionId}\" as ${phase}: ${describeError(error)}`)\n return false\n }\n const changed = mark.phase !== phase\n mark.phase = phase\n if (changed) markers.get(sessionId)?.announce()\n return true\n }\n\n /** Whether this session is standing still at the park point. */\n const parked = (sessionId: SessionId): boolean => {\n const activity = watch.activity(sessionId)\n if (activity.runningTools.length > 0) return false\n // A held request IS the safe point: the previous step drained and the loop\n // is waiting on this very gate. With no turn open there is no next request\n // to hold, so the session is standing still there too.\n return gate.isHeld(sessionId) || !activity.turnOpen\n }\n\n /**\n * The phase a durable record holds for one session right now, whether or not\n * this process holds it. A member whose session this host cannot read still\n * has a record, and that record is the only thing anyone knows about it.\n */\n const recordedPhase = (sessionId: SessionId): ParkPhase | undefined =>\n marks.get(sessionId)?.phase ?? registry.get(sessionId)?.phase\n\n /**\n * The state this session's header reports: the worst phase among the members\n * the session's own press registered, folded with the session's own.\n *\n * The control speaks for a tree, so it may not say \"safe to close\" while a\n * member of that tree is still draining, stalled, or not readable — the user\n * would close the lid on work this press started. A branch the walk could not\n * read may hold a member nobody can see, so an unread branch counts as\n * unfinished rather than as empty.\n * @param mark - the session's own mark, whose phase the fold starts from.\n * @returns the phase to publish and the goal opt-out that rides beside it.\n */\n const treeStatus = async (sessionId: SessionId, mark: ParkMark, signal?: AbortSignal): Promise<ParkStatus> => {\n const walk = await walkDescendants(ctx, sessionId, signal)\n const phases: ParkPhase[] = [mark.phase]\n for (const member of walk.members) {\n const phase = recordedPhase(member.id)\n if (phase !== undefined) phases.push(phase)\n }\n // An unread branch: unknown is not clean, and it is not a settled state\n // either, so it folds in as the phase a member would report between the\n // park point and the flush.\n for (let at = 0; at < walk.unread.length + (walk.unreadWhole ? 1 : 0); at += 1) phases.push('parking')\n\n const folded = foldTreePhases(phases)\n // The goal opt-out is the session's own record's, never a member's: the\n // switch beside this marker names the session it changes.\n const record = registry.get(sessionId)\n return {\n phase: folded.phase,\n safe: folded.safe,\n runningTools: mark.phase === 'draining' ? watch.activity(sessionId).runningTools : [],\n ...record === undefined ? {} : { rearmGoal: record.rearmGoal },\n }\n }\n\n /**\n * Ensure one paused session has its three verbs registered. Registration is\n * once per park: the status is answered by the command, not frozen into the\n * descriptor, so a phase change needs no re-registration to be readable — the\n * one {@link ParkMarkers.announce} performs is the announcement itself and\n * nothing more.\n */\n const publish = (sessionId: SessionId): void => {\n const mark = marks.get(sessionId)\n if (mark === undefined || markers.has(sessionId)) return\n const agent = ctx.agents.get(sessionId)\n if (agent === undefined) return\n // The record is read per status call rather than frozen into the mark: both\n // the goal opt-out and the members of the tree the press registered are\n // durable state beside the phase, not part of this mark.\n const status = async (signal?: AbortSignal): Promise<string> => encodeParkStatus(await treeStatus(sessionId, mark, signal))\n const definition = (): CommandDefinition => ({\n definitionId: CommandDefinitionId(STATUS_DEFINITION_ID),\n name: STATUS_COMMAND,\n // This descriptor is only the marker the browser looks for; the state\n // it reports is derived per call, tree and all.\n description: 'Report this session\\'s pause state',\n handler: async (invocation) => ({ kind: 'success', text: await status(invocation.signal) }),\n })\n /** Re-register the status verb, which is the announcement every phase change rides. */\n let announce: (() => void) | undefined\n // The registry keys scoped definitions off the context a call is made\n // through, and Cordis refuses an undeclared service access, so the marker\n // goes through a `commands`-injected child of THIS agent's context: the\n // scope layer is that agent, two sessions never collide, and agent\n // disposal removes the marker on its own.\n const fiber = agent.ctx.inject(['commands'], (scope) => {\n scope.commands.register({\n definitionId: CommandDefinitionId(RESUME_DEFINITION_ID),\n name: RESUME_COMMAND,\n description: 'Resume this paused session and its subagents',\n handler: async (invocation) => (modelIssued(ctx)\n ? { kind: 'error', text: UNATTENDED_TEXT }\n : (await resumePath.releaseTree(invocation.agent.id, invocation.signal)).answer),\n })\n /** The status verb's current registration; {@link announce} replaces it. */\n let registered = scope.commands.register(definition())\n announce = () => {\n // Disposing and registering back is one unregistration and one\n // registration — the two `commands/change` events a transition costs,\n // microseconds apart, which the client's own window merges into one\n // read. The three verbs are the same set on either side of it.\n registered()\n registered = scope.commands.register(definition())\n }\n // This session's own goal opt-out. It is the one goal switch that needs no\n // cross-session target — the caller IS the session whose record changes —\n // so the decision list's \"only a row you were shown\" binding has nothing\n // to bind here. It changes durable state, so a model turn is refused\n // exactly as for every other park verb.\n scope.commands.register({\n definitionId: CommandDefinitionId(GOAL_DEFINITION_ID),\n name: GOAL_COMMAND,\n description: 'Turn this paused Session\\'s goal re-arm on or off',\n handler: async () => {\n if (modelIssued(ctx)) return { kind: 'error', text: UNATTENDED_TEXT }\n const record = registry.get(sessionId)\n if (record === undefined) return { kind: 'error', text: NOT_PARKED_TEXT }\n return await resumePath.setRearmGoal(sessionId, !record.rearmGoal)\n },\n })\n })\n markers.set(sessionId, { fiber, announce: () => { announce?.() } })\n }\n\n /**\n * Walk one session's mark as far forward as its current activity allows.\n *\n * The move is published by `settle`, once the write that makes `parking`\n * durable has resolved; until then the mark keeps answering with `draining`,\n * which is less than the truth rather than more than the record can back.\n * @param sessionId - the marked session to re-derive.\n * @returns whether this call moved the mark out of `draining` (and so owes a flush).\n */\n const advance = (sessionId: SessionId): boolean => {\n const mark = marks.get(sessionId)\n if (mark === undefined || mark.phase !== 'draining' || mark.advancing) return false\n // An adopted mark reports the outcome of the episode that ended with the\n // previous process; this process may hold the session, but it does not get\n // to rewrite that outcome.\n if (mark.origin === 'registry') return false\n if (!parked(sessionId)) return false\n mark.advancing = true\n return true\n }\n\n /**\n * Flush a parked session at the session level and publish what actually\n * happened.\n *\n * The flush is the promise that survives a crash: a handle's own flush would\n * leave the routed live buffer — the pending input — in memory. `safe` is\n * therefore reserved for a flush this process ran and that resolved; a\n * session that is no longer in the store cannot be flushed at all, so its\n * park stops at the phase that says the flush never happened.\n */\n const settle = async (sessionId: SessionId, attempt: number): Promise<void> => {\n const mark = marks.get(sessionId)\n if (mark === undefined || mark.generation !== attempt) return\n /** Whether this attempt still owns the mark: a cancel or a newer press replaces or drops it. */\n const owned = (): boolean => marks.get(sessionId) === mark && mark.generation === attempt\n const session = ctx.sessions.get(sessionId)\n try {\n if (session === undefined) {\n // Nothing live to flush. The park point was reached and nothing about\n // this session is in memory, but no session-level flush can be run for\n // it either — so this process cannot call it clean.\n await commit(sessionId, mark, 'parking')\n return\n }\n // \"Stopped, flush pending\" is the state a crash during this flush leaves\n // behind, and it must be on disk before the flush starts.\n await commit(sessionId, mark, 'parking')\n if (!owned()) return\n try {\n await ctx.sessions.flush(session)\n } catch (error: unknown) {\n if (!owned()) return\n ctx.logger.warn(`pause: flush failed for \"${sessionId}\": ${describeError(error)}`)\n await commit(sessionId, mark, 'flush-failed')\n return\n }\n if (!owned()) return\n await commit(sessionId, mark, 'safe')\n } finally {\n // A later look may retry a park whose phase write failed; a mark that got\n // past `draining` is not advanced again whatever happened here.\n if (marks.get(sessionId) === mark && mark.phase === 'draining') mark.advancing = false\n }\n }\n\n /** Look at one marked session and act on what it is doing. */\n const look = (sessionId: SessionId): void => {\n publish(sessionId)\n if (!advance(sessionId)) return\n const attempt = marks.get(sessionId)?.generation\n if (attempt !== undefined) void settle(sessionId, attempt)\n }\n\n /**\n * Press one session's pause and make it durable.\n *\n * The press IS a durable promise: a pause that cannot be recorded is taken\n * back here rather than left as one a restart silently forgets. A session a\n * pause already owns is left exactly where it is — including one adopted\n * from an earlier process, whose outcome is not this process's to re-derive.\n *\n * A session with nothing live is left alone entirely. It has no request to\n * hold and no session-level flush to run, so a record for it could only be\n * one of two lies: `safe`, a park that never happened but that the clean path\n * would silently resume, or a phase that never settles, which would keep\n * \"safe to close\" from ever appearing. Registering the running tree therefore\n * means registering the members a pause can actually hold and flush.\n * @param sessionId - the session to pause.\n * @param signal - cancellation for the resumability read and registry write.\n * @param knownResumable - whether the caller already proved the local route is\n * `continuable` from the parent catalog, avoiding a second Session read.\n * @returns what the press did; only `registered` created a new park.\n */\n const press = async (\n sessionId: SessionId,\n signal?: AbortSignal,\n knownResumable = false,\n ): Promise<PressResult> => {\n if (ctx.sessions.get(sessionId) === undefined) return 'not-live'\n if (!knownResumable && !await isResumableSession(ctx, sessionId, signal)) return 'not-resumable'\n if (!gate.pause(sessionId)) return 'already-paused'\n const mark: ParkMark = { phase: 'draining', generation: ++generation, origin: 'press', advancing: false }\n marks.set(sessionId, mark)\n if (await commit(sessionId, mark, 'draining')) {\n // Whatever this process could not hold earlier is moot now: the session\n // is live and the press owns it.\n unheld.delete(sessionId)\n return 'registered'\n }\n drop(sessionId)\n gate.resume(sessionId)\n return 'unrecordable'\n }\n\n /**\n * Press one session's pause and register its whole tree behind it.\n *\n * \"One session's work\" includes the subagents it delegated to, nested ones\n * included, and the user does not pause them one by one. So the press walks\n * the session's own catalog tree and gives every member the same durable\n * treatment the session gets: {@link press} registers what it can hold, and a\n * member whose record cannot be written is named here rather than counted as\n * paused.\n *\n * A tree the walk could not read is the one case where the press does less\n * than the tree: the session itself is still registered — the user pressed a\n * session, and its park is real — and the answer says the subagents were not\n * covered rather than counting a tree nobody read.\n * @param sessionId - the session whose tree the user pressed.\n * @returns which members are registered and which could not be.\n */\n const pressTree = async (sessionId: SessionId, signal?: AbortSignal): Promise<PressTreeOutcome> => {\n // Nothing live means nothing this press could hold; the check is also what\n // keeps a session disposed in the middle of a press on the path that\n // records the park point it reached.\n if (ctx.sessions.get(sessionId) === undefined) {\n return { members: 0, registered: 0, rootRegistered: false, unrecorded: [], unread: false }\n }\n const walk = await walkDescendants(ctx, sessionId, signal)\n // A tree this press could not read is not a reason to drop the session\n // itself: the user pressed a session, and its own park is still real. What\n // is refused is the claim about the tree, which is why the count stays at\n // the one member this press actually covers.\n if (walk.unreadWhole) {\n const pressed = await press(sessionId, signal)\n // The look is unconditional on purpose only after a mark exists: a\n // session that left the store during this press has a mark and cannot be\n // flushed, which is the one way its park still reaches a durable \"not\n // clean\" instead of a phase that never settles.\n if (pressed === 'registered') look(sessionId)\n globalPause.markUnrecorded(sessionId, pressed === 'unrecordable')\n return {\n members: pressed === 'registered' ? 1 : 0,\n registered: pressed === 'registered' ? 1 : 0,\n rootRegistered: pressed === 'registered',\n unrecorded: pressed === 'unrecordable' ? [sessionId] : [],\n unread: true,\n }\n }\n const registered: SessionId[] = []\n const unrecorded: SessionId[] = []\n const members = walk.members.filter(member => member.mode === 'continuable')\n for (const id of [sessionId, ...members.map(member => member.id)]) {\n // The press is per member on purpose: one member that cannot be recorded\n // is named while the rest keep the pause they got. A member on a\n // one-shot or external route is never registered: there is no later\n // resume that could honestly satisfy that promise.\n const pressed = await press(id, signal, id !== sessionId)\n if (pressed === 'registered') {\n registered.push(id)\n // A member this host has nothing live for has no mark to walk; one that\n // does is looked at now, exactly as the session's own press looked at\n // itself.\n look(id)\n } else if (pressed === 'unrecordable') {\n unrecorded.push(id)\n }\n // The aggregate owns the same fact, so a member the registry refused is\n // named by the whole-app report too. A skipped member is not a failed\n // promise and is not named as one.\n globalPause.markUnrecorded(id, pressed === 'unrecordable')\n }\n return {\n members: registered.length + unrecorded.length,\n registered: registered.length,\n rootRegistered: registered.includes(sessionId),\n unrecorded,\n unread: false,\n }\n }\n\n const sync = (sessionId: SessionId): void => {\n if (!gate.isPaused(sessionId)) {\n drop(sessionId)\n return\n }\n // A session paused before its agent existed (or one whose agent was\n // replaced) still owes the browser both commands.\n publish(sessionId)\n }\n\n /**\n * Take the pause back: the durable record goes first, so a process that dies\n * in between does not re-hold a session the user has already released. A\n * record that cannot be dropped leaves the session paused, which is the state\n * the registry still describes.\n */\n const release = async (sessionId: SessionId, text: string): Promise<CommandResult> => {\n if (!gate.isPaused(sessionId)) return { kind: 'success', text }\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { kind: 'error', text: UNRECORDED_TEXT }\n }\n gate.resume(sessionId)\n return { kind: 'success', text }\n }\n\n // The resume verb, the global resume verb, and the goal opt-out ride one\n // path. It is mounted here because the release order and the marked origin\n // are this control's own facts.\n const resumePath: ResumePath = installResumePath(ctx, gate, registry, options, {\n release,\n adopted: sessionId => marks.get(sessionId)?.origin === 'registry',\n })\n\n // The decision list reads the same durable registry and acts through the same\n // resume path, so a session is on exactly one of the two morning paths: the\n // silent one when its record says `safe`, this one otherwise.\n const triage: TriageListService = mountTriageList(ctx, registry, resumePath, options.maxListedLogEvents)\n\n /**\n * The one handler the four decision verbs share. The list is addressed by\n * session like every other command on this channel, so the row the user\n * decided on travels as the command's own input. The calling session is what\n * makes that row a target: the list acts only on rows it published to this\n * caller, and never on a model's command.\n */\n const decide = (action: TriageAction) => async (invocation: CommandInvocation): Promise<CommandResult> => {\n if (modelIssued(ctx)) return { kind: 'error', text: UNATTENDED_TEXT }\n const target = invocation.rawInput.trim()\n if (target.length === 0) return { kind: 'error', text: TRIAGE_NOT_LISTED_TEXT }\n return await triage.decide(invocation.agent.id, SessionId(target), action, invocation.signal)\n }\n\n // The whole-app park reads the marks through this one accessor, so the\n // aggregate and the per-session control can never describe different states.\n const globalPause: GlobalPause = mountGlobalPause(ctx, watch, registry, {\n isPaused: sessionId => gate.isPaused(sessionId),\n press,\n look,\n live: () => new Map<SessionId, LivePark>([...marks].map(([sessionId, mark]) => [sessionId, {\n phase: mark.phase,\n // An adopted mark reports the outcome of the episode that ended with the\n // previous process, and a failed flush is already final: neither walks\n // any further here. Neither does a session that left the store — there is\n // no flush left to run for it, so it will not finish in this process.\n moving: mark.origin === 'press' && mark.phase !== 'flush-failed' && ctx.sessions.get(sessionId) !== undefined,\n }])),\n unheld: () => unheld,\n })\n\n // One effect owns the whole surface: unloading removes the pause verb, the\n // resume verbs, the subscriptions, and every marker this plugin registered.\n ctx.effect(function* () {\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(PAUSE_DEFINITION_ID),\n name: PAUSE_COMMAND,\n description: 'Pause this Session and its subagents before their next model request',\n handler: async (invocation) => {\n if (modelIssued(ctx)) return { kind: 'error', text: UNATTENDED_TEXT }\n const sessionId = invocation.agent.id\n const pressed = await pressTree(sessionId, invocation.signal)\n if (pressed.unrecorded.length > 0) {\n return { kind: 'error', text: unrecordedTreeText(pressed.unrecorded) }\n }\n if (!pressed.rootRegistered) {\n return { kind: 'error', text: NOT_RESUMABLE_PARK_TEXT }\n }\n const agents = pressed.rootRegistered ? pressed.registered - 1 : pressed.registered\n // A tree this press could not read leaves the session itself parked and\n // its subagents unaccounted for; the answer says exactly that much.\n if (pressed.unread) return { kind: 'success', text: `${pausedText(agents)} ${TREE_UNREAD_TEXT}` }\n // The press returns now: the drain happens while the user stays free to\n // walk away or press again to cancel it. The count names the whole\n // tree, so the answer cannot be mistaken for stopping one thing.\n return { kind: 'success', text: pausedText(agents) }\n },\n })\n // Registered globally, so the morning path exists on any session the user\n // has open — the sessions it resumes have no live agent to carry a command\n // of their own yet.\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(RESUME_ALL_DEFINITION_ID),\n name: RESUME_ALL_COMMAND,\n description: 'Resume every paused session',\n handler: async (invocation) => (modelIssued(ctx)\n ? { kind: 'error', text: UNATTENDED_TEXT }\n : { kind: 'success', text: summarizeResume(await resumePath.resumeParked(invocation.signal)) }),\n })\n // The one-press whole-app park. Registered globally for the same reason:\n // the sessions it registers are the ones the user is not looking at.\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(PAUSE_ALL_DEFINITION_ID),\n name: PAUSE_ALL_COMMAND,\n description: 'Pause every running Session and its subagents',\n handler: async (invocation) => (modelIssued(ctx)\n ? { kind: 'error', text: UNATTENDED_TEXT }\n : globalPause.pressAll(invocation.signal)),\n })\n // The aggregate read behind the whole-app control. Its result carries the\n // state as of this read, which is what makes a moving park observable at\n // all — a descriptor cannot change after registration. A read changes no\n // state, so it is open to any caller.\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(STATE_ALL_DEFINITION_ID),\n name: STATE_ALL_COMMAND,\n description: 'Report whether every session is paused',\n handler: async (invocation) => globalPause.readStatus(invocation.signal),\n })\n // The morning decision list, and the four decisions it offers. All five are\n // registered globally for the same reason the whole-app verbs are: the\n // sessions they act on are the ones with no live agent to carry a command.\n // The decisions take the listed session as their input, because the command\n // channel is addressed by the session the user is typing on; the read\n // records what it showed that session, which is what the decisions act on.\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_DEFINITION_ID),\n name: TRIAGE_COMMAND,\n description: 'List sessions that did not pause completely',\n handler: async (invocation) => ({ kind: 'success', text: encodeTriageList(await triage.read(invocation.agent.id, invocation.signal)) }),\n })\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_CONTINUE_DEFINITION_ID),\n name: TRIAGE_CONTINUE_COMMAND,\n description: 'Resume a listed session: <session id>',\n handler: decide('continue'),\n })\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_NOTE_DEFINITION_ID),\n name: TRIAGE_NOTE_COMMAND,\n description: 'Resume a listed session with a note that its tool result is unknown: <session id>',\n handler: decide('note'),\n })\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_REVIEW_DEFINITION_ID),\n name: TRIAGE_REVIEW_COMMAND,\n description: 'Open a listed session without running it: <session id>',\n handler: decide('review'),\n })\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_LEAVE_DEFINITION_ID),\n name: TRIAGE_LEAVE_COMMAND,\n description: 'Keep a listed session paused: <session id>',\n handler: decide('leave'),\n })\n // The one listed verb that is not a resume: it flips whether the next\n // resume of that session may re-open its goal, which is the only thing a\n // resume spends without the user watching it.\n yield ctx.commands.register({\n definitionId: CommandDefinitionId(TRIAGE_GOAL_DEFINITION_ID),\n name: TRIAGE_GOAL_COMMAND,\n description: 'Turn a listed Session\\'s goal re-arm on or off: <session id>',\n handler: decide('goal'),\n })\n yield gate.onChange(sessionId => { sync(sessionId) })\n // A request arriving at (or leaving) the gate is the moment a session can\n // become parked. The notification lands inside the request path, so the\n // look is deferred to a clean stack.\n yield gate.onHeld(sessionId => {\n queueMicrotask(() => { look(sessionId) })\n })\n yield watch.onChange(sessionId => { look(sessionId) })\n // An agent created after its session was paused still owes the browser the\n // marker: a session keeps its pause across an agent replacement.\n yield ctx.on('agent/created', ({ agent }) => {\n sync(agent.id)\n return undefined\n })\n // The agent's own teardown already removed the marker registration; forget\n // the stale handle so a replacement agent can register its own.\n yield ctx.on('agent/disposed', ({ agent }) => {\n markers.delete(agent.id)\n return undefined\n })\n yield async () => {\n const pending = [...markers.values()].map(marker => marker.fiber.dispose())\n markers.clear()\n marks.clear()\n await Promise.all(pending)\n }\n }, 'pause: session control')\n\n return {\n adopt: async () => {\n for (const entry of registry.list()) {\n // Every registered session this host can read is held again, whether or\n // not anything is live: the request that wakes it is the one this gate\n // must catch, and the wake-up may come from a scheduled task or a\n // webhook long after this mount. A record whose session cannot be read\n // is not held — the id would be pinned with nothing behind it and no\n // verb could ever clear it — and is reported instead.\n if (!await sessionReadable(ctx, entry.sessionId)) {\n unheld.add(entry.sessionId)\n ctx.logger.warn(`pause: the pause recorded for \"${entry.sessionId}\" is not held:`\n + ' this host cannot read that session')\n continue\n }\n if (gate.pause(entry.sessionId)) {\n marks.set(entry.sessionId, {\n phase: entry.phase,\n generation: ++generation,\n origin: 'registry',\n advancing: false,\n })\n }\n publish(entry.sessionId)\n }\n // A session that was already live when this instance mounted still owes\n // the browser the marker: the registry holds the list, and nothing else\n // re-publishes it for agents that predate this mount.\n for (const agent of ctx.agents.list()) sync(agent.id)\n },\n }\n}\n","/**\n * The pause gate: one session's hold at the model-request waterfall.\n *\n * The hold sits on the `llm/stream` waterfall, which is the only point that\n * satisfies all four conditions at once: the previous step's tool calls have\n * drained, the request is already built, the session's pending input is\n * committed, and — because nothing pulls the adapter — no timer is armed\n * anywhere. Machine sleep, network loss, and the adapter's stream-idle\n * watchdog therefore cannot touch a parked request.\n * @module dsh-pause-session/gate\n */\n\nimport type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n\n/** Message of the failure a held request ends with when its plugin unloads first. */\nexport const PLUGIN_UNLOADED = 'pause: plugin lifetime ended while a request was held'\n\n/** One latch a paused request waits on. */\ninterface Latch {\n readonly promise: Promise<void>\n readonly open: () => void\n}\n\n/** One paused session and the latch every request of that session waits on. */\ninterface Pause {\n readonly sessionId: SessionId\n readonly released: Promise<void>\n release(): void\n /**\n * Requests of this session waiting at the gate right now. Membership in\n * `paused` says the session is marked; this count says a request has\n * actually arrived and stopped, which is a different fact — the one the\n * park state is built from.\n */\n holding: number\n}\n\n/** The abort signal's own error, or a stable one when the abort carried no reason. */\nfunction abortError(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('pause: request aborted while held')\n}\n\n/** A latch without `Promise.withResolvers`, which this project's ES2022 lib predates. */\nfunction latch(): Latch {\n let open: () => void = () => {}\n const promise = new Promise<void>((resolve) => { open = resolve })\n return { promise, open }\n}\n\n/**\n * Resolve once the session is released or any of `signals` aborts. No timer is\n * armed: the wait ends only by release or by an abort, so a parked request\n * cannot be woken by the clock. Listeners are removed once the wait settles.\n */\nfunction raceRelease(released: Promise<void>, signals: readonly AbortSignal[]): Promise<void> {\n const wake = latch()\n const onAbort = (): void => { wake.open() }\n const watching = signals.filter(signal => !signal.aborted)\n if (watching.length !== signals.length) wake.open()\n for (const signal of watching) signal.addEventListener('abort', onAbort, { once: true })\n return Promise.race([released, wake.promise]).finally(() => {\n for (const signal of watching) signal.removeEventListener('abort', onAbort)\n })\n}\n\n/** Options of {@link PauseGate}. */\nexport interface PauseGateOptions {\n /** Aborted when the owning plugin unloads; a held request must never delegate after that. */\n readonly lifetime: AbortSignal\n}\n\n/** Per-session pause state and the waterfall listener that enforces it. */\nexport class PauseGate {\n /** Paused sessions; membership IS the pause state. */\n private readonly paused = new Map<SessionId, Pause>()\n private readonly listeners = new Set<(sessionId: SessionId, paused: boolean) => void>()\n private readonly heldListeners = new Set<(sessionId: SessionId, held: boolean) => void>()\n\n /**\n * @param options - the plugin-lifetime signal every held request races.\n */\n constructor(private readonly options: PauseGateOptions) {}\n\n /**\n * Subscribe to pause-state changes.\n * @param listener - called with the session whose state changed and its new state.\n * @returns the unsubscribe function.\n */\n onChange(listener: (sessionId: SessionId, paused: boolean) => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /**\n * Subscribe to \"a request arrived at / left the gate\" for a paused session.\n *\n * This is the moment a session can become parked, so it is also the moment a\n * park observer has to look. Listeners run synchronously inside the request\n * path: they must only record that a look is owed.\n * @param listener - called with the session and whether it now holds a request.\n * @returns the unsubscribe function.\n */\n onHeld(listener: (sessionId: SessionId, held: boolean) => void): () => void {\n this.heldListeners.add(listener)\n return () => { this.heldListeners.delete(listener) }\n }\n\n /**\n * @param sessionId - session to inspect.\n * @returns whether the session is currently paused.\n */\n isPaused(sessionId: SessionId): boolean {\n return this.paused.has(sessionId)\n }\n\n /**\n * @param sessionId - session to inspect.\n * @returns whether a model request of that session is waiting at the gate\n * right now. False for a session that is paused but whose loop has not built\n * its next request yet — a running tool, for instance, is still draining.\n */\n isHeld(sessionId: SessionId): boolean {\n return (this.paused.get(sessionId)?.holding ?? 0) > 0\n }\n\n /**\n * Enter one request that is about to wait. The count rises synchronously with\n * {@link hold}, so an observer notified at that moment reads `isHeld` as true.\n * @param pause - the session's pause record.\n * @returns the exit that lowers the count again.\n */\n private enter(pause: Pause): () => void {\n pause.holding += 1\n this.notifyHeld(pause.sessionId, true)\n return () => {\n pause.holding -= 1\n if (pause.holding === 0) this.notifyHeld(pause.sessionId, false)\n }\n }\n\n /**\n * Pause one session: its next model request is held until {@link resume}. A\n * request already past the waterfall keeps running — pausing never\n * interrupts a model call or a tool call that has started.\n * @param sessionId - session to pause.\n * @returns whether this call changed the state.\n */\n pause(sessionId: SessionId): boolean {\n if (this.paused.has(sessionId)) return false\n const release = latch()\n this.paused.set(sessionId, { sessionId, released: release.promise, release: release.open, holding: 0 })\n this.notify(sessionId, true)\n return true\n }\n\n /**\n * Resume one session: release everything it holds and stop holding what\n * follows. The requests sent from then on are the ones built before the\n * hold, so a resumed session continues from the point it stopped.\n * @param sessionId - session to resume.\n * @returns whether this call changed the state.\n */\n resume(sessionId: SessionId): boolean {\n const pause = this.paused.get(sessionId)\n if (pause === undefined) return false\n this.paused.delete(sessionId)\n pause.release()\n this.notify(sessionId, false)\n return true\n }\n\n /**\n * The wait one model request must finish before it may delegate, and the exit\n * that reports the request as no longer waiting.\n * @param request - the request reaching the waterfall.\n * @returns the wait plus its release, or `undefined` when this gate does not\n * hold that request.\n */\n hold(request: GenerateOptions): { readonly wait: Promise<void>; readonly left: () => void } | undefined {\n // Context compaction and session titling are background model work inside\n // the same session and carry a purpose; a session pause must never freeze\n // them. Only a request the loop built for its own turn is gateable.\n if (request.purpose !== undefined) return undefined\n const sessionId = request.sessionId\n if (sessionId === undefined) return undefined\n const pause = this.paused.get(sessionId)\n if (pause === undefined) return undefined\n const left = this.enter(pause)\n const wait = raceRelease(\n pause.released,\n request.signal === undefined ? [this.options.lifetime] : [request.signal, this.options.lifetime],\n )\n return { wait, left }\n }\n\n /**\n * The `llm/stream` listener.\n *\n * `ctx.waterfall` composes listeners synchronously, so this returns an async\n * iterator synchronously — an `async` listener would hand the loop a promise\n * where it iterates a stream. One generator body reaches `next()` at most\n * once, which is what makes delegation single-shot per HELD request; a flag\n * shared across requests would also short-circuit everything issued after\n * the release.\n * @param request - the full request, already built and frozen by the loop.\n * @param next - the downstream waterfall chain.\n * @returns the stream, delayed until this request may delegate.\n */\n readonly stream = (request: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk> => {\n const gate = this\n return (async function* gated(): AsyncIterable<StreamChunk> {\n const held = gate.hold(request)\n try {\n if (held !== undefined) await held.wait\n // The race decides when to stop waiting; the signals decide whether\n // delegating is allowed at this exact moment. A request whose phase\n // aborted, or whose plugin unloaded, must never reach the adapter.\n if (request.signal?.aborted === true) throw abortError(request.signal)\n if (gate.options.lifetime.aborted) throw new Error(PLUGIN_UNLOADED)\n yield* next()\n } finally {\n // Leaving is what tells an observer the session stopped standing at\n // the park point — whether it delegated or aborted.\n held?.left()\n }\n })()\n }\n\n private notify(sessionId: SessionId, paused: boolean): void {\n for (const listener of [...this.listeners]) listener(sessionId, paused)\n }\n\n private notifyHeld(sessionId: SessionId, held: boolean): void {\n for (const listener of [...this.heldListeners]) listener(sessionId, held)\n }\n}\n","/**\n * What a session is doing between \"pause was asked for\" and \"its next model\n * request is standing still at the gate\".\n *\n * Two facts decide that, and each has its own owner:\n *\n * - Whether a turn is open is derived state the harness already maintains:\n * the agent registry owns the `turnBoundary` session projection, which folds\n * `turn/start`, `turn/end`, `step/start`, and `step/end` into\n * `openTurnStartSeq`. Reading it through `ctx.agents` is a synchronous\n * projection read, not a scan of the event log (synchronous history reads are\n * prohibited for new code).\n * - Which tool is in flight is a log-derived per-session fact, so it is a\n * session projection too. The projection folds each committed\n * `tool/call` → `tool/result` pairing and keeps the calls in order; the\n * framework owns replay, caching, and disposal. This module listens only to\n * committed events so it can wake the control, never to keep a second copy\n * of the state itself.\n * @module dsh-pause-session/park\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Session, SessionId } from '@deepseek-ai/dsh-session'\nimport type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'\nimport { z } from 'zod'\n// Type-only: pulls the agent registry's Context merge (ctx.agents).\nimport type {} from '@deepseek-ai/dsh-agent'\n\n/** One tool call the session has committed but not yet answered. */\nconst openCallSchema = z.object({\n /** The provider-issued id pairing the call with the result that has not arrived. */\n callId: z.string(),\n /** The tool's own name, as the model called it. */\n name: z.string(),\n})\n\n/** The host-only projection state: open calls in call order. */\ntype OpenToolCall = z.infer<typeof openCallSchema>\n\n/**\n * The host-only fold behind {@link ParkWatch}. It is registered on the\n * plugin's fiber; unload removes it with every other registration.\n */\nconst parkActivityProjection: ProjectionDefinition<'dsh-pause-session/activity', OpenToolCall[]> = {\n key: 'dsh-pause-session/activity',\n stateSchema: z.array(openCallSchema),\n init: () => [],\n apply: (state, event) => {\n if (event.type === 'tool/call') {\n return [...state, { callId: String(event.data.callId), name: event.data.name }]\n }\n if (event.type === 'tool/result') {\n const callId = String(event.data.message.toolCallId)\n const at = state.findIndex(call => call.callId === callId)\n if (at < 0) return state\n return [...state.slice(0, at), ...state.slice(at + 1)]\n }\n return state\n },\n stateVersion: 1,\n}\n\n/** One session's current activity, as the control reports it. */\nexport interface ParkActivity {\n /** Whether the session is inside a turn — including one waiting for this very park. */\n readonly turnOpen: boolean\n /** Names of the calls currently in flight, in call order; empty when no tool is running. */\n readonly runningTools: readonly string[]\n}\n\n/** Both halves of the activity question for one session. */\nexport interface ParkWatch {\n /**\n * @param sessionId - session to inspect.\n * @returns the session's current turn and tool activity.\n */\n activity(sessionId: SessionId): ParkActivity\n /**\n * Subscribe to activity changes.\n * @param listener - called with the session whose activity changed; it must\n * only record that a look is owed, never re-enter the session.\n * @returns the unsubscribe function.\n */\n onChange(listener: (sessionId: SessionId) => void): () => void\n}\n\n/** Whether the agent registry's `turnBoundary` fold reports an open turn. */\nfunction turnOpen(ctx: Context, session: Session): boolean {\n return ctx.agents.turnBoundaryOf(session).openTurnStartSeq !== null\n}\n\n/**\n * Mount the per-session activity projection and its notification edge.\n * @param ctx - host context carrying the session store and the projection registry.\n * @returns the watch the park state is derived from.\n */\nexport async function mountParkWatch(ctx: Context): Promise<ParkWatch> {\n const activity = await ctx.sessionProjections.register(parkActivityProjection)\n const listeners = new Set<(sessionId: SessionId) => void>()\n\n const notify = (sessionId: SessionId): void => {\n for (const listener of [...listeners]) listener(sessionId)\n }\n\n ctx.effect(() => ctx.on('session/event', (session, event) => {\n switch (event.type) {\n case 'tool/call':\n case 'tool/result':\n // The projection owns the open-call list; the committed event only\n // says the control owes a look at it.\n notify(session.id)\n return\n case 'turn/start':\n case 'turn/end':\n // Turn state itself is read from the projection, but a turn boundary is\n // the other half of \"is this session standing still\": a turn that ends\n // with no further request can never produce the hold a park would wait\n // for, so the park predicate has to look again here.\n notify(session.id)\n return\n default:\n return\n }\n }), 'pause: session activity changes')\n\n return {\n activity: (sessionId) => {\n const session = ctx.sessions.get(sessionId)\n if (session === undefined) {\n // A session with no live log cannot be mid-turn; the park predicate\n // owns what that means.\n return { turnOpen: false, runningTools: [] }\n }\n return {\n turnOpen: turnOpen(ctx, session),\n runningTools: activity.stateOf(session).map(call => call.name),\n }\n },\n onChange: (listener) => {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n }\n}\n","/**\n * The durable park registry: which sessions are paused, and how cleanly each\n * one stopped.\n *\n * The registry lives in the profile's storage — `ctx.storageDomain`, the\n * schema-validated domain data form over whichever backend the composition\n * routes it to — and never in the session log: a pause is not part of a\n * session's history, and the list has to be readable by a process that has not\n * opened any session yet. The domain's own backend route is configuration, so\n * this module names no path and performs no IO of its own.\n *\n * The domain keeps the DEFAULT whole-unit layout on purpose. The per-record\n * layout reads a malformed or stale record document as an absent record, which\n * here would silently drop one paused session; a whole-unit file turns any\n * corruption into a failed `open`, and a failed open fails the plugin's mount\n * loudly instead of resuming everything.\n * @module dsh-pause-session/registry\n */\n\nimport { z } from 'zod'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\nimport { defineDomain, domainTable, DomainError } from '@deepseek-ai/dsh-storage-domain'\nimport type { Domain } from '@deepseek-ai/dsh-storage-domain'\nimport { PARK_PHASES } from './shared.js'\nimport type { ParkPhase } from './shared.js'\n\n/** Domain and backend-unit name; one document per profile under the storage root. */\nexport const PARK_REGISTRY_DOMAIN = 'pause_registry'\n\n/** One session's stored park outcome. */\nexport const parkRecord = z.object({\n /** How far that session got before its process ended; only `safe` is clean. */\n phase: z.enum(PARK_PHASES),\n /** When this record was last written — a phase change, or a goal opt-out flip. */\n updatedAt: z.number().int().nonnegative(),\n /**\n * Whether resuming this session may re-arm its goal. Re-arming is opt-OUT\n * because a re-armed goal continues on its own and spends the owner's money;\n * a record written before this field existed reads as `true`, which is the\n * product default (a goal session comes back running its rounds).\n *\n * Every phase write of one park episode folds through the stored record, so\n * the opt-out a user sets while the session is still draining survives the\n * `parking` and `safe` writes that follow it.\n */\n rearmGoal: z.boolean().default(true),\n})\n\n/** One session's stored park outcome. */\nexport type ParkRecord = z.infer<typeof parkRecord>\n\n/**\n * The registry declaration. `version: 1` and no `compatibleVersions`: the\n * domain layer rejects a stored unit stamped with any other version rather than\n * migrating it, so an unrecognized registry fails loud at open. Validation is\n * the rejecting default for the same reason — a record this build cannot read\n * must stop the mount, not disappear from the list.\n */\nexport const parkRegistryDomain = defineDomain({\n name: PARK_REGISTRY_DOMAIN,\n version: 1,\n tables: { sessions: domainTable<SessionId, ParkRecord>(parkRecord) },\n})\n\n/** One session the registry remembers. */\nexport interface ParkedSession {\n readonly sessionId: SessionId\n readonly phase: ParkPhase\n /** When the phase was written. */\n readonly updatedAt: number\n /** Whether resuming this session may re-arm its goal; see {@link parkRecord}. */\n readonly rearmGoal: boolean\n}\n\n/** The durable pause list, opened over the profile's storage. */\nexport interface ParkRegistry {\n /**\n * @returns every remembered session, in insertion order.\n */\n list(): readonly ParkedSession[]\n /**\n * The same list as {@link list}, as a lazily kept read-only view.\n *\n * The whole-app aggregate and the decision list read this once per status\n * read, and a status read can happen on every command-registry change; the\n * view is rebuilt only when the stored entries actually changed, so a read\n * that changes nothing does not build a second copy of the list to look at.\n * A caller must not mutate it.\n */\n readonly parked: ReadonlyMap<SessionId, ParkedSession>\n /**\n * @param sessionId - session to look up.\n * @returns the remembered session, or `undefined` when nothing is parked under that id.\n */\n get(sessionId: SessionId): ParkedSession | undefined\n /**\n * Write one session's outcome; resolves once it is durable. The goal opt-out\n * already recorded for that session survives a phase change — the phases of\n * one park episode all describe the same parked session.\n * @param sessionId - the session being parked.\n * @param phase - how far the park has got.\n */\n record(sessionId: SessionId, phase: ParkPhase): Promise<void>\n /**\n * Set one session's goal opt-out; resolves once it is durable.\n * @param sessionId - the parked session to change.\n * @param rearmGoal - whether the clean resume path may re-arm its goal.\n * @throws when no record exists for that session — the flag describes a\n * parked session, and inventing a record would invent a park.\n */\n setRearmGoal(sessionId: SessionId, rearmGoal: boolean): Promise<void>\n /**\n * Drop one session from the list, so a later process does not re-hold it.\n * @param sessionId - the session being released.\n */\n forget(sessionId: SessionId): Promise<void>\n}\n\n/**\n * Open the registry over the mounted storage. The caller owns the handle's\n * lifetime; this registers the close as an effect on `ctx`, so the unit is\n * released with the plugin.\n * @param ctx - plugin context carrying `storageDomain`.\n * @returns the open registry.\n * @throws whatever `open` rejects with — a `StorageError` for an unreadable\n * unit, a `DomainError('invalid-record')` for a record this build cannot read.\n */\nexport async function openParkRegistry(ctx: Context): Promise<ParkRegistry> {\n const domain: Domain<typeof parkRegistryDomain> = await ctx.storageDomain.open(parkRegistryDomain)\n ctx.effect(() => () => domain.close(), 'pause: registry')\n const sessions = domain.table('sessions')\n /** The entries the cached view was built from; replaced whenever they differ. */\n let cachedFrom: readonly ParkedSession[] = []\n let cached: ReadonlyMap<SessionId, ParkedSession> | undefined\n const entryOf = (sessionId: SessionId): ParkedSession | undefined => {\n const record = sessions.get(sessionId)\n return record === undefined\n ? undefined\n : { sessionId, phase: record.phase, updatedAt: record.updatedAt, rearmGoal: record.rearmGoal }\n }\n const entriesNow = (): readonly ParkedSession[] => [...sessions.entries()].map(([sessionId, record]) => ({\n sessionId,\n phase: record.phase,\n updatedAt: record.updatedAt,\n rearmGoal: record.rearmGoal,\n }))\n return {\n list: entriesNow,\n get parked() {\n const entries = entriesNow()\n // Same length and same records: the view the last read built still\n // describes this list, so hand that one back rather than another copy.\n if (cached !== undefined && entries.length === cachedFrom.length\n && entries.every((entry, at) => entry === cachedFrom[at])) return cached\n cachedFrom = entries\n cached = new Map(entries.map(entry => [entry.sessionId, entry]))\n return cached\n },\n get: entryOf,\n record: async (sessionId, phase) => {\n const updatedAt = Date.now()\n // A record already there is folded through `update`, so the phase write\n // and the record's own goal opt-out are decided in the backend's job\n // order rather than from a read taken before this write was queued.\n if (sessions.get(sessionId) !== undefined) {\n try {\n await sessions.update(sessionId, current => ({ ...current, phase, updatedAt }))\n return\n } catch (error: unknown) {\n // Released between that read and this job: fall through and start the\n // record again, which is what the park phase is asking for.\n if (!(error instanceof DomainError) || error.code !== 'missing-key') throw error\n }\n }\n // A fresh object per write: domain records are the stored objects and are\n // never mutated in place.\n await sessions.put(sessionId, { phase, updatedAt, rearmGoal: true })\n },\n setRearmGoal: async (sessionId, rearmGoal) => {\n // `update` decides existence and reads the current record inside the\n // backend's own job order, so a phase write queued beside it cannot be\n // clobbered by a stale read.\n await sessions.update(sessionId, current => ({ ...current, rearmGoal, updatedAt: Date.now() }))\n },\n forget: async (sessionId) => {\n await sessions.delete(sessionId)\n },\n }\n}\n","/**\n * Host half of dsh-pause-session.\n *\n * Mounts the pause gate — the per-session hold at the model-request waterfall —\n * the activity fold the park predicate reads, the durable park registry, the\n * session control the browser half drives, and the clean resume path that\n * answers the input a parked session left pending.\n *\n * The registry is read before anything is gated, so a session a previous\n * process left parked is held again by this one without any user action; a\n * registry this build cannot read fails the mount loudly rather than silently\n * resuming every session it named.\n * @module dsh-pause-session\n */\n\nimport { z } from 'zod'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { TRIAGE_LOG_MAX_EVENTS, TRIAGE_WINDOW_EVENTS } from './coldlog.js'\nimport { installSessionControl } from './control.js'\nimport { PauseGate } from './gate.js'\nimport { mountParkWatch } from './park.js'\nimport { openParkRegistry } from './registry.js'\n\n/** Plugin name; the Loader row and the bundle patch both use it. */\nexport const name = 'dsh-pause-session'\n\n/**\n * Required host services: the model runtime to gate, the stores the control\n * needs, the projection registry the park predicate reads turn state from, and\n * the profile's storage, which holds the pause registry.\n */\nexport const inject = ['llm', 'commands', 'agents', 'sessions', 'sessionProjections', 'storageDomain']\n\n/**\n * Provider routes whose request conversion drops an empty user message, so the\n * clean resume path's wake input never reaches the model on them.\n *\n * The list is deployment knowledge, not a constant this plugin may assume:\n * whether an empty user message survives is decided by the adapter (and, for\n * the pi-ai family, by the provider API that adapter is configured for), and a\n * plugin cannot introspect an adapter. The default names the routes the shipped\n * DeepSeek adapter registers; a profile that routes a session elsewhere gets\n * the visible marker instead of a silent empty turn until it declares that\n * route here.\n */\nexport const DEFAULT_SILENT_WAKE_ROUTES = ['deepseek-official', 'deepseek-account'] as const\n\n/** Validated plugin configuration. */\nconst ConfigShape = z.object({\n /** Provider routes known to drop an empty user message; see {@link DEFAULT_SILENT_WAKE_ROUTES}. */\n silentWakeRoutes: z.array(z.string()).default([...DEFAULT_SILENT_WAKE_ROUTES]),\n /**\n * How many events of a parked session's stored log the morning list folds per\n * row, counted back from the end. The list reads a session's stop position\n * and its unknown tool outcomes, both facts about the log's end, so this\n * bounds what a panel left open costs without changing what a row says about\n * a log that fits inside it. A row whose log needs more than this is reported\n * unreadable rather than guessed at; see `./coldlog.ts`.\n */\n maxListedLogEvents: z.number().int().min(1).max(TRIAGE_LOG_MAX_EVENTS).default(TRIAGE_WINDOW_EVENTS),\n})\n\n/** Validated plugin configuration. */\nexport type Config = z.infer<typeof ConfigShape>\n\n/**\n * The Loader hands a row's config through this schema, and an empty row config\n * arrives as `undefined`; folding it to `{}` first is what lets the field\n * defaults above apply instead of failing the mount.\n */\nexport const Config = z.preprocess(value => value ?? {}, ConfigShape)\n\n/**\n * Mount the durable pause registry, the pause gate, the activity fold, their\n * session control, and the clean resume path.\n *\n * The registry opens first on purpose: if it cannot be read, nothing else is\n * registered, so a failed mount leaves no half-installed control behind.\n * @param ctx - the plugin's host context.\n * @param config - validated route knowledge for the resume path's wake content.\n */\nexport async function apply(ctx: Context, config: Config = Config.parse({})): Promise<void> {\n const registry = await openParkRegistry(ctx)\n const lifetime = new AbortController()\n // The gate races this signal, so unloading releases every held request\n // instead of stranding a generator no one can reach again.\n ctx.effect(() => () => { lifetime.abort(new Error('pause: plugin unloaded')) }, 'pause: lifetime')\n const gate = new PauseGate({ lifetime: lifetime.signal })\n ctx.effect(\n () => ctx.on('llm/stream', (request, next) => gate.stream(request, next)),\n 'pause: pause gate',\n )\n const watch = await mountParkWatch(ctx)\n const control = installSessionControl(ctx, gate, watch, registry, {\n silentWakeRoutes: config.silentWakeRoutes,\n maxListedLogEvents: config.maxListedLogEvents,\n })\n await control.adopt()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,UAAU,UAA4B;CACpD,KAAK,IAAI,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC5D,MAAM,UAAU,SAAS;EAEzB,IAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,YAAY,QAAQ,SAAS,aAAa;EACxF,OAAO,QAAQ,SAAS;CAC1B;CACA,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,eACd,QACA,aAAgD;CAEhD,MAAM,SAAS,CAAC,GAAG,QAAQ,GAAG,uBAAuB,MAAM,CAAC;CAC5D,MAAM,OAAO,YAAY,QAAQ,WAAW;CAC5C,OAAO,KAAK,MAAM,SAAS,QAAO;EAChC,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GAAW,OAAO,CAAA;EAChC,MAAM,UAAU,mBAAmB,OAAO,KAAK,iBAAiB;EAChE,OAAO,YAAY,OAAO,CAAA,IAAK,CAAC,OAAO;CACzC,CAAC;AACH;;;;;;;;AASA,SAAgB,WAAW,QAA+B;CACxD,MAAM,SAAS,kBAAkB,MAAM,CAAC,EAAE;CAC1C,OAAO,WAAW,KAAA,KAAa,OAAO,SAAS,SAAS,KAAK,OAAO,MAAM,SAAS;AACrF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClDA,MAAa,gBAAgB;;AAG7B,MAAa,iBAAiB;;AAG9B,MAAa,qBAAqB;;AAGlC,MAAa,oBAAoB;;AAGjC,MAAa,oBAAoB;;AAGjC,MAAa,iBAAiB;;AAG9B,MAAa,eAAe;;AAG5B,MAAa,iBAAiB;;AAG9B,MAAa,0BAA0B;;AAGvC,MAAa,sBAAsB;;AAGnC,MAAa,wBAAwB;;AAGrC,MAAa,uBAAuB;;AAGpC,MAAa,sBAAsB;;AAGnC,MAAa,sBAAsB;;AAGnC,MAAa,uBAAuB;;AAGpC,MAAa,2BAA2B;;AAGxC,MAAa,0BAA0B;;AAGvC,MAAa,0BAA0B;;AAGvC,MAAa,uBAAuB;;AAGpC,MAAa,qBAAqB;;AAGlC,MAAa,uBAAuB;;AAGpC,MAAa,gCAAgC;;AAG7C,MAAa,4BAA4B;;AAGzC,MAAa,8BAA8B;;AAG3C,MAAa,6BAA6B;;AAG1C,MAAa,4BAA4B;;AAGzC,MAAa,gBAAgB;;;;;;AAO7B,MAAM,cAAc;;;;;;;;;AAUpB,SAAS,YAAY,MAAY;CAC/B,MAAM,UAAU,KAAK,QAAQ,eAAc,cAAa,cAAc,OAAO,QAAQ,KAAK,WAAW;CACrG,OAAO,QAAQ,WAAW,WAAW,IAAI,KAAK,YAAY;AAC5D;;;;;;AAyDA,SAAgB,cAAc,OAAc;CAC1C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;;;;;;;;;;;;;;AAeA,MAAa,cAAc;CAAC;CAAY;CAAW;CAAQ;AAAc;;;;;;;;;AAazE,MAAM,iBAA4C;CAChD,MAAM;CACN,UAAU;CACV,SAAS;CACT,gBAAgB;;;;;;;;;;;;;AAclB,SAAgB,eAAe,QAA2B;CACxD,IAAI,QAAmB;CACvB,KAAK,MAAM,SAAS,QAClB,IAAI,eAAe,SAAS,eAAe,QAAQ,QAAQ;CAE7D,OAAO;EAAE,OAAO;EAAO,MAAM,UAAU;CAAM;AAC/C;;AAGA,MAAM,aAAwC;CAC5C,UAAU;CACV,SAAS;CACT,MAAM;CACN,gBAAgB;;;;;;AAOlB,MAAM,UAAU;AAChB,MAAM,WAAW;;;;;;AAgCjB,SAAgB,iBAAiB,QAAkB;CAMjD,OAAO,GAAG,GALM,gBAAgB,WAAW,OAAO,WACpC,OAAO,aAAa,WAAW,IACzC,KACA,YAAY,OAAO,aAAa,IAAI,WAAW,CAAC,CAAC,KAAK,IAAI,MACjD,OAAO,cAAc,KAAA,IAAY,KAAK,WAAW,OAAO,YAAY,UAAU;AAE7F;;;;;AA6BA,MAAa,uBAAuB;;AAwEpC,SAAS,YAAY,OAAwB;CAC3C,OAAO,MAAM,WAAW,IAAI,cAAc,MAAM,IAAI,WAAW,CAAC,CAAC,KAAK,IAAI;AAC5E;;;;;;AAOA,SAAS,SAAS,IAAsB;CACtC,OAAO,OAAO,KAAA,IAAY,cAAc,YAAY,EAAE;AACxD;;;;;;;AA8BA,SAAgB,mBAAmB,QAAoB;CACrD,OAAO,GAAG,uBAAuB,OAAO,QAAA,gBACnB,OAAO,WAAA,UACb,OAAO,KAAA,eACF,SAAS,OAAO,QAAQ,EAAA,cACzB,YAAY,OAAO,QAAQ,EAAA,aAC5B,OAAO,QAAA,aACP,YAAY,OAAO,OAAO,EAAA,iBACtB,YAAY,OAAO,WAAW,EAAA,kBAC7B,YAAY,OAAO,WAAW,EAAA,UACtC,YAAY,OAAO,IAAI,EAAA,WACtB,YAAY,OAAO,KAAK;AAC1C;;;;;;AAgDA,MAAa,gBAAgB;;AA+E7B,MAAM,WAAW;;AAGjB,SAAS,aAAa,QAA8B;CAClD,IAAI,OAAO,WAAW,GAAG,OAAO;CAChC,OAAO,OACJ,KAAI,UAAS,GAAG,YAAY,MAAM,IAAI,EAAC,GAAI,YAAY,MAAM,MAAM,GAAG,CAAC,CACvE,KAAK,IAAI;AACd;;AAgBA,SAAS,WAAW,KAAc;CAChC,IAAI,IAAI,cAAc,KAAA,GAAW,OAAO;CACxC,MAAM,WAAW,IAAI,SAAS,KAAA,IAC1B,KACA,SAAS,OAAO,IAAI,IAAI,IAAI,IAAI,SAAS,KAAA,IAAY,KAAK,SAAS,OAAO,IAAI,IAAI;CACtF,OAAO,GAAG,YAAY,IAAI,SAAS,IAAI;AACzC;;;;;;AAOA,SAAgB,iBAAiB,MAAgB;CAC/C,MAAM,OAAO,GAAG,gBAAgB,OAAO,KAAK,KAAK,MAAM;CACvD,OAAO,KAAK,KAAK,QAAQ,MAAM,QAAO;EACpC,MAAM,UAAU,IAAI,YAAY,eAAe,eAAe,WAAW,GAAG;EAC5E,OAAO,GAAG,KAAI,SAAU,YAAY,IAAI,SAAS,EAAA,WACjC,IAAI,MAAA,aACF,QAAA,YACD,IAAI,YAAY,eAAe,cAAc,aAAa,IAAI,MAAM,EAAA,WACrE,IAAI,YAAY,gBAAgB,IAAI,OAAO,WAAW,IAAI,cAAc,YAAY,IAAI,OAAO,KAAI,UAAS,MAAM,IAAI,CAAC,EAAA,UACxH,IAAI,OAAO,WAAW,KAAA,UACtB,IAAI,WAAW,WAAW,KAAA,UAC1B,IAAI,YAAY,UAAU;CAC3C,GAAG,IAAI;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/jBA,MAAa,uBAAuB;;;;;;;;;;AAWpC,MAAa,wBAAwB;;;;;;;AAQrC,MAAM,qCAA0C,IAAI,IAAI;CAAC;CAAe;CAAgB;AAAqB,CAAC;;;;;;AA0B9G,SAAgB,WAAW,OAAmB;CAC5C,MAAM,OAAO,MAAM;CACnB,IAAI,OAAO,SAAS,YAAY,SAAS,MAAM,OAAO,CAAA;CACtD,MAAM,OAAO,UAAU,QAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;CAC3E,MAAM,OAAO,UAAU,QAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;CAC3E,OAAO;EAAE,GAAG,SAAS,KAAA,IAAY,CAAA,IAAK,EAAE,KAAI;EAAI,GAAG,SAAS,KAAA,IAAY,CAAA,IAAK,EAAE,KAAI;CAAE;AACvF;;;;;;;;;;;;;;;AAgBA,SAAS,gBAAgB,QAA+B;CACtD,MAAM,wBAAQ,IAAI,IAAG;CACrB,MAAM,2BAAW,IAAI,IAAG;CACxB,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,aAAa;GAC9B,MAAM,IAAI,OAAO,MAAM,KAAK,MAAM,GAAG,MAAM,KAAK,IAAI;GACpD;EACF;EACA,IAAI,MAAM,SAAS,eAAe;EAClC,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,UAAU,CAAC;CACpD;CACA,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,QAAQ,UAAU,SAAS,IAAI,MAAM,IAAI,CAAA,IAAK,CAAC;EAAE;EAAM;CAAM,CAAE,CAAC;AAC9F;;;;;;;;;;;;;;;AAgBA,SAAS,cAAc,QAAiC,QAA8B;CAGpF,IAAI,OAAO,SAAS,GAAG,OAAO;CAC9B,MAAM,aAAa,IAAI,IAAI,OAAO,KAAI,UAAS,CAAC,MAAM,KAAe,KAAK,CAAC,CAAC;CAC5E,MAAM,iBAA0C;EAC9C,KAAK,MAAM,OAAO,CAAC,GAAG,YAAY,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,QAAO,GAAI;GAC1D,MAAM,QAAQ,WAAW,IAAI,GAAa;GAC1C,IAAI,UAAU,KAAA,GAAW,OAAO;EAClC;CAEF;CACA,IAAI;CACJ,IAAI;EACF,SAAS,SAAQ;CACnB,QAAQ;EAIN,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG;GACjD,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,KAAA,KAAa,mBAAmB,KAAK,MAAM,MAAM;GAC/D,SAAS;GACT;EACF;CACF;CACA,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,UAAU,mBAAmB,MAAM;CACzC,IAAI,YAAY,MAAM,OAAO;CAE7B,IAAI,QAAQ,SAAS,YAAY,QAAQ,SAAS,aAAa,OAAO;CACtE,OAAO,QAAQ,SAAS;AAC1B;;;;;;;;;;;;;;;AAgBA,SAAgB,SACd,QACA,UACA,aAAsD;CAEtD,IAAI;CACJ,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG;EACjD,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,mBAAmB,IAAI,MAAM,IAAI,GAAG;EAC/D,OAAO;EACP;CACF;CACA,MAAM,SAAS,gBAAgB,MAAM;CACrC,MAAM,gBAAgB,WAAW,MAAM;CACvC,IAAI,CAAC,UAAU;EAKb,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,MAAM,OAAO,cAAc,QAAQ,MAAM;EACzC,OAAO;GAAE;GAAM;GAAQ;GAAM,UAAU,QAAQ;EAAa;CAC9D;CACA,IAAI;CACJ,IAAI;EACF,OAAO,UAAU,eAAe,QAAQ,WAAW,CAAC;CACtD,QAAQ;EACN;CACF;CACA,OAAO;EAAE;EAAM;EAAQ;EAAM,UAAU,QAAQ;CAAa;AAC9D;;;;;;;;;AA6BA,eAAe,QAAQ,QAAsB,OAAe,QAAoB;CAC9E,QAAQ,MAAM,OAAO,KAAK,OAAO,GAAG,EAAE,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM,EAAE,CAAE,EAAA,CAAG,OAAO,SAAS;AACtG;;;;;;;;;;;;AAaA,eAAe,WAAW,QAAsB,QAAoB;CAClE,IAAI,OAAO;CACX,OAAO,MAAM,QAAQ,QAAQ,MAAM,MAAM,GAAG;EAC1C,IAAI,QAAA,KAA+B,OAAO,KAAA;EAC1C,OAAO,KAAK,IAAI,OAAO,GAAG,qBAAqB;CACjD;CACA,IAAI,MAAM;CACV,OAAO,OAAO,MAAM,GAAG;EACrB,MAAM,MAAM,MAAM,KAAK,OAAO,OAAO,OAAO,CAAC;EAC7C,IAAI,MAAM,QAAQ,QAAQ,KAAK,MAAM,GAAG,MAAM;OACzC,OAAO;CACd;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,eAAsB,SACpB,KACA,WACA,eAAuB,sBACvB,QAAoB;CAEpB,MAAM,cAAc,IAAI,IAAI,oBAAoB;CAChD,IAAI,gBAAgB,KAAA,GAAW,OAAO,EAAE,MAAM,aAAY;CAC1D,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,YAAY,KAAK,WAAW,QAAQ,EACjD,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM,EAAA,CACxC;CACH,SAAS,OAAgB;EACvB,IAAI,QAAQ,YAAY,MAAM,MAAM;EACpC,IAAI,OAAO,KAAK,4CAA4C,UAAS,KAAM,cAAc,KAAK,GAAG;EACjG,OAAO,EAAE,MAAM,aAAY;CAC7B;CACA,IAAI;EACF,MAAM,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,CAAC;EACnD,MAAM,QAAQ,MAAM,WAAW,QAAQ,MAAM;EAC7C,IAAI,UAAU,KAAA,GAAW;GACvB,IAAI,OAAO,KAAK,6BAA6B,UAAS,qCAAsC;GAC5F,OAAO,EAAE,MAAM,aAAY;EAC7B;EACA,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,MAAM;EAIxC,OAAO;GAAE,MAAM;GAAQ,SAAQ,MAHV,OAAO,KAAK,OAAO,QAAQ,EAC9C,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM,EAAA,CACxC,EAAA,CACqC;GAAQ,UAAU,UAAU;EAAC;CACrE,SAAS,OAAgB;EACvB,IAAI,QAAQ,YAAY,MAAM,MAAM;EACpC,IAAI,OAAO,KAAK,4CAA4C,UAAS,KAAM,cAAc,KAAK,GAAG;EACjG,OAAO,EAAE,MAAM,aAAY;CAC7B,UAAU;EACR,MAAM,OAAO,MAAK;CACpB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvRA,IAAa,0BAAb,cAA6C,MAAK;CAChD,YAAY,WAAoB;EAC9B,MAAM,qBAAqB,OAAO,SAAS,EAAC,6CAA8C;EAC1F,KAAK,OAAO;CACd;;;;;;;;;;;;AAaF,SAAgB,8BAA8B,QAA+B;CAC3E,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,SAAS,uBAAuB,OAAO;EACjD,IAAI,MAAM,SAAS,yBAAyB,OAAO;CACrD;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,mBACpB,KACA,WACA,QAAoB;CAEpB,MAAM,QAAQ,IAAI,IAAI,cAAc;CACpC,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,cAAc,MAAM,MAAM,eAAe,WAAW,EACxD,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM,EAAA,CACxC;CACD,IAAI;EAEF,OAAO,uBADW,YAAY,OAAO,MAAM,YAAY,mBACzB,CAAS,CAAC,EAAE,SAAS;CACrD,UAAU;EACR,YAAY,OAAO,QAAQ,CAAA;CAC7B;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,mBACpB,KACA,WACA,QACA,QAAoB;CAEpB,MAAM,UAAU,IAAI,IAAI,cAAc;CACtC,MAAM,QAAQ,IAAI,IAAI,cAAc;CACpC,IAAI,UAAU,KAAA,GAAW;EACvB,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,MAAM,8DAA8D;EAEhF,OAAO,CAAA;CACT;CACA,MAAM,cAAc,MAAM,MAAM,eAAe,WAAW,EACxD,GAAI,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM,EAAA,CACzC;CACD,IAAI;EACF,MAAM,cAAc,YAAY;EAChC,MAAM,YAAY,YAAY,OAAO,MAAM,YAAY,mBAAmB;EAC1E,MAAM,aAAa,uBAAuB,SAAS;EACnD,IAAI,YAAY,SAAS,YAAY,MAAM,IAAI,wBAAwB,SAAS;EAChF,IAAI,YAAY,KAAA,KAAa,gBAAgB,KAAA,GAAW;GACtD,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,MAAM,4DAA4D;GAE9E,OAAO,CAAA;EACT;EACA,IAAI,WAAW,KAAA,GAAW;GAGxB,MAAM,WAAW,MAAM,QAAQ,QAAQ,YAAY,OAAO,eAAe,KAAA,CAAS;GAClF,OAAO,EACL,OAAO,OAAO,aAAY;IAAG,MAAM,QAAQ,MAAM,UAAU,SAAS,EAAE;GAAE,EAAA;EAE5E;EACA,MAAM,wBAAwB,8BAA8B,SAAS;EACrE,MAAM,WAAW,wBAAwB,YAAY,OAAO,cAAc,KAAA;EAC1E,IAAI,yBAAyB,OAAO,aAAa,UAC/C,MAAM,IAAI,MAAM,aAAa,OAAO,SAAS,EAAC,2CAA4C;EAE5F,MAAM,cAAgC;GACpC,GAAG,OAAO,aAAa,WAAW,EAAE,aAAa,SAAQ,IAAK,CAAA;GAC9D,GAAG,YAAY,SAAS,iBAAiB,WAAW,YAAY,KAAA,IAC5D,EAAE,SAAS,WAAW,QAAO,IAC7B,CAAA;GACJ,GAAG,YAAY,SAAS,iBAAiB,WAAW,eAAe,KAAA,IAC/D,EAAE,YAAY,WAAW,WAAU,IACnC,CAAA;;EAEN,MAAM,eAAyC,YAAY,SAAS,gBAChE;GACA,GAAG,WAAW,kBAAkB,KAAA,IAAY,CAAA,IAAK,EAAE,UAAU,WAAW,cAAa;GACrF,GAAG,WAAW,eAAe,KAAA,IAAY,CAAA,IAAK,EAAE,OAAO,WAAW,WAAU;GAC5E,GAAG,WAAW,yBAAyB,KAAA,IACnC,CAAA,IACA,EAAE,iBAAiB,WAAW,qBAAoB;MAEtD,KAAA;EACJ,OAAO;GACL,GAAG,iBAAiB,KAAA,IAAY,CAAA,IAAK,EAAE,aAAY;GACnD,OAAO,OAAO,aAAY;IAAG,MAAM,sBAAsB,UAAU,QAAQ,WAAW;GAAE;;CAE5F,UAAU;EACR,YAAY,OAAO,QAAQ,CAAA;CAC7B;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrHA,eAAsB,gBACpB,KACA,eACA,QAAoB;CAEpB,MAAM,YAAY,IAAI,IAAI,WAAW;CACrC,IAAI,cAAc,KAAA,GAAW,OAAO;EAAE,SAAS,CAAA;EAAI,QAAQ,CAAA;EAAI,aAAa;CAAK;CACjF,IAAI;EACF,MAAM,OAAO,MAAM,UAAU,gBAAgB,eAAe,MAAM;EAClE,MAAM,UAAwB,CAAA;EAC9B,MAAM,SAAyB,CAAA;EAC/B,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,IAAI,SAAS,SAAS;IACxB,QAAQ,KAAK;KAAE,IAAI,IAAI;KAAI,MAAM,IAAI;IAAI,CAAE;IAC3C;GACF;GACA,OAAO,KAAK;IAAE,IAAI,IAAI;IAAI,QAAQ,IAAI;GAAM,CAAE;EAChD;EACA,OAAO;GAAE;GAAS;GAAQ,aAAa;EAAK;CAC9C,SAAS,OAAgB;EACvB,IAAI,QAAQ,YAAY,MAAM,MAAM;EACpC,IAAI,OAAO,KAAK,+CAA+C,cAAa,KAAM,cAAc,KAAK,GAAG;EACxG,OAAO;GAAE,SAAS,CAAA;GAAI,QAAQ,CAAA;GAAI,aAAa;EAAI;CACrD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1DA,MAAM,eAAe;;AA8ErB,SAAS,UAAU,MAAc;CAC/B,OAAO;EACL,SAAS,KAAK,QAAQ,KAAI,WAAU,OAAO,EAAE;EAG7C,aAAa,KAAK,QAAQ,MAAK,WAAU,OAAO,SAAS,aAAa,KACjE,KAAK,OAAO,MAAK,WAAU,OAAO,WAAW,aAAa;EAG/D,QAAQ,KAAK,eAAe,KAAK,OAAO,MAAK,WAAU,OAAO,WAAW,aAAa;;AAE1F;;;;;;;;;;AAWA,SAAgB,iBACd,KACA,OACA,UACA,MAAqB;;;;;;CAOrB,MAAM,6BAAa,IAAI,IAAG;;CAE1B,MAAM,kBAAkB,WAAsB,SAAuB;EACnE,IAAI,MAAM,WAAW,IAAI,SAAS;OAC7B,WAAW,OAAO,SAAS;CAClC;;CAEA,MAAM,wBAAQ,IAAI,IAAG;;;;;;;CAQrB,MAAM,WAAW,UAAyB;EACxC,IAAI,MAAM,WAAW,WAAW,OAAO;EACvC,MAAM,WAAW,MAAM,SAAS,MAAM,EAAE;EACxC,IAAI,SAAS,YAAY,SAAS,aAAa,SAAS,GAAG,OAAO;EAClE,OAAO,MAAM,MAAM,SAAS,SAAS,MAAM,MAAM,SAAS,SAAS;CACrE;;;;;;;;CASA,MAAM,WAAW,OAAO,WAAsB,WAA8C;EAC1F,QAAQ,eAAc;EACtB,MAAM,SAAS,MAAM,IAAI,SAAS;EAClC,IAAI,WAAW,KAAA,GAAW,OAAO;EAGjC,MAAM,UAAU,IAAI,IAAI,WAAW,MAAM,KAAA,IACrC;GAAE,SAAS,CAAA;GAAI,aAAa;GAAO,QAAQ;EAAI,IAC/C,UAAU,MAAM,gBAAgB,KAAK,WAAW,MAAM,CAAC;EAC3D,MAAM,IAAI,WAAW,OAAO;EAC5B,OAAO;CACT;;;;;;;;;CAUA,MAAM,iBAAiB,OAAO,WAAuD;EACnF,MAAM,wBAAQ,IAAI,IAAG;EACrB,KAAK,MAAM,SAAS,IAAI,OAAO,KAAI,GAAI,IAAI,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,EAAE;EAG7E,KAAK,MAAM,QAAQ,CAAC,GAAG,KAAK,GAC1B,KAAK,MAAM,WAAW,MAAM,SAAS,MAAM,MAAM,EAAA,CAAG,SAAS,MAAM,IAAI,MAAM;EAE/E,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,QAAO,cAAa,CAAC,KAAK,SAAS,SAAS,CAAC;CACjE;;;;;;;;;CAUA,MAAM,YAAY,WAAkD;EAClE,MAAM,OAAO,IAAI,IAAI,MAAM;EAC3B,IAAI,SAAS,KAAA,GAAW,OAAO,CAAA;EAC/B,MAAM,uBAAO,IAAI,IAAG;EACpB,MAAM,WAAW,UAA2B;GAC1C,KAAK,MAAM,QAAQ,UAAU,KAAA,IAAY,KAAK,KAAI,IAAK,KAAK,KAAK,KAAK,GAAG;IACvE,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY;IAC7D,KAAK,IAAI,OAAO,KAAK,EAAE,CAAC;GAC1B;EACF;EACA,QAAO;EACP,KAAK,MAAM,SAAS,QAAQ,QAAQ,KAAK;EACzC,OAAO,CAAC,GAAG,IAAI;CACjB;;;;;;;;CASA,MAAM,SAAS,OAAO,WAA+C;EACnE,MAAM,yBAAS,IAAI,IAAG;EAGtB,KAAK,MAAM,CAAC,WAAW,WAAW,SAAS,QAAQ,OAAO,IAAI,WAAW;GAAE,OAAO,OAAO;GAAO,QAAQ;EAAK,CAAE;EAG/G,MAAM,OAAO,KAAK,KAAI;EACtB,KAAK,MAAM,CAAC,WAAW,SAAS,MAAM,OAAO,IAAI,WAAW,IAAI;EAChE,KAAK,MAAM,aAAa,YAAY,OAAO,IAAI,WAAW;GAAE,OAAO;GAAY,QAAQ;EAAK,CAAE;EAC9F,MAAM,SAAS,KAAK,OAAM;EAG1B,KAAK,MAAM,aAAa,CAAC,GAAG,MAAM,KAAI,CAAE,GAAG,IAAI,CAAC,OAAO,IAAI,SAAS,GAAG,MAAM,OAAO,SAAS;EAE7F,IAAI,OAAO;EACX,IAAI,UAAU;EACd,MAAM,WAAqB,CAAA;EAC3B,MAAM,UAAoB,CAAA;EAC1B,KAAK,MAAM,CAAC,WAAW,SAAS,QAAQ;GAItC,IAAI,OAAO,IAAI,SAAS,GAAG;IACzB,QAAQ,KAAK,OAAO,SAAS,CAAC;IAC9B;GACF;GACA,IAAI,KAAK,UAAU,QAAQ;IACzB,QAAQ;IACR;GACF;GAEA,CADe,KAAK,SAAS,WAAW,QAAA,CACjC,KAAK,OAAO,SAAS,CAAC;GAK7B,IAAI,KAAK,UAAU,KAAK,UAAU,WAAW,WAAW;EAC1D;EAEA,MAAM,cAAwB,CAAA;EAC9B,MAAM,cAAwB,CAAA;EAC9B,MAAM,SAAS,CAAC,GAAG,OAAO,KAAI,CAAE;EAQhC,MAAM,WAAW,OAAO,MAAK,cAAa,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,OAAO;EAI5E,MAAM,QAAQ,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAM,cAAa,CAAC,WAAW,MAAM,SAAS,WAAW,MAAM,CAAC,CAAU,CAAC;EACtH,KAAK,MAAM,CAAC,WAAW,SAAS,OAAO;GACrC,IAAI,KAAK,aAAa,YAAY,KAAK,OAAO,SAAS,CAAC;GACxD,IAAI,KAAK,QAAQ,YAAY,KAAK,OAAO,SAAS,CAAC;EACrD;EAEA,MAAM,QAA2B,CAAA;EACjC,IAAI,IAAI,IAAI,WAAW,MAAM,KAAA,GAAW,MAAM,KAAK,OAAO;EAC1D,IAAI,IAAI,IAAI,MAAM,MAAM,KAAA,GAAW,MAAM,KAAK,MAAM;EAEpD,MAAM,aAAa,OAAO;EAU1B,OAAO;GACL,SAP6B,eAAe,IAC1C,SACA,SAAS,cAAc,YAAY,WAAW,IAC5C,SACA;GAIJ;GACA;GACA,GAAG,aAAa,KAAA,IAAY,CAAA,IAAK,EAAE,UAAU,OAAO,QAAQ,EAAC;GAC7D,UAAU,SAAS,KAAI;GACvB;GACA,SAAS,QAAQ,KAAI;GACrB,aAAa,YAAY,KAAI;GAC7B,aAAa,YAAY,KAAI;GAC7B,MAAM,SAAS,MAAM;GACrB;;CAEJ;CAEA,OAAO;EACL,UAAU,OAAO,WAAU;GACzB,KAAK,IAAI,QAAQ,GAAG,QAAQ,cAAc,SAAS,GAAG;IACpD,MAAM,UAAU,MAAM,eAAe,MAAM;IAC3C,IAAI,QAAQ,WAAW,GAAG;IAC1B,KAAK,MAAM,aAAa,SAAS;KAC/B,QAAQ,eAAc;KAMtB,QAAQ,MADc,KAAK,MAAM,WAAW,MAAM,GAClD;MACE,KAAK;OACH,eAAe,WAAW,KAAK;OAC/B,KAAK,KAAK,SAAS;OACnB;MACF,KAAK;OACH,eAAe,WAAW,IAAI;OAC9B;MACF,SACE;KACJ;IACF;GACF;GACA,MAAM,SAAS,mBAAmB,MAAM,OAAO,MAAM,CAAC;GACtD,OAAO,WAAW,SAAS,IACvB;IAAE,MAAM;IAAW,MAAM;GAAM,IAC/B;IAAE,MAAM;IAAS,MAAM;GAAM;EACnC;EACA,YAAY,OAAO,YAAY;GAAE,MAAM;GAAW,MAAM,mBAAmB,MAAM,OAAO,MAAM,CAAC;EAAC;EAChG;;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9SA,MAAa,cAAc;;AAoB3B,MAAa,eAAe;;;;;AAM5B,MAAa,eAAe;;AAI5B,MAAa,kBAAkB;;AAG/B,MAAa,yBAAyB;;AAGtC,MAAa,mBAAmB;;;;;;;AAQhC,MAAa,cAAc;;AAI3B,MAAa,oBAAkB;;AAG/B,MAAa,qBAAqB;AAClC,MAAa,sBAAsB;;;;;;;;AA6HnC,SAAgB,qBAAqB,UAAkC;CACrE,MAAM,UAAU,SAAS,QAAO,YAAW,QAAQ,OAAO,CAAC,CAAC;CAC5D,MAAM,OAAO,YAAY,IACrB,yCACA,YAAY,IACV,kDACA,iBAAiB,QAAO;CAC9B,MAAM,UAAU,SAAS,QAAO,YAAW,CAAC,QAAQ,OAAO;CAC3D,IAAI,QAAQ,WAAW,GAAG,OAAO,GAAG,KAAI;CACxC,MAAM,QAAQ,QACX,QAAO,YAAW,QAAQ,YAAY,WAAW,CAAC,CAClD,KAAI,YAAW,IAAI,OAAO,QAAQ,SAAS,EAAC,EAAG;CAMlD,OAAO,GAAG,KAAI,IALD,MAAM,WAAW,IAC1B,GAAG,OAAO,QAAQ,MAAM,EAAC,yBACzB,QAAQ,WAAW,MAAM,SACvB,GAAG,MAAM,KAAK,IAAI,EAAC,6BACnB,GAAG,MAAM,KAAK,IAAI,EAAC,iCAAkC,OAAO,QAAQ,SAAS,MAAM,MAAM,EAAC,4BACzE;AACzB;;;;;;;;;;AAqBA,SAAgB,kBACd,KACA,MACA,UACA,SACA,MAAgB;;CAGhB,MAAM,WAAW,YAAgD,QAAQ,cAAa,CAAE,EAAE;;;;;;;;;;;;;CAc1F,MAAM,eAAe,UAAsB;EACzC,IAAI,MAAM,MAAM,QAAO,MAAO,KAAA,GAAW;EACzC,MAAM,SAAS,QAAQ,MAAM,OAAO;EACpC,IAAI,WAAW,KAAA,GAAW;EAC1B,MAAM,MAAM,OAAO;GACjB,UAAU,OAAO;GACjB,OAAO,OAAO;GACd,GAAG,OAAO,oBAAoB,KAAA,IAAY,CAAA,IAAK,EAAE,iBAAiB,OAAO,gBAAe;GACzF;CACH;;CAGA,MAAM,eAAe,aACnB,QAAQ,iBAAiB,SAAS,QAAQ,IAAI,CAAA,IAAK,CAAC;EAAE,MAAM;EAAQ,MAAM;CAAW,CAAE;;;;;;;;;;;;CAazF,MAAM,QAAQ,OAAc,YAA8B;EACxD,IAAI,QAAQ,SAAS,QAAQ;EAC7B,IAAI,KAAK,OAAO,MAAM,EAAE,KAAK,MAAM,WAAW,QAAQ;EACtD,IAAI,CAAC,UAAU,MAAM,QAAQ,eAAc,CAAE,GAAG;EAChD,MAAM,WAAW,QAAQ,MAAM,OAAO,CAAC,EAAE;EACzC,IAAI,aAAa,KAAA,GAAW;EAC5B,MAAM,UAA0B,QAAQ,SAAS,YAC7C,CAAC;GAAE,MAAM;GAAQ,MAAM,QAAQ;EAAI,CAAE,IACrC,YAAY,QAAQ;EACxB,MAAM,SAAS,kBAAkB;GAAE;GAAS,QAAQ,EAAE,MAAM,QAAO;EAAE,CAAE,CAAC;CAC1E;;;;;;;;;;;CAYA,MAAM,aAAa,UAAsB;EACvC,MAAM,QAAQ,IAAI,IAAI,OAAO;EAC7B,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI;GACF,MAAM,OAAO,MAAM,IAAI,KAAK;GAC5B,IAAI,SAAS,KAAA,KAAa,KAAK,UAAU,YAAY,KAAK,eAAe,YAAY;GACrF,IAAI,KAAK,iBAAiB,KAAK,eAAe;GAC9C,MAAM,OAAO,OAAO;IAAE,IAAI,KAAK;IAAI,UAAU,KAAK;GAAQ,CAAE;EAC9D,SAAS,OAAgB;GACvB,IAAI,OAAO,KAAK,yCAAyC,MAAM,GAAE,KAAM,cAAc,KAAK,GAAG;EAC/F;CACF;;;;;;;;;;;;;;;;;;CAmBA,MAAM,WAAW,OACf,WACA,SACA,OACA,UACA,WAC0B;EAC1B,MAAM,SAAS,aAAa,KAAA,IAAY,KAAA,IAAY,IAAI,OAAO,IAAI,QAAQ;EAC3E,IAAI;EACJ,IAAI;GACF,MAAM,cAAc,MAAM,mBAAmB,KAAK,WAAW,QAAQ,MAAM;GAG3E,SAAS,MAAM,IAAI,KAAK,OAAO,OAAO;IACpC,iBAAiB;IACjB,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,aAAa,OAAM;IACpD,GAAG;IACH,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,OAAM;IACxC,EAAA,CAAG;EACN,SAAS,OAAgB;GACvB,IAAI,iBAAiB,yBACnB,OAAO;IACL;IACA,SAAS;IACT,SAAS;IACT,QAAQ;KAAE,MAAM;KAAS,MAAM;IAAgB;;GAGnD,IAAI,OAAO,KAAK,0BAA0B,UAAS,yBAA0B,cAAc,KAAK,GAAG;GACnG,OAAO;IACL;IACA,SAAS;IACT,SAAS;IACT,QAAQ;KAAE,MAAM;KAAS,MAAM;IAAsB;;EAEzD;EACA,YAAY,KAAK;EAOjB,KAAK,OAAO,OAAO;EACnB,IAAI;GACF,MAAM,SAAS,OAAO,SAAS;EACjC,SAAS,OAAgB;GACvB,IAAI,OAAO,KAAK,0BAA0B,UAAS,6BAA8B,cAAc,KAAK,GAAG;GACvG,OAAO;IAAE;IAAW,SAAS;IAAO,SAAS;IAAc,QAAQ;KAAE,MAAM;KAAS,MAAM;IAAe;GAAE;EAC7G;EACA,KAAK,OAAO,SAAS;EACrB,IAAI,OAAO,UAAU,KAAK;EAC1B,OAAO;GAAE;GAAW,SAAS;GAAM,QAAQ;IAAE,MAAM;IAAW,MAAM;GAAY;EAAE;CACpF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,MAAM,gBAAgB,OACpB,WACA,SACA,UACA,UACA,WAC0B;EAC1B,MAAM,SAAS,SAAS,IAAI,SAAS;EACrC,IAAI,WAAW,KAAA,GACb,OAAO;GAAE;GAAW,SAAS;GAAO,SAAS;GAAc,QAAQ;IAAE,MAAM;IAAW,MAAM;GAAe;EAAE;EAM/G,IAAI,aAAa,UACf,OAAO;GAAE;GAAW,SAAS;GAAO,SAAS;GAAiB,QAAQ;IAAE,MAAM;IAAW,MAAM;GAAW;EAAE;EAE9G,MAAM,OAAO,IAAI,OAAO,IAAI,SAAS;EACrC,IAAI,SAAS,KAAA,GAAW,OAAO,MAAM,SAAS,WAAW,SAAS,OAAO,WAAW,UAAU,MAAM;EACpG,IAAI,CAAC,KAAK,SAAS,SAAS,GAAG;GAK7B,YAAY,IAAI;GAChB,KAAK,MAAM,OAAO;GAClB,IAAI;IACF,MAAM,SAAS,OAAO,SAAS;GACjC,SAAS,OAAgB;IACvB,IAAI,OAAO,KAAK,0BAA0B,UAAS,6BAA8B,cAAc,KAAK,GAAG;IACvG,OAAO;KAAE;KAAW,SAAS;KAAO,SAAS;KAAc,QAAQ;MAAE,MAAM;MAAS,MAAM;KAAe;IAAE;GAC7G;GACA,IAAI,OAAO,WAAW,UAAU,IAAI;GACpC,OAAO;IAAE;IAAW,SAAS;IAAM,QAAQ;KAAE,MAAM;KAAW,MAAM;IAAY;GAAE;EACpF;EACA,MAAM,UAAU,KAAK,QAAQ,SAAS;EAItC,IAAI,SAAS;GACX,YAAY,IAAI;GAChB,KAAK,MAAM,OAAO;EACpB;EACA,MAAM,WAAW,MAAM,KAAK,QAAQ,WAAW,YAAY;EAC3D,IAAI,SAAS,SAAS,SACpB,OAAO;GAAE;GAAW,SAAS;GAAO,SAAS;GAAc,QAAQ;EAAQ;EAE7E,IAAI,WAAW,OAAO,WAAW,UAAU,IAAI;EAC/C,OAAO;GAAE;GAAW,SAAS;GAAM,QAAQ;EAAQ;CACrD;;;;;;;;;;;;;;;CAgBA,MAAM,gBAAgB,OACpB,WACA,SACA,UACA,WAC0B;EAC1B,MAAM,SAAS,SAAS,IAAI,SAAS;EACrC,MAAM,OAAO,IAAI,OAAO,IAAI,SAAS;EAErC,IAAI,SAAS,KAAA,GAAW;GACtB,IAAI,CAAC,KAAK,SAAS,SAAS,GAAG;IAG7B,IAAI,WAAW,KAAA,KAAa,OAAO,UAAU,QAC3C,OAAO;KAAE;KAAW,SAAS;KAAO,SAAS;KAAa,QAAQ;MAAE,MAAM;MAAS,MAAM;KAAY;IAAE;IAEzG,IAAI,WAAW,KAAA,GACb,OAAO;KAAE;KAAW,SAAS;KAAO,SAAS;KAAc,QAAQ;MAAE,MAAM;MAAW,MAAM;KAAe;IAAE;IAO/G,IAAI;KACF,MAAM,SAAS,OAAO,SAAS;IACjC,SAAS,OAAgB;KACvB,IAAI,OAAO,KAAK,0BAA0B,UAAS,6BAA8B,cAAc,KAAK,GAAG;KACvG,OAAO;MAAE;MAAW,SAAS;MAAO,SAAS;MAAc,QAAQ;OAAE,MAAM;OAAS,MAAM;MAAe;KAAE;IAC7G;IACA,YAAY,IAAI;IAChB,KAAK,MAAM,OAAO;IAClB,IAAI,OAAO,WAAW,UAAU,IAAI;IACpC,OAAO;KAAE;KAAW,SAAS;KAAM,QAAQ;MAAE,MAAM;MAAW,MAAM;KAAY;IAAE;GACpF;GAIA,MAAM,UAAU,KAAK,QAAQ,SAAS;GACtC,IAAI,WAAW,WAAW,KAAA,KAAa,OAAO,UAAU,QACtD,OAAO;IAAE;IAAW,SAAS;IAAO,SAAS;IAAa,QAAQ;KAAE,MAAM;KAAS,MAAM;IAAY;GAAE;GAIzG,IAAI,SAAS;IACX,YAAY,IAAI;IAChB,KAAK,MAAM,OAAO;GACpB;GACA,MAAM,WAAW,MAAM,KAAK,QAAQ,WAAW,YAAY;GAC3D,IAAI,SAAS,SAAS,SACpB,OAAO;IAAE;IAAW,SAAS;IAAO,SAAS;IAAc,QAAQ;GAAQ;GAI7E,IAAI,YAAY,QAAQ,aAAa,OAAO,UAAU,IAAI;GAC1D,OAAO;IAAE;IAAW,SAAS;IAAM,QAAQ;GAAQ;EACrD;EAEA,IAAI,WAAW,KAAA,GACb,OAAO;GAAE;GAAW,SAAS;GAAO,SAAS;GAAc,QAAQ;IAAE,MAAM;IAAW,MAAM;GAAe;EAAE;EAI/G,IAAI,OAAO,UAAU,QACnB,OAAO;GAAE;GAAW,SAAS;GAAO,SAAS;GAAa,QAAQ;IAAE,MAAM;IAAS,MAAM;GAAY;EAAE;EAEzG,OAAO,MAAM,SAAS,WAAW,SAAS,OAAO,WAAW,UAAU,MAAM;CAC9E;;;;;;;;;;;CAYA,MAAM,eAAe,OAAO,YAAkC,WAA+C;EAC3G,MAAM,QAAQ,IAAI,IAAI,UAAU;EAChC,MAAM,2BAAW,IAAI,IAAG;EACxB,MAAM,YAAY,IAAI,IAAI,WAAW;EACrC,IAAI,cAAc,KAAA,GAChB,KAAK,MAAM,aAAa,YAAY;GAClC,QAAQ,eAAc;GACtB,IAAI;IACF,KAAK,MAAM,SAAS,MAAM,UAAU,aAAa,WAAW,MAAM,GAChE,IAAI,MAAM,IAAI,MAAM,EAAE,GAAG,SAAS,IAAI,MAAM,IAAI,SAAS;GAE7D,SAAS,OAAgB;IACvB,IAAI,QAAQ,YAAY,MAAM,MAAM;IACpC,IAAI,OAAO,KAAK,0CAA0C,UAAS,KAAM,cAAc,KAAK,GAAG;GACjG;EACF;EAEF,MAAM,WAAW,cAAgC;GAC/C,IAAI,QAAQ;GACZ,MAAM,uBAAO,IAAI,IAAe,CAAC,SAAS,CAAC;GAC3C,KAAK,IAAI,KAAK,SAAS,IAAI,SAAS,GAAG,OAAO,KAAA,GAAW,KAAK,SAAS,IAAI,EAAE,GAAG;IAE9E,IAAI,KAAK,IAAI,EAAE,GAAG;IAClB,KAAK,IAAI,EAAE;IACX,SAAS;GACX;GACA,OAAO;EACT;EACA,MAAM,yBAAS,IAAI,IAAG;EACtB,KAAK,MAAM,aAAa,YAAY;GAClC,MAAM,QAAQ,QAAQ,SAAS;GAC/B,MAAM,SAAS,OAAO,IAAI,KAAK;GAC/B,IAAI,WAAW,KAAA,GAAW,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;QAClD,OAAO,KAAK,SAAS;EAC5B;EACA,OAAO;GACL,SAAS;GACT,QAAQ,CAAC,GAAG,OAAO,KAAI,CAAE,CAAC,CAAC,MAAM,MAAM,UAAU,OAAO,KAAK,CAAC,CAC3D,KAAI,UAAS,OAAO,IAAI,KAAK,KAAK,CAAA,CAAE;;CAE3C;CAEA,OAAO;EACL;EACA;EACA,aAAa,OAAO,WAAW,WAAU;GAKvC,MAAM,aAAa,CADH,WAAW,IAAI,MAAM,gBAAgB,KAAK,WAAW,MAAM,EAAA,CAAG,QAAQ,KAAI,WAAU,OAAO,EAAE,CAC1F,CAAM,CAAC,QAAO,WAAU,SAAS,IAAI,MAAM,MAAM,KAAA,CAAS;GAC7E,IAAI,WAAW,WAAW,GACxB,OAAO;IAAE,SAAS;IAAG,SAAS;IAAG,UAAU,CAAA;IAAI,QAAQ;KAAE,MAAM;KAAW,MAAM;IAAe;GAAE;GASnG,MAAM,aAAa,WAAW,OAAM,WAAU,SAAS,IAAI,MAAM,CAAC,EAAE,UAAU,MAAM;GACpF,MAAM,WAA4B,CAAA;GAClC,IAAI,YACF,KAAK,MAAM,UAAU,YAAY;IAC/B,QAAQ,eAAc;IACtB,MAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,YAAY;IACtD,SAAS,KAAK,OAAO,SAAS,UAC1B;KAAE,WAAW;KAAQ,SAAS;KAAO,SAAS;KAAc;IAAM,IAClE;KAAE,WAAW;KAAQ,SAAS;KAAM;IAAM,CAAE;GAClD;QACK;IACL,MAAM,UAAU,MAAM,aAAa,YAAY,MAAM;IACrD,KAAK,MAAM,SAAS,QAAQ,QAAQ;KAClC,QAAQ,eAAc;KACtB,KAAK,MAAM,UAAU,OAAO;MAM1B,MAAM,SAAS,SAAS,IAAI,MAAM;MAClC,IAAI,WAAW,KAAA,KAAa,OAAO,UAAU,QAAQ;OACnD,SAAS,KAAK;QACZ,WAAW;QACX,SAAS;QACT,SAAS;QACT,QAAQ;SAAE,MAAM;SAAS,MAAM;QAAY;QAC5C;OACD;MACF;MACA,SAAS,KAAK,MAAM,cAAc,QAAQ,EAAE,MAAM,SAAQ,GAAI,QAAQ,QAAQ,IAAI,MAAM,GAAG,MAAM,CAAC;KACpG;IACF;GACF;GACA,MAAM,OAAO,SAAS,MAAK,YAAW,QAAQ,cAAc,SAAS;GACrE,MAAM,WAAW,SACd,QAAQ,YAAmE,CAAC,QAAQ,WAAW,QAAQ,YAAY,KAAA,CAAS,CAAC,CAC7H,KAAI,aAAY;IAAE,WAAW,QAAQ;IAAW,SAAS,QAAQ;GAAO,EAAG;GAC9E,OAAO;IACL,SAAS,SAAS;IAClB,SAAS,SAAS,QAAO,YAAW,QAAQ,OAAO,CAAC,CAAC;IACrD;IAIA,QAAQ,SAAS,KAAA,IACb;KAAE,MAAM;KAAW,MAAM;IAAe,IACxC;KAAE,MAAM,SAAS,WAAW,KAAK,KAAK,UAAU,YAAY;KAAS,MAAM,qBAAqB,QAAQ;IAAC;;EAEjH;EACA,cAAc,OAAO,WAAU;GAC7B,MAAM,UAAU,MAAM,aAAa,SAAS,KAAI,CAAE,CAAC,KAAI,UAAS,MAAM,SAAS,GAAG,MAAM;GACxF,MAAM,WAA4B,CAAA;GAClC,KAAK,MAAM,SAAS,QAAQ,QAAQ;IAClC,QAAQ,eAAc;IAGtB,SAAS,KAAK,GAAG,MAAM,QAAQ,IAAI,MAAM,KAAI,cAC3C,cAAc,WAAW,EAAE,MAAM,SAAQ,GAAI,QAAQ,QAAQ,IAAI,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC;GAC1F;GACA,OAAO;EACT;EACA,cAAc,OAAO,WAAW,cAAa;GAC3C,IAAI;IACF,MAAM,SAAS,aAAa,WAAW,SAAS;GAClD,SAAS,OAAgB;IACvB,IAAI,OAAO,KAAK,gDAAgD,UAAS,KAAM,cAAc,KAAK,GAAG;IACrG,OAAO;KAAE,MAAM;KAAS,MAAM;IAAe;GAC/C;GACA,OAAO;IAAE,MAAM;IAAW,MAAM,YAAY,qBAAqB;GAAmB;EACtF;;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvpBA,MAAa,mBAAmB;;AAGhC,MAAa,yBAAyB;;;;;;AAOtC,MAAa,sBAAsB;;;;;;;;;;;;;AAenC,MAAa,uBAAuB;;;;;;;;;AAkBpC,SAAS,MACP,KACA,OACA,QACA,UAAiB;CAEjB,MAAM,EAAE,WAAW,OAAO,WAAW,cAAc;CACnD,MAAM,QAAQ,SAAS,QAAQ,UAAU,IAAI,SAAS,kBAAkB;CAIxE,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,MAAM,EAAE,MAAM,QAAQ,MAAM,aAAa;CACzC,OAAO;EACL,WAAW,OAAO,SAAS;EAC3B;EACA;EACA,SAAS;EACT,GAAG,SAAS,KAAA,IAAY,CAAA,IAAK;GAAE,WAAW,KAAK;GAAM,GAAG,WAAW,IAAI;EAAC;EACxE;EACA;EACA;EAIA;;AAEJ;;;;;;;;;;;;;AAuCA,SAAgB,gBACd,KACA,UACA,YACA,eAAuB,sBAAoB;;;;;;;CAQ3C,MAAM,wBAAQ,IAAI,IAAG;CAErB,MAAM,cAAc,WAAkC;EACpD,WAAW,OAAO,MAAM,SAAS;EACjC,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,SAAS;EACT,QAAQ,CAAA;EACR,MAAM;EACN,UAAU;EACV,WAAW,MAAM;;CAGnB,MAAM,WAAW,OAAO,OAAmB,WAA4C;EACrF,MAAM,OAAO,MAAM,SAAS,KAAK,MAAM,WAAW,cAAc,MAAM;EAGtE,IAAI,KAAK,SAAS,cAAc,OAAO,WAAW,KAAK;EAGvD,OAAO,MAAM,KAAK,OAAO,KAAK,QAAQ,KAAK,QAAQ,KAAK,WAAW,KAAK;CAC1E;;;;;;;;CASA,MAAM,SAAS,OACb,QACA,WACA,QACA,WAC0B;EAC1B,IAAI,EAAE,MAAM,IAAI,MAAM,CAAC,EAAE,IAAI,OAAO,SAAS,CAAC,KAAK,QAAQ,OAAO;GAAE,MAAM;GAAS,MAAM;EAAmB;EAC5G,MAAM,SAAS,SAAS,IAAI,SAAS;EAGrC,IAAI,WAAW,KAAA,KAAa,OAAO,UAAU,QAC3C,OAAO;GAAE,MAAM;GAAS,MAAM;EAAsB;EAEtD,IAAI,WAAW,SAAS,OAAO;GAAE,MAAM;GAAW,MAAM;EAAgB;EACxE,IAAI,WAAW,QAAQ,OAAO,MAAM,WAAW,aAAa,WAAW,CAAC,OAAO,SAAS;EAIxF,IAAI,WAAW,UACb,QAAQ,MAAM,WAAW,cAAc,WAAW,EAAE,MAAM,OAAM,GAAI,UAAU,KAAA,GAAW,MAAM,EAAA,CAAG;EAEpG,MAAM,OAAe,WAAW,aAAa,EAAE,MAAM,SAAQ,IAAK;GAAE,MAAM;GAAW,MAAM;EAAoB;EAC/G,QAAQ,MAAM,WAAW,cAAc,WAAW,MAAM,WAAW,KAAA,GAAW,MAAM,EAAA,CAAG;CACzF;CAEA,OAAO;EACL,MAAM,OAAO,QAAQ,WAAU;GAC7B,QAAQ,eAAc;GACtB,MAAM,OAAO,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,OAAO,OAAM,CAAE,CAAC,CACzD,SAAQ,UAAS,MAAM,UAAU,SAAS,CAAA,IAAK,CAAC,SAAS;IAAE,GAAG;IAAO,OAAO,MAAM;GAAK,GAAI,MAAM,CAAC,CAAC,CAAC;GACvG,MAAM,IAAI,QAAQ,IAAI,IAAI,KAAK,KAAI,QAAO,IAAI,SAAS,CAAC,CAAC;GACzD,OAAO,EAAE,KAAI;EACf;EACA;;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvKA,SAAS,WAAW,QAAc;CAMhC,OAAO,UALY,WAAW,IAC1B,iBACA,WAAW,IACT,gCACA,oBAAoB,OAAO,MAAM,EAAC,YACb;AAE7B;;AAGA,MAAM,kBAAkB;;;;;;;AAQxB,MAAM,mBAAmB;;AAGzB,MAAM,0BAA0B;;;;;;;AAQhC,SAAS,mBAAmB,OAA2B;CACrD,OAAO,sCAAsC,MAAM,KAAI,SAAQ,IAAI,OAAO,IAAI,EAAC,EAAG,CAAC,CAAC,KAAK,IAAI,EAAC;AAEhG;;;;;;;AAQA,MAAa,kBAAkB;;;;;;;;;;;;;AAe/B,SAAS,YAAY,KAAY;CAC/B,IAAI;EACF,OAAO,IAAI,OAAO,iBAAgB,MAAO,KAAA;CAC3C,QAAQ;EAGN,OAAO;CACT;AACF;;;;;;;;AASA,eAAe,gBAAgB,KAAc,WAAoB;CAC/D,IAAI,IAAI,SAAS,IAAI,SAAS,MAAM,KAAA,GAAW,OAAO;CACtD,MAAM,cAAc,IAAI,IAAI,oBAAoB;CAGhD,IAAI,gBAAgB,KAAA,GAAW,OAAO;CACtC,IAAI;EACF,OAAO,MAAM,YAAY,KAAK,SAAS,MAAM,KAAA;CAC/C,SAAS,OAAgB;EAGvB,IAAI,OAAO,KAAK,gDAAgD,UAAS,KAAM,cAAc,KAAK,GAAG;EACrG,OAAO;CACT;AACF;;AAgFA,SAAgB,gBAAgB,UAAkC;CAChE,MAAM,UAAU,SAAS,QAAO,YAAW,QAAQ,OAAO,CAAC,CAAC;CAC5D,MAAM,UAAU,SAAS,SAAS;CAClC,MAAM,OAAO,YAAY,IACrB,6BACA,YAAY,IACV,kDACA,iBAAiB,QAAO;CAC9B,IAAI,YAAY,GAAG,OAAO,GAAG,KAAI;CAKjC,OAAO,GAAG,KAAI,IAJE,SAAS,QAAO,YAAW,QAAQ,YAAY,WAAW,CAAC,CAAC,WACnD,UACrB,GAAG,QAAO,oDACV,GAAG,QAAO,uBACS;AACzB;;;;;;;;;;;;;AAwBA,SAAgB,sBACd,KACA,MACA,OACA,UACA,SAA8B;;CAG9B,MAAM,wBAAQ,IAAI,IAAG;;CAErB,MAAM,0BAAU,IAAI,IAAG;;;;;;;CAOvB,MAAM,yBAAS,IAAI,IAAG;;CAStB,IAAI,aAAa;CAEjB,MAAM,QAAQ,cAA8B;EAC1C,MAAM,OAAO,SAAS;EACtB,MAAM,SAAS,QAAQ,IAAI,SAAS;EACpC,IAAI,WAAW,KAAA,GAAW;EAC1B,QAAQ,OAAO,SAAS;EACxB,QAAa,QAAQ,OAAO,MAAM,QAAO,CAAE,CAAC,CAAC,OAAO,UAAkB;GACpE,IAAI,OAAO,KAAK,0CAA0C,cAAc,KAAK,GAAG;EAClF,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;CAuBA,MAAM,SAAS,OAAO,WAAsB,MAAgB,UAAsC;EAChG,IAAI;GACF,MAAM,SAAS,OAAO,WAAW,KAAK;EACxC,SAAS,OAAgB;GACvB,IAAI,OAAO,KAAK,8CAA8C,UAAS,OAAQ,MAAK,IAAK,cAAc,KAAK,GAAG;GAC/G,OAAO;EACT;EACA,MAAM,UAAU,KAAK,UAAU;EAC/B,KAAK,QAAQ;EACb,IAAI,SAAS,QAAQ,IAAI,SAAS,CAAC,EAAE,SAAQ;EAC7C,OAAO;CACT;;CAGA,MAAM,UAAU,cAAiC;EAC/C,MAAM,WAAW,MAAM,SAAS,SAAS;EACzC,IAAI,SAAS,aAAa,SAAS,GAAG,OAAO;EAI7C,OAAO,KAAK,OAAO,SAAS,KAAK,CAAC,SAAS;CAC7C;;;;;;CAOA,MAAM,iBAAiB,cACrB,MAAM,IAAI,SAAS,CAAC,EAAE,SAAS,SAAS,IAAI,SAAS,CAAC,EAAE;;;;;;;;;;;;;CAc1D,MAAM,aAAa,OAAO,WAAsB,MAAgB,WAA6C;EAC3G,MAAM,OAAO,MAAM,gBAAgB,KAAK,WAAW,MAAM;EACzD,MAAM,SAAsB,CAAC,KAAK,KAAK;EACvC,KAAK,MAAM,UAAU,KAAK,SAAS;GACjC,MAAM,QAAQ,cAAc,OAAO,EAAE;GACrC,IAAI,UAAU,KAAA,GAAW,OAAO,KAAK,KAAK;EAC5C;EAIA,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU,KAAK,cAAc,IAAI,IAAI,MAAM,GAAG,OAAO,KAAK,SAAS;EAErG,MAAM,SAAS,eAAe,MAAM;EAGpC,MAAM,SAAS,SAAS,IAAI,SAAS;EACrC,OAAO;GACL,OAAO,OAAO;GACd,MAAM,OAAO;GACb,cAAc,KAAK,UAAU,aAAa,MAAM,SAAS,SAAS,CAAC,CAAC,eAAe,CAAA;GACnF,GAAG,WAAW,KAAA,IAAY,CAAA,IAAK,EAAE,WAAW,OAAO,UAAS;;CAEhE;;;;;;;;CASA,MAAM,WAAW,cAA8B;EAC7C,MAAM,OAAO,MAAM,IAAI,SAAS;EAChC,IAAI,SAAS,KAAA,KAAa,QAAQ,IAAI,SAAS,GAAG;EAClD,MAAM,QAAQ,IAAI,OAAO,IAAI,SAAS;EACtC,IAAI,UAAU,KAAA,GAAW;EAIzB,MAAM,SAAS,OAAO,WAA0C,iBAAiB,MAAM,WAAW,WAAW,MAAM,MAAM,CAAC;EAC1H,MAAM,oBAAuC;GAC3C,cAAc,oBAAoB,oBAAoB;GACtD,MAAM;GAGN,aAAa;GACb,SAAS,OAAO,gBAAgB;IAAE,MAAM;IAAW,MAAM,MAAM,OAAO,WAAW,MAAM;GAAC;;;EAG1F,IAAI;EAMJ,MAAM,QAAQ,MAAM,IAAI,OAAO,CAAC,UAAU,IAAI,UAAS;GACrD,MAAM,SAAS,SAAS;IACtB,cAAc,oBAAoB,oBAAoB;IACtD,MAAM;IACN,aAAa;IACb,SAAS,OAAO,eAAgB,YAAY,GAAG,IAC3C;KAAE,MAAM;KAAS,MAAM;IAAe,KACrC,MAAM,WAAW,YAAY,WAAW,MAAM,IAAI,WAAW,MAAM,EAAA,CAAG;IAC5E;;GAED,IAAI,aAAa,MAAM,SAAS,SAAS,WAAU,CAAE;GACrD,iBAAgB;IAKd,WAAU;IACV,aAAa,MAAM,SAAS,SAAS,WAAU,CAAE;GACnD;GAMA,MAAM,SAAS,SAAS;IACtB,cAAc,oBAAoB,kBAAkB;IACpD,MAAM;IACN,aAAa;IACb,SAAS,YAAW;KAClB,IAAI,YAAY,GAAG,GAAG,OAAO;MAAE,MAAM;MAAS,MAAM;KAAe;KACnE,MAAM,SAAS,SAAS,IAAI,SAAS;KACrC,IAAI,WAAW,KAAA,GAAW,OAAO;MAAE,MAAM;MAAS,MAAM;KAAe;KACvE,OAAO,MAAM,WAAW,aAAa,WAAW,CAAC,OAAO,SAAS;IACnE;IACD;EACH,CAAC;EACD,QAAQ,IAAI,WAAW;GAAE;GAAO,gBAAe;IAAG,WAAU;GAAG;EAAC,CAAE;CACpE;;;;;;;;;;CAWA,MAAM,WAAW,cAAiC;EAChD,MAAM,OAAO,MAAM,IAAI,SAAS;EAChC,IAAI,SAAS,KAAA,KAAa,KAAK,UAAU,cAAc,KAAK,WAAW,OAAO;EAI9E,IAAI,KAAK,WAAW,YAAY,OAAO;EACvC,IAAI,CAAC,OAAO,SAAS,GAAG,OAAO;EAC/B,KAAK,YAAY;EACjB,OAAO;CACT;;;;;;;;;;;CAYA,MAAM,SAAS,OAAO,WAAsB,YAAkC;EAC5E,MAAM,OAAO,MAAM,IAAI,SAAS;EAChC,IAAI,SAAS,KAAA,KAAa,KAAK,eAAe,SAAS;;EAEvD,MAAM,cAAuB,MAAM,IAAI,SAAS,MAAM,QAAQ,KAAK,eAAe;EAClF,MAAM,UAAU,IAAI,SAAS,IAAI,SAAS;EAC1C,IAAI;GACF,IAAI,YAAY,KAAA,GAAW;IAIzB,MAAM,OAAO,WAAW,MAAM,SAAS;IACvC;GACF;GAGA,MAAM,OAAO,WAAW,MAAM,SAAS;GACvC,IAAI,CAAC,MAAK,GAAI;GACd,IAAI;IACF,MAAM,IAAI,SAAS,MAAM,OAAO;GAClC,SAAS,OAAgB;IACvB,IAAI,CAAC,MAAK,GAAI;IACd,IAAI,OAAO,KAAK,4BAA4B,UAAS,KAAM,cAAc,KAAK,GAAG;IACjF,MAAM,OAAO,WAAW,MAAM,cAAc;IAC5C;GACF;GACA,IAAI,CAAC,MAAK,GAAI;GACd,MAAM,OAAO,WAAW,MAAM,MAAM;EACtC,UAAU;GAGR,IAAI,MAAM,IAAI,SAAS,MAAM,QAAQ,KAAK,UAAU,YAAY,KAAK,YAAY;EACnF;CACF;;CAGA,MAAM,QAAQ,cAA8B;EAC1C,QAAQ,SAAS;EACjB,IAAI,CAAC,QAAQ,SAAS,GAAG;EACzB,MAAM,UAAU,MAAM,IAAI,SAAS,CAAC,EAAE;EACtC,IAAI,YAAY,KAAA,GAAW,OAAY,WAAW,OAAO;CAC3D;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAM,QAAQ,OACZ,WACA,QACA,iBAAiB,UACO;EACxB,IAAI,IAAI,SAAS,IAAI,SAAS,MAAM,KAAA,GAAW,OAAO;EACtD,IAAI,CAAC,kBAAkB,CAAC,MAAM,mBAAmB,KAAK,WAAW,MAAM,GAAG,OAAO;EACjF,IAAI,CAAC,KAAK,MAAM,SAAS,GAAG,OAAO;EACnC,MAAM,OAAiB;GAAE,OAAO;GAAY,YAAY,EAAE;GAAY,QAAQ;GAAS,WAAW;EAAK;EACvG,MAAM,IAAI,WAAW,IAAI;EACzB,IAAI,MAAM,OAAO,WAAW,MAAM,UAAU,GAAG;GAG7C,OAAO,OAAO,SAAS;GACvB,OAAO;EACT;EACA,KAAK,SAAS;EACd,KAAK,OAAO,SAAS;EACrB,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,MAAM,YAAY,OAAO,WAAsB,WAAmD;EAIhG,IAAI,IAAI,SAAS,IAAI,SAAS,MAAM,KAAA,GAClC,OAAO;GAAE,SAAS;GAAG,YAAY;GAAG,gBAAgB;GAAO,YAAY,CAAA;GAAI,QAAQ;EAAK;EAE1F,MAAM,OAAO,MAAM,gBAAgB,KAAK,WAAW,MAAM;EAKzD,IAAI,KAAK,aAAa;GACpB,MAAM,UAAU,MAAM,MAAM,WAAW,MAAM;GAK7C,IAAI,YAAY,cAAc,KAAK,SAAS;GAC5C,YAAY,eAAe,WAAW,YAAY,cAAc;GAChE,OAAO;IACL,SAAS,YAAY,eAAe,IAAI;IACxC,YAAY,YAAY,eAAe,IAAI;IAC3C,gBAAgB,YAAY;IAC5B,YAAY,YAAY,iBAAiB,CAAC,SAAS,IAAI,CAAA;IACvD,QAAQ;;EAEZ;EACA,MAAM,aAA0B,CAAA;EAChC,MAAM,aAA0B,CAAA;EAChC,MAAM,UAAU,KAAK,QAAQ,QAAO,WAAU,OAAO,SAAS,aAAa;EAC3E,KAAK,MAAM,MAAM,CAAC,WAAW,GAAG,QAAQ,KAAI,WAAU,OAAO,EAAE,CAAC,GAAG;GAKjE,MAAM,UAAU,MAAM,MAAM,IAAI,QAAQ,OAAO,SAAS;GACxD,IAAI,YAAY,cAAc;IAC5B,WAAW,KAAK,EAAE;IAIlB,KAAK,EAAE;GACT,OAAO,IAAI,YAAY,gBACrB,WAAW,KAAK,EAAE;GAKpB,YAAY,eAAe,IAAI,YAAY,cAAc;EAC3D;EACA,OAAO;GACL,SAAS,WAAW,SAAS,WAAW;GACxC,YAAY,WAAW;GACvB,gBAAgB,WAAW,SAAS,SAAS;GAC7C;GACA,QAAQ;;CAEZ;CAEA,MAAM,QAAQ,cAA8B;EAC1C,IAAI,CAAC,KAAK,SAAS,SAAS,GAAG;GAC7B,KAAK,SAAS;GACd;EACF;EAGA,QAAQ,SAAS;CACnB;;;;;;;CAQA,MAAM,UAAU,OAAO,WAAsB,SAAwC;EACnF,IAAI,CAAC,KAAK,SAAS,SAAS,GAAG,OAAO;GAAE,MAAM;GAAW;EAAI;EAC7D,IAAI;GACF,MAAM,SAAS,OAAO,SAAS;EACjC,SAAS,OAAgB;GACvB,IAAI,OAAO,KAAK,0BAA0B,UAAS,6BAA8B,cAAc,KAAK,GAAG;GACvG,OAAO;IAAE,MAAM;IAAS,MAAM;GAAe;EAC/C;EACA,KAAK,OAAO,SAAS;EACrB,OAAO;GAAE,MAAM;GAAW;EAAI;CAChC;CAKA,MAAM,aAAyB,kBAAkB,KAAK,MAAM,UAAU,SAAS;EAC7E;EACA,UAAS,cAAa,MAAM,IAAI,SAAS,CAAC,EAAE,WAAW;EACxD;CAKD,MAAM,SAA4B,gBAAgB,KAAK,UAAU,YAAY,QAAQ,kBAAkB;;;;;;;;CASvG,MAAM,UAAU,WAAyB,OAAO,eAAyD;EACvG,IAAI,YAAY,GAAG,GAAG,OAAO;GAAE,MAAM;GAAS,MAAM;EAAe;EACnE,MAAM,SAAS,WAAW,SAAS,KAAI;EACvC,IAAI,OAAO,WAAW,GAAG,OAAO;GAAE,MAAM;GAAS,MAAM;EAAsB;EAC7E,OAAO,MAAM,OAAO,OAAO,WAAW,MAAM,IAAI,UAAU,MAAM,GAAG,QAAQ,WAAW,MAAM;CAC9F;CAIA,MAAM,cAA2B,iBAAiB,KAAK,OAAO,UAAU;EACtE,WAAU,cAAa,KAAK,SAAS,SAAS;EAC9C;EACA;EACA,YAAY,IAAI,IAAyB,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,WAAW,UAAU,CAAC,WAAW;GACzF,OAAO,KAAK;GAKZ,QAAQ,KAAK,WAAW,WAAW,KAAK,UAAU,kBAAkB,IAAI,SAAS,IAAI,SAAS,MAAM,KAAA;GACrG,CAAC,CAAC;EACH,cAAc;EACf;CAID,IAAI,OAAO,aAAS;EAClB,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,mBAAmB;GACrD,MAAM;GACN,aAAa;GACb,SAAS,OAAO,eAAc;IAC5B,IAAI,YAAY,GAAG,GAAG,OAAO;KAAE,MAAM;KAAS,MAAM;IAAe;IACnE,MAAM,YAAY,WAAW,MAAM;IACnC,MAAM,UAAU,MAAM,UAAU,WAAW,WAAW,MAAM;IAC5D,IAAI,QAAQ,WAAW,SAAS,GAC9B,OAAO;KAAE,MAAM;KAAS,MAAM,mBAAmB,QAAQ,UAAU;IAAC;IAEtE,IAAI,CAAC,QAAQ,gBACX,OAAO;KAAE,MAAM;KAAS,MAAM;IAAuB;IAEvD,MAAM,SAAS,QAAQ,iBAAiB,QAAQ,aAAa,IAAI,QAAQ;IAGzE,IAAI,QAAQ,QAAQ,OAAO;KAAE,MAAM;KAAW,MAAM,GAAG,WAAW,MAAM,EAAC,GAAI;IAAkB;IAI/F,OAAO;KAAE,MAAM;KAAW,MAAM,WAAW,MAAM;IAAC;GACpD;GACD;EAID,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,wBAAwB;GAC1D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,eAAgB,YAAY,GAAG,IAC3C;IAAE,MAAM;IAAS,MAAM;GAAe,IACtC;IAAE,MAAM;IAAW,MAAM,gBAAgB,MAAM,WAAW,aAAa,WAAW,MAAM,CAAC;GAAC;GAC/F;EAGD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,uBAAuB;GACzD,MAAM;GACN,aAAa;GACb,SAAS,OAAO,eAAgB,YAAY,GAAG,IAC3C;IAAE,MAAM;IAAS,MAAM;GAAe,IACtC,YAAY,SAAS,WAAW,MAAM;GAC3C;EAKD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,uBAAuB;GACzD,MAAM;GACN,aAAa;GACb,SAAS,OAAO,eAAe,YAAY,WAAW,WAAW,MAAM;GACxE;EAOD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,oBAAoB;GACtD,MAAM;GACN,aAAa;GACb,SAAS,OAAO,gBAAgB;IAAE,MAAM;IAAW,MAAM,iBAAiB,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI,WAAW,MAAM,CAAC;GAAC;GACrI;EACD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,6BAA6B;GAC/D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,UAAU;GAC3B;EACD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,yBAAyB;GAC3D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,MAAM;GACvB;EACD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,2BAA2B;GAC7D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,QAAQ;GACzB;EACD,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,0BAA0B;GAC5D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,OAAO;GACxB;EAID,MAAM,IAAI,SAAS,SAAS;GAC1B,cAAc,oBAAoB,yBAAyB;GAC3D,MAAM;GACN,aAAa;GACb,SAAS,OAAO,MAAM;GACvB;EACD,MAAM,KAAK,UAAS,cAAY;GAAG,KAAK,SAAS;EAAE,CAAC;EAIpD,MAAM,KAAK,QAAO,cAAY;GAC5B,qBAAoB;IAAG,KAAK,SAAS;GAAE,CAAC;EAC1C,CAAC;EACD,MAAM,MAAM,UAAS,cAAY;GAAG,KAAK,SAAS;EAAE,CAAC;EAGrD,MAAM,IAAI,GAAG,kBAAkB,EAAE,YAAW;GAC1C,KAAK,MAAM,EAAE;EAEf,CAAC;EAGD,MAAM,IAAI,GAAG,mBAAmB,EAAE,YAAW;GAC3C,QAAQ,OAAO,MAAM,EAAE;EAEzB,CAAC;EACD,MAAM,YAAW;GACf,MAAM,UAAU,CAAC,GAAG,QAAQ,OAAM,CAAE,CAAC,CAAC,KAAI,WAAU,OAAO,MAAM,QAAO,CAAE;GAC1E,QAAQ,MAAK;GACb,MAAM,MAAK;GACX,MAAM,QAAQ,IAAI,OAAO;EAC3B;CACF,GAAG,wBAAwB;CAE3B,OAAO,EACL,OAAO,YAAW;EAChB,KAAK,MAAM,SAAS,SAAS,KAAI,GAAI;GAOnC,IAAI,CAAC,MAAM,gBAAgB,KAAK,MAAM,SAAS,GAAG;IAChD,OAAO,IAAI,MAAM,SAAS;IAC1B,IAAI,OAAO,KAAK,kCAAkC,MAAM,UAAS,kDACxB;IACzC;GACF;GACA,IAAI,KAAK,MAAM,MAAM,SAAS,GAC5B,MAAM,IAAI,MAAM,WAAW;IACzB,OAAO,MAAM;IACb,YAAY,EAAE;IACd,QAAQ;IACR,WAAW;IACZ;GAEH,QAAQ,MAAM,SAAS;EACzB;EAIA,KAAK,MAAM,SAAS,IAAI,OAAO,KAAI,GAAI,KAAK,MAAM,EAAE;CACtD,EAAA;AAEJ;;;;;;;;;;;;;;;AC14BA,MAAa,kBAAkB;;AAuB/B,SAAS,WAAW,QAAmB;CACrC,OAAO,OAAO,kBAAkB,QAAQ,OAAO,yBAAS,IAAI,MAAM,mCAAmC;AACvG;;AAGA,SAAS,QAAK;CACZ,IAAI,aAAwB,CAAE;CAE9B,OAAO;EAAE,SAAA,IADW,SAAe,YAAW;GAAG,OAAO;EAAQ,CACvD;EAAS;CAAI;AACxB;;;;;;AAOA,SAAS,YAAY,UAAyB,SAA+B;CAC3E,MAAM,OAAO,MAAK;CAClB,MAAM,gBAAqB;EAAG,KAAK,KAAI;CAAG;CAC1C,MAAM,WAAW,QAAQ,QAAO,WAAU,CAAC,OAAO,OAAO;CACzD,IAAI,SAAS,WAAW,QAAQ,QAAQ,KAAK,KAAI;CACjD,KAAK,MAAM,UAAU,UAAU,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAI,CAAE;CACvF,OAAO,QAAQ,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,cAAa;EACzD,KAAK,MAAM,UAAU,UAAU,OAAO,oBAAoB,SAAS,OAAO;CAC5E,CAAC;AACH;;AASA,IAAa,YAAb,MAAsB;CASS;;CAPZ,yBAAS,IAAI,IAAG;CAChB,4BAAY,IAAI,IAAG;CACnB,gCAAgB,IAAI,IAAG;;;;CAKxC,YAA6B,SAAyB;EAAzB,KAAA,UAAA;CAA4B;;;;;;CAOzD,SAAS,UAAyD;EAChE,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAY;GAAG,KAAK,UAAU,OAAO,QAAQ;EAAE;CACjD;;;;;;;;;;CAWA,OAAO,UAAuD;EAC5D,KAAK,cAAc,IAAI,QAAQ;EAC/B,aAAY;GAAG,KAAK,cAAc,OAAO,QAAQ;EAAE;CACrD;;;;;CAMA,SAAS,WAAoB;EAC3B,OAAO,KAAK,OAAO,IAAI,SAAS;CAClC;;;;;;;CAQA,OAAO,WAAoB;EACzB,QAAQ,KAAK,OAAO,IAAI,SAAS,CAAC,EAAE,WAAW,KAAK;CACtD;;;;;;;CAQQ,MAAM,OAAY;EACxB,MAAM,WAAW;EACjB,KAAK,WAAW,MAAM,WAAW,IAAI;EACrC,aAAY;GACV,MAAM,WAAW;GACjB,IAAI,MAAM,YAAY,GAAG,KAAK,WAAW,MAAM,WAAW,KAAK;EACjE;CACF;;;;;;;;CASA,MAAM,WAAoB;EACxB,IAAI,KAAK,OAAO,IAAI,SAAS,GAAG,OAAO;EACvC,MAAM,UAAU,MAAK;EACrB,KAAK,OAAO,IAAI,WAAW;GAAE;GAAW,UAAU,QAAQ;GAAS,SAAS,QAAQ;GAAM,SAAS;EAAC,CAAE;EACtG,KAAK,OAAO,WAAW,IAAI;EAC3B,OAAO;CACT;;;;;;;;CASA,OAAO,WAAoB;EACzB,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;EACvC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,KAAK,OAAO,OAAO,SAAS;EAC5B,MAAM,QAAO;EACb,KAAK,OAAO,WAAW,KAAK;EAC5B,OAAO;CACT;;;;;;;;CASA,KAAK,SAAwB;EAI3B,IAAI,QAAQ,YAAY,KAAA,GAAW,OAAO,KAAA;EAC1C,MAAM,YAAY,QAAQ;EAC1B,IAAI,cAAc,KAAA,GAAW,OAAO,KAAA;EACpC,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;EACvC,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;EAChC,MAAM,OAAO,KAAK,MAAM,KAAK;EAK7B,OAAO;GAAE,MAJI,YACX,MAAM,UACN,QAAQ,WAAW,KAAA,IAAY,CAAC,KAAK,QAAQ,QAAQ,IAAI,CAAC,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,CAExF;GAAM;EAAI;CACrB;;;;;;;;;;;;;;CAeS,UAAU,SAA0B,SAAsE;EACjH,MAAM,OAAO;EACb,QAAQ,gBAAgB,QAAK;GAC3B,MAAM,OAAO,KAAK,KAAK,OAAO;GAC9B,IAAI;IACF,IAAI,SAAS,KAAA,GAAW,MAAM,KAAK;IAInC,IAAI,QAAQ,QAAQ,YAAY,MAAM,MAAM,WAAW,QAAQ,MAAM;IACrE,IAAI,KAAK,QAAQ,SAAS,SAAS,MAAM,IAAI,MAAM,eAAe;IAClE,OAAO,KAAI;GACb,UAAU;IAGR,MAAM,KAAI;GACZ;EACF,EAAA,CAAE;CACJ;CAEQ,OAAO,WAAsB,QAAe;EAClD,KAAK,MAAM,YAAY,CAAC,GAAG,KAAK,SAAS,GAAG,SAAS,WAAW,MAAM;CACxE;CAEQ,WAAW,WAAsB,MAAa;EACpD,KAAK,MAAM,YAAY,CAAC,GAAG,KAAK,aAAa,GAAG,SAAS,WAAW,IAAI;CAC1E;;;;;;;;;;;;;;;;;;;;;;;;;AC9MF,MAAM,iBAAiB,EAAE,OAAO;;CAE9B,QAAQ,EAAE,OAAM;;CAEhB,MAAM,EAAE,OAAM;CACf;;;;;AASD,MAAM,yBAA6F;CACjG,KAAK;CACL,aAAa,EAAE,MAAM,cAAc;CACnC,YAAY,CAAA;CACZ,QAAQ,OAAO,UAAS;EACtB,IAAI,MAAM,SAAS,aACjB,OAAO,CAAC,GAAG,OAAO;GAAE,QAAQ,OAAO,MAAM,KAAK,MAAM;GAAG,MAAM,MAAM,KAAK;EAAI,CAAE;EAEhF,IAAI,MAAM,SAAS,eAAe;GAChC,MAAM,SAAS,OAAO,MAAM,KAAK,QAAQ,UAAU;GACnD,MAAM,KAAK,MAAM,WAAU,SAAQ,KAAK,WAAW,MAAM;GACzD,IAAI,KAAK,GAAG,OAAO;GACnB,OAAO,CAAC,GAAG,MAAM,MAAM,GAAG,EAAE,GAAG,GAAG,MAAM,MAAM,KAAK,CAAC,CAAC;EACvD;EACA,OAAO;CACT;CACA,cAAc;;;AA4BhB,SAAS,SAAS,KAAc,SAAgB;CAC9C,OAAO,IAAI,OAAO,eAAe,OAAO,CAAC,CAAC,qBAAqB;AACjE;;;;;;AAOA,eAAsB,eAAe,KAAY;CAC/C,MAAM,WAAW,MAAM,IAAI,mBAAmB,SAAS,sBAAsB;CAC7E,MAAM,4BAAY,IAAI,IAAG;CAEzB,MAAM,UAAU,cAA8B;EAC5C,KAAK,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,SAAS,SAAS;CAC3D;CAEA,IAAI,aAAa,IAAI,GAAG,kBAAkB,SAAS,UAAS;EAC1D,QAAQ,MAAM,MAAd;GACE,KAAK;GACL,KAAK;IAGH,OAAO,QAAQ,EAAE;IACjB;GACF,KAAK;GACL,KAAK;IAKH,OAAO,QAAQ,EAAE;IACjB;GACF,SACE;EACJ;CACF,CAAC,GAAG,iCAAiC;CAErC,OAAO;EACL,WAAW,cAAa;GACtB,MAAM,UAAU,IAAI,SAAS,IAAI,SAAS;GAC1C,IAAI,YAAY,KAAA,GAGd,OAAO;IAAE,UAAU;IAAO,cAAc,CAAA;GAAE;GAE5C,OAAO;IACL,UAAU,SAAS,KAAK,OAAO;IAC/B,cAAc,SAAS,QAAQ,OAAO,CAAC,CAAC,KAAI,SAAQ,KAAK,IAAI;;EAEjE;EACA,WAAW,aAAY;GACrB,UAAU,IAAI,QAAQ;GACtB,aAAY;IAAG,UAAU,OAAO,QAAQ;GAAE;EAC5C;;AAEJ;;;;;;;;ACpFA,MAAa,qBAAqB,aAAa;CAC7C,MAAM;CACN,SAAS;CACT,QAAQ,EAAE,UAAU,YA/BI,EAAE,OAAO;;EAEjC,OAAO,EAAE,KAAK,WAAW;;EAEzB,WAAW,EAAE,OAAM,CAAE,CAAC,IAAG,CAAE,CAAC,YAAW;;;;;;;;;;;EAWvC,WAAW,EAAE,QAAO,CAAE,CAAC,QAAQ,IAAI;EAgBoB,CAAU,EAAC;CACnE;;;;;;;;;;AAiED,eAAsB,iBAAiB,KAAY;CACjD,MAAM,SAA4C,MAAM,IAAI,cAAc,KAAK,kBAAkB;CACjG,IAAI,mBAAmB,OAAO,MAAK,GAAI,iBAAiB;CACxD,MAAM,WAAW,OAAO,MAAM,UAAU;;CAExC,IAAI,aAAuC,CAAA;CAC3C,IAAI;CACJ,MAAM,WAAW,cAAmD;EAClE,MAAM,SAAS,SAAS,IAAI,SAAS;EACrC,OAAO,WAAW,KAAA,IACd,KAAA,IACA;GAAE;GAAW,OAAO,OAAO;GAAO,WAAW,OAAO;GAAW,WAAW,OAAO;EAAS;CAChG;CACA,MAAM,mBAA6C,CAAC,GAAG,SAAS,QAAO,CAAE,CAAC,CAAC,KAAK,CAAC,WAAW,aAAa;EACvG;EACA,OAAO,OAAO;EACd,WAAW,OAAO;EAClB,WAAW,OAAO;GAClB;CACF,OAAO;EACL,MAAM;EACN,IAAI,SAAM;GACR,MAAM,UAAU,WAAU;GAG1B,IAAI,WAAW,KAAA,KAAa,QAAQ,WAAW,WAAW,UACrD,QAAQ,OAAO,OAAO,OAAO,UAAU,WAAW,GAAG,GAAG,OAAO;GACpE,aAAa;GACb,SAAS,IAAI,IAAI,QAAQ,KAAI,UAAS,CAAC,MAAM,WAAW,KAAK,CAAC,CAAC;GAC/D,OAAO;EACT;EACA,KAAK;EACL,QAAQ,OAAO,WAAW,UAAS;GACjC,MAAM,YAAY,KAAK,IAAG;GAI1B,IAAI,SAAS,IAAI,SAAS,MAAM,KAAA,GAC9B,IAAI;IACF,MAAM,SAAS,OAAO,YAAW,aAAY;KAAE,GAAG;KAAS;KAAO;IAAS,EAAG;IAC9E;GACF,SAAS,OAAgB;IAGvB,IAAI,EAAE,iBAAiB,gBAAgB,MAAM,SAAS,eAAe,MAAM;GAC7E;GAIF,MAAM,SAAS,IAAI,WAAW;IAAE;IAAO;IAAW,WAAW;GAAI,CAAE;EACrE;EACA,cAAc,OAAO,WAAW,cAAa;GAI3C,MAAM,SAAS,OAAO,YAAW,aAAY;IAAE,GAAG;IAAS;IAAW,WAAW,KAAK,IAAG;GAAE,EAAG;EAChG;EACA,QAAQ,OAAO,cAAa;GAC1B,MAAM,SAAS,OAAO,SAAS;EACjC;;AAEJ;;;;;;;;;;;;;;;;;;ACrKA,MAAa,OAAO;;;;;;AAOpB,MAAa,SAAS;CAAC;CAAO;CAAY;CAAU;CAAY;CAAsB;AAAe;;;;;;;;;;;;;AAcrG,MAAa,6BAA6B,CAAC,qBAAqB,kBAAkB;;AAGlF,MAAM,cAAc,EAAE,OAAO;;CAE3B,kBAAkB,EAAE,MAAM,EAAE,OAAM,CAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,0BAA0B,CAAC;;;;;;;;;CAS7E,oBAAoB,EAAE,OAAM,CAAE,CAAC,IAAG,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,qBAAqB,CAAC,CAAC,QAAQ,oBAAoB;CACpG;;;;;;AAUD,MAAa,SAAS,EAAE,YAAW,UAAS,SAAS,CAAA,GAAI,WAAW;;;;;;;;;;AAWpE,eAAsB,MAAM,KAAc,SAAiB,OAAO,MAAM,CAAA,CAAE,GAAC;CACzE,MAAM,WAAW,MAAM,iBAAiB,GAAG;CAC3C,MAAM,WAAW,IAAI,gBAAe;CAGpC,IAAI,mBAAkB;EAAG,SAAS,sBAAM,IAAI,MAAM,wBAAwB,CAAC;CAAE,GAAG,iBAAiB;CACjG,MAAM,OAAO,IAAI,UAAU,EAAE,UAAU,SAAS,OAAM,CAAE;CACxD,IAAI,aACI,IAAI,GAAG,eAAe,SAAS,SAAS,KAAK,OAAO,SAAS,IAAI,CAAC,GACxE,mBAAmB;CAOrB,MAJgB,sBAAsB,KAAK,MAAM,MAD7B,eAAe,GAAG,GACkB,UAAU;EAChE,kBAAkB,OAAO;EACzB,oBAAoB,OAAO;EAEvB,CAAO,CAAC,MAAK;AACrB"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/history.ts","../src/shared.ts","../src/coldlog.ts","../src/tree.ts","../src/global.ts","../src/release-order.ts","../src/composition.ts","../src/resume-text.ts","../src/resume-activation.ts","../src/resume.ts","../src/triage.ts","../src/snapshot.ts","../src/park-state.ts","../src/control.ts","../src/gate.ts","../src/park.ts","../src/remote.ts","../src/remote-adapter.ts","../src/registry.ts","../src/index.ts"],"sourcesContent":["/**\n * The message history a session's log derives to, and the one rule that reads\n * whether it still owes the model an answer.\n *\n * That question is asked twice and must have one answer: the clean wake asks it\n * before it hands a session an input (an input nobody owes an answer to would\n * open a turn of its own), and the decision list publishes it as the row's\n * `wake` field. A published promise that the wake then declines to keep is the\n * report lying, so both read the same derivation and the same rule.\n *\n * The derivation is the harness's own: the surface fold the loop reads its\n * history through, with the same message projections this process has\n * registered, and an interrupted tail closed exactly as the harness closes it\n * on the next read. `resume.ts` reads the live form of it\n * (`Session.deriveMessages()`); {@link derivedHistory} is the cold form over a\n * stored log, which is all the decision list may claim — it opens a log to read\n * it and never activates a session.\n * @module dsh-pause-session/history\n */\n\nimport type { Message } from '@deepseek-ai/dsh-llm'\nimport {\n deriveEventMessage, foldSurface, foldRequestHeader, interruptedTurnClosers,\n} from '@deepseek-ai/dsh-session'\nimport type { SessionEvent, SessionMessageProjection } from '@deepseek-ai/dsh-session'\n\n/**\n * Whether one derived history ends owing the model an answer: the last thing\n * said on the model-visible surface is not an assistant message.\n * @param messages - a derived history, in model-visible order.\n * @returns whether a model still owes that history a reply.\n */\nexport function owesReply(messages: readonly Message[]): boolean {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]\n // System and developer entries are request framing, not conversation.\n if (message === undefined || message.role === 'system' || message.role === 'developer') continue\n return message.role !== 'assistant'\n }\n return false\n}\n\n/**\n * The messages one stored log derives to, as the harness would read them on the\n * session's next activation: the open tail turn is closed with the harness's\n * own synthetic results first, then the log is folded into its surface.\n * @param events - the stored events, exactly as they are stored and in sequence order.\n * @param projections - this process's registered message projections.\n * @returns the derived history, in model-visible order.\n * @throws when the log cannot be folded — an event this build has no interpreter\n * for, or surface metadata that does not apply to this history.\n */\nexport function derivedHistory(\n events: readonly SessionEvent[],\n projections: readonly SessionMessageProjection[],\n): Message[] {\n const stored = [...events, ...interruptedTurnClosers(events)]\n const fold = foldSurface(stored, projections)\n return fold.nodes.flatMap((seq) => {\n const event = stored[seq]\n if (event === undefined) return []\n const message = deriveEventMessage(event, fold.projectedMessages)\n return message === null ? [] : [message]\n })\n}\n\n/**\n * Whether one stored log names the route a wake would be addressed to. The wake\n * needs a provider and a model to build a request at all, and the request\n * header the session itself persisted is the only place that names them.\n * @param events - the stored events, in sequence order.\n * @returns whether the log's last request header carries both route fields.\n */\nexport function routeNamed(events: readonly SessionEvent[]): boolean {\n const config = foldRequestHeader(events)?.config\n return config !== undefined && config.provider.length > 0 && config.model.length > 0\n}\n","/**\n * The identities both halves of dsh-pause-session name, the phase vocabulary\n * they 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 native Remote payloads are the live\n * vocabulary; there is no command encoding or slash-command surface here.\n * @module dsh-pause-session/shared\n */\n\n/** Result copy for a press or a release the durable registry refused to record. */\nexport const UNRECORDED_TEXT = 'pause could not write the pause registry, so it did not change the pause state.'\n\n/**\n * One user-facing answer from an action. The controls render `text` verbatim;\n * `kind` only says whether the press succeeded.\n */\nexport interface PauseAnswer {\n readonly kind: 'success' | 'error'\n readonly text: string\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/** 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/** One JSON snapshot: the whole-app verdict plus every live paused session. */\nexport interface PauseSnapshot {\n readonly global: GlobalStatus\n readonly sessions: Readonly<Record<string, ParkStatus>>\n}\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 on the native stream 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/** 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 * Reading one parked session's stored log through a bound, and the facts the\n * morning list may claim from what it read.\n *\n * The decision list reads the same log the wake reads, but for the opposite\n * reason: the wake has to reproduce the session, so it reads everything. The\n * list only has to name where the session stopped, which tool call the log\n * never heard back from, and whether an answer is still owed — all of them\n * facts about the log's END. Reading from seq 0 to the end for every row, on\n * every time the panel opens, would make a long\n * session's cost grow with a log nobody is asking about.\n *\n * WHAT THE BOUND BUYS. A stored log is append-only, so the tail is stable: the\n * read takes the last {@link TRIAGE_WINDOW_EVENTS} events, or the whole log when\n * it is shorter, and the answer is derived from that. The window is complete\n * whenever the log fits inside it, which is the common case; the fold the rest\n * of this plugin reads history through is then the harness's own, over exactly\n * the events the harness would fold.\n *\n * WHAT THE TAIL ALONE PROVES. A truncated window still answers the list's\n * questions, for one reason the harness guarantees: a dangling tool call exists\n * only inside the step that is still open, because a step's end is written\n * after its results. So an unanswered call is always inside the final open\n * span, and the tail that contains that span contains every one of them. What\n * the window cannot prove — a log a replacement event rewrote from further back\n * — is not guessed at: {@link logFacts} reports `undefined` and the row says\n * `unreadable`, because a list that invents a stop position is worse than a\n * list that says it could not read one.\n * @module dsh-pause-session/coldlog\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { deriveEventMessage, foldSurface } from '@deepseek-ai/dsh-session'\nimport type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` read seam and its handle.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport { derivedHistory, owesReply, routeNamed } from './history.js'\nimport { describeError } from './shared.js'\nimport type { TriageTrace } from './shared.js'\n\n/**\n * Events the read looks at before it gives up. Sized to hold a whole final\n * step — the model exchange, its tool calls, and their results — with room to\n * spare, at a cost that does not grow with the session.\n */\nexport const TRIAGE_WINDOW_EVENTS = 2000\n\n/**\n * The longest log the read will walk while locating where its tail starts.\n *\n * The window's start is found by probing for the log's length, so a log longer\n * than this cannot be located from its end at a cost worth paying for a panel\n * left open. Such a log — a million stored events — is reported unreadable\n * rather than read from the front, which is the read this module exists to\n * avoid.\n */\nexport const TRIAGE_LOG_MAX_EVENTS = 1_000_000\n\n/**\n * Events that are not the session's own story: the bookkeeping of the pause\n * legacy command records a previous surface wrote, and the pending-input\n * splices the inbox writes. They are real and durable, but \"it stopped right\n * after the action finished\"\n * is not what \"where did it stop\" means.\n */\nconst BOOKKEEPING_EVENTS: ReadonlySet<string> = new Set(['command/run', 'command/done', 'agent/inbox/spliced'])\n\n/** How one stored log was seen by the bounded read. */\nexport type ColdLog =\n /** The events the read returned, and whether they are the whole stored log. */\n | { readonly kind: 'read'; readonly events: readonly SessionEvent[]; readonly complete: boolean }\n /** The log could not be opened or read, or it is longer than this read will walk. */\n | { readonly kind: 'unreadable' }\n\n/** The facts the decision list claims, all of them read off the log's end. */\nexport interface ColdFacts {\n /** What the stored log's last event was, and the turn/step it belongs to. */\n readonly last: SessionEvent | undefined\n /** Tool calls the stored events record and no stored result answers. */\n readonly traces: readonly TriageTrace[]\n /** Whether the stored log ends owing the model an answer. */\n readonly owed: boolean\n /** Whether the log names the route a wake would be addressed to. */\n readonly wakeable: boolean\n}\n\n/**\n * The turn and step an event belongs to, when it carries them.\n * @param event - one stored event.\n * @returns the position fields the event has, and nothing for the fields it does not.\n */\nexport function positionOf(event: SessionEvent): { readonly turn?: number; readonly step?: number } {\n const data = event.data\n if (typeof data !== 'object' || data === null) return {}\n const turn = 'turn' in data && typeof data.turn === 'number' ? data.turn : undefined\n const step = 'step' in data && typeof data.step === 'number' ? data.step : undefined\n return { ...turn === undefined ? {} : { turn }, ...step === undefined ? {} : { step } }\n}\n\n/**\n * Every tool call the STORED events record and no stored result answers.\n *\n * Deliberately the stored prefix rather than the repaired view: what a row\n * reports is the state of the session's own log, and the harness's synthetic\n * unknown-outcome results are exactly what that log is missing. Taking the\n * repaired view here would subtract the very traces the row exists to show.\n *\n * A call is answered only by a result the SAME window holds, which is what the\n * harness's own repair rule reads too: an unanswered call lives in the step\n * that is still open, so it is inside this window or it is answered.\n * @param events - the stored events the read returned, in sequence order.\n * @returns one trace per call with no result, in call order.\n */\nfunction unansweredCalls(events: readonly SessionEvent[]): TriageTrace[] {\n const names = new Map<string, string>()\n const answered = new Set<string>()\n for (const event of events) {\n if (event.type === 'tool/call') {\n names.set(String(event.data.callId), event.data.name)\n continue\n }\n if (event.type !== 'tool/result') continue\n answered.add(String(event.data.message.toolCallId))\n }\n return [...names].flatMap(([callId, tool]) => answered.has(callId) ? [] : [{ tool, callId }])\n}\n\n/**\n * Whether a log ends owing the model an answer, read from the tail alone.\n *\n * This is the harness's own rule — the last thing on the model-visible surface\n * is not an assistant message — read the way the harness reads it: the surface\n * fold over the events at hand, then the message the last node derives to. The\n * fold is handed a window rather than the whole log, so it refuses rather than\n * guesses when it meets a replacement it cannot resolve; a log it cannot fold\n * falls back to the last event that produces a message at all, which is the\n * same answer by a narrower route.\n * @param events - the stored events the read returned, in sequence order.\n * @param traces - the unanswered calls, as the row reports them.\n * @returns whether the session still owes the model a reply.\n */\nfunction tailOwesReply(events: readonly SessionEvent[], traces: readonly TriageTrace[]): boolean {\n // A log whose last step is open closes with a synthetic tool result for\n // every unanswered call, and a tool result is a message.\n if (traces.length > 0) return true\n const bySequence = new Map(events.map(event => [event.seq as number, event]))\n const lastNode = (): SessionEvent | undefined => {\n for (const seq of [...foldSurface(events).nodes].reverse()) {\n const event = bySequence.get(seq as number)\n if (event !== undefined) return event\n }\n return undefined\n }\n let anchor: SessionEvent | undefined\n try {\n anchor = lastNode()\n } catch {\n // The window cannot be folded as a log — an event it holds was rewritten\n // by something it does not. The narrower route still reads the same fact\n // off the end: the last event that derives a message.\n for (let at = events.length - 1; at >= 0; at -= 1) {\n const event = events[at]\n if (event === undefined || deriveEventMessage(event) === null) continue\n anchor = event\n break\n }\n }\n if (anchor === undefined) return false\n const message = deriveEventMessage(anchor)\n if (message === null) return false\n // System and developer entries are request framing, not conversation.\n if (message.role === 'system' || message.role === 'developer') return false\n return message.role !== 'assistant'\n}\n\n/**\n * The facts one bounded read of a stored log supports.\n *\n * A complete read is folded by the harness's own derivation, so the list and\n * the wake answer the \"does it still owe a reply\" question through one rule.\n * A truncated read answers from the tail, and only where the tail is proof: a\n * replacement event that rewrote a surface range from outside the window is\n * not visible here, so a log whose tail a later event could have rewritten is\n * reported unreadable rather than guessed at.\n * @param events - the stored events the read returned, in sequence order.\n * @param complete - whether those events are the whole stored log.\n * @param projections - this process's registered message projections.\n * @returns the row's facts, or `undefined` when this read cannot support them.\n */\nexport function logFacts(\n events: readonly SessionEvent[],\n complete: boolean,\n projections: Context['sessions']['messageProjections'],\n): ColdFacts | undefined {\n let last: SessionEvent | undefined\n for (let at = events.length - 1; at >= 0; at -= 1) {\n const event = events[at]\n if (event === undefined || BOOKKEEPING_EVENTS.has(event.type)) continue\n last = event\n break\n }\n const traces = unansweredCalls(events)\n const wakeableRoute = routeNamed(events)\n if (!complete) {\n // A truncated window has to hold the stop position, or it does not hold\n // the answer this row exists to publish: the log continued before what the\n // window saw, so \"no event to name\" would claim a session that left no\n // trace. That window is reported unreadable, not summarized.\n if (last === undefined) return undefined\n const owed = tailOwesReply(events, traces)\n return { last, traces, owed, wakeable: owed && wakeableRoute }\n }\n let owed: boolean\n try {\n owed = owesReply(derivedHistory(events, projections))\n } catch {\n return undefined\n }\n return { last, traces, owed, wakeable: owed && wakeableRoute }\n}\n\n/**\n * The one seam the length probe needs from an open handle: `read` at an offset\n * with a one-event length. It is the handle's own signature, narrowed here so a\n * reader can see that the probe never asks for a prefix it does not use.\n */\ninterface OffsetReader {\n /**\n * @param offset - first logical event seq to include.\n * @param length - maximum number of events to return.\n * @param options - cancellation for this read.\n * @returns a slice of the log, empty at or past its end.\n */\n read(\n offset?: number,\n length?: number,\n options?: { readonly signal?: AbortSignal },\n ): Promise<{ readonly events: readonly SessionEvent[] }>\n}\n\n/**\n * Whether one handle holds at least `count` events, asked without materializing\n * more than one of them.\n * @param handle - an open read handle onto the log.\n * @param count - the event count to test for.\n * @param signal - cancellation for the probe.\n * @returns whether the log reaches that far.\n */\nasync function reaches(handle: OffsetReader, count: number, signal?: AbortSignal): Promise<boolean> {\n return (await handle.read(count, 1, { ...signal === undefined ? {} : { signal } })).events.length > 0\n}\n\n/**\n * The stored log's own length, found by probing rather than by reading it.\n *\n * The handle's read seam is the only way to ask a backend anything, and it\n * takes an offset — so the count has to be probed for. The probe doubles until\n * it passes the end and then bisects, so a log of any length costs a handful\n * of one-event reads, and none of them materializes the prefix it skips.\n * @param handle - an open read handle onto the log.\n * @param signal - cancellation for the probe.\n * @returns the event count, or `undefined` when the log is longer than this read will walk.\n */\nasync function eventCount(handle: OffsetReader, signal?: AbortSignal): Promise<number | undefined> {\n let high = 1\n while (await reaches(handle, high, signal)) {\n if (high >= TRIAGE_LOG_MAX_EVENTS) return undefined\n high = Math.min(high * 2, TRIAGE_LOG_MAX_EVENTS)\n }\n let low = 0\n while (high - low > 1) {\n const mid = low + Math.floor((high - low) / 2)\n if (await reaches(handle, mid, signal)) low = mid\n else high = mid\n }\n // `reaches(n)` answers whether the event at zero-based index n exists; the\n // event count is one past the last existing index.\n return await reaches(handle, 0, signal) ? low + 1 : 0\n}\n\n/**\n * Read one session's stored log, bounded to the events the list's facts need.\n *\n * The window is the last `windowEvents` events, found by probing for the log's\n * length and then reading exactly that slice. A short log is returned whole and\n * marked complete; a log longer than {@link TRIAGE_LOG_MAX_EVENTS} is reported\n * unreadable without being read at all.\n * @param ctx - host context carrying the optional persistence service.\n * @param sessionId - the stored session to read.\n * @param windowEvents - how many events of the log's end one row may fold.\n * @param signal - cancellation for the open, probe, and read.\n * @returns the bounded read, or `unreadable` when no read seam exists, it refused, or the log is too long to walk.\n * @throws the cancellation reason when `signal` aborts the read.\n */\nexport async function coldRead(\n ctx: Context,\n sessionId: SessionId,\n windowEvents: number = TRIAGE_WINDOW_EVENTS,\n signal?: AbortSignal,\n): Promise<ColdLog> {\n const persistence = ctx.get('sessionPersistence')\n if (persistence === undefined) return { kind: 'unreadable' }\n let handle\n try {\n handle = await persistence.open(sessionId, 'read', {\n ...signal === undefined ? {} : { signal },\n })\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not open the stored log of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'unreadable' }\n }\n try {\n const window = Math.max(1, Math.floor(windowEvents))\n const count = await eventCount(handle, signal)\n if (count === undefined) {\n ctx.logger.warn(`pause: the stored log of \"${sessionId}\" is longer than this read will walk`)\n return { kind: 'unreadable' }\n }\n const start = Math.max(0, count - window)\n const stored = await handle.read(start, window, {\n ...signal === undefined ? {} : { signal },\n })\n return { kind: 'read', events: stored.events, complete: start === 0 }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the stored log of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'unreadable' }\n } finally {\n await handle.close()\n }\n}\n","/**\n * One session's subagent tree, read from the parent catalogs.\n *\n * \"This session's work\" includes the subagents it delegated to, nested ones\n * included, so the session-header press and the status the header shows both\n * need the same answer to \"which sessions hang under this one\".\n * `listDescendants` is the harness's own recursive walk of the parent-owned\n * catalogs, and it is the only source: the tree is read from the parents that\n * record it, never guessed from session ids.\n *\n * A walk is read, not remembered. The tree is a fact that grows — a delegation\n * in flight publishes its child after the parent's catalog was read — so a\n * press walks the tree at press time and a status read walks it at read time.\n * `@module dsh-pause-session/tree`\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.subagents` service and its descendant rows.\nimport type {} from '@deepseek-ai/dsh-subagent'\nimport { describeError } from './shared.js'\n\n/** One session a walk found under another, with the catalog's own mode for it. */\nexport interface TreeMember {\n readonly id: SessionId\n /**\n * The catalog's durable mode for this child. `external` is a child with no\n * local session to park, and `unsupported` is a mode this build cannot read;\n * the other two are the harness's own split between a route a restart can\n * resume (`continuable`) and one it cannot.\n */\n readonly mode: 'one-shot' | 'external' | 'continuable' | 'unsupported'\n}\n\n/** One branch a walk reached and could not read. */\nexport interface UnreadBranch {\n readonly id: SessionId\n /**\n * `corrupt` and `unavailable` mean the branch's own catalog was not read, so\n * the members under it are unknown. `unsupported` means the parent's catalog\n * records a mode this build cannot read; that branch was still traversed, and\n * what it holds is known — but no restart can resume through it.\n */\n readonly reason: 'corrupt' | 'unsupported' | 'unavailable'\n}\n\n/** What one session's tree walk found. */\nexport interface TreeWalk {\n /**\n * Catalog descendants in pre-order, direct children first and each child's\n * own children after it. A missing subagent service leaves this empty.\n */\n readonly members: readonly TreeMember[]\n /**\n * Branches the walk reached and could not fully read. A branch named here may\n * hold members nobody can see, so it is never read as \"no members\".\n */\n readonly unread: readonly UnreadBranch[]\n /**\n * Whether the walk itself failed, so even the branches are unknown. Named\n * separately from {@link unread}, which lists the branches of a walk that ran.\n */\n readonly unreadWhole: boolean\n}\n\n/**\n * Read the subagent tree under one session.\n *\n * A failure to read a branch is reported as unread rather than as an empty\n * tree: the difference is what keeps a press from claiming it registered a tree\n * it never saw, and keeps a status answer from calling itself safe behind an\n * unknown branch.\n * @param ctx - host context carrying the optional subagent service.\n * @param rootSessionId - the session whose descendants are requested.\n * @param signal - cancellation for the catalog walk.\n * @returns the descendants found and what of the walk could not be read.\n * @throws the cancellation reason when `signal` aborts the walk.\n */\nexport async function walkDescendants(\n ctx: Context,\n rootSessionId: SessionId,\n signal?: AbortSignal,\n): Promise<TreeWalk> {\n const subagents = ctx.get('subagents')\n if (subagents === undefined) return { members: [], unread: [], unreadWhole: false }\n try {\n const rows = await subagents.listDescendants(rootSessionId, signal)\n const members: TreeMember[] = []\n const unread: UnreadBranch[] = []\n for (const row of rows) {\n if (row.kind === 'child') {\n members.push({ id: row.id, mode: row.mode })\n continue\n }\n unread.push({ id: row.id, reason: row.reason })\n }\n return { members, unread, unreadWhole: false }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the subagent tree of \"${rootSessionId}\": ${describeError(error)}`)\n return { members: [], unread: [], unreadWhole: true }\n }\n}\n","/**\n * The whole-app park: one action that registers every running session tree,\n * and the aggregate answer to \"can I close the lid?\".\n *\n * The per-session control owns one session's park; this module owns the set.\n * It reads three real sources and never infers what they can say themselves:\n *\n * - `ctx.agents.list()` plus the park activity fold decide which sessions are\n * running. A session with no live agent is not running, and a session with\n * nothing unfinished — no tool in flight, no turn open, no delivery the loop\n * has not claimed — is left alone.\n * - The subagent runtime's descendant rows decide the tree and the one-shot\n * fact. A row's `mode` is the harness's own durable record of whether that\n * child can be resumed after a restart; a branch the walk could not read is\n * reported as unread rather than assumed clean.\n * - The job registry decides which background jobs belong to the registered\n * trees. Those run outside the agent loop: this plugin cannot stop them, so\n * it names them.\n *\n * The aggregate is derived in one place so the host's answer and the browser's\n * reading of it cannot disagree, and it is a promise about the durable\n * registry: a session is `safe` only when its recorded phase says so.\n * @module dsh-pause-session/global\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.jobs` service and its read-only projection.\nimport type {} from '@deepseek-ai/dsh-jobs'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport type { GlobalBlindSpot, GlobalStatus, GlobalVerdict, ParkPhase } from './shared.js'\nimport { walkDescendants } from './tree.js'\nimport type { TreeWalk } from './tree.js'\nimport type { PauseAnswer } from './shared.js'\n\n/**\n * How many times one press walks the trees again. A tree can grow while the\n * walk runs — a delegation already in flight publishes its child after the\n * parent's catalog was read — so the walk repeats until it stops finding a\n * running session this action has not pressed yet.\n */\nconst PRESS_ROUNDS = 4\n\n/** One session's live park state, as the per-session control sees it. */\nexport interface LivePark {\n readonly phase: ParkPhase\n /** Whether this process can still walk the session to a flushed park. */\n readonly moving: boolean\n}\n\n/** What one press of the per-session control did. */\nexport type PressResult = 'registered' | 'unrecordable' | 'not-live' | 'not-resumable' | 'already-paused'\n\n/** What the whole-app control needs from the per-session control. */\nexport interface GlobalPauseDeps {\n /**\n * @param sessionId - session to inspect.\n * @returns whether a press already owns this session.\n */\n isPaused(sessionId: SessionId): boolean\n /**\n * Press one session's pause and make it durable.\n * @param sessionId - session to register.\n * @param signal - cancellation for the resumability and registry reads.\n * @returns what the press did; only `registered` created a new park.\n */\n press(sessionId: SessionId, signal?: AbortSignal): Promise<PressResult>\n /**\n * Look at one session's mark now that it is paused.\n * @param sessionId - the session that was just pressed.\n */\n look(sessionId: SessionId): void\n /**\n * @returns every session a live mark owns, with the phase it reports now.\n */\n live(): ReadonlyMap<SessionId, LivePark>\n /**\n * @returns every registered session this process could not hold again — a\n * record whose session cannot be read here, so nothing in this process can\n * park it, release it, or act on it.\n */\n unheld(): ReadonlySet<SessionId>\n}\n\n/** The whole-app park, as the plugin entry drives it. */\nexport interface GlobalPause {\n /**\n * Register and park every running session tree in one action. Sessions this\n * pause already owns are left where they are; an idle session is not touched.\n * @param signal - cancellation for the tree reads this action performs.\n * @returns the aggregate as of this action's end.\n */\n pressAll(signal?: AbortSignal): Promise<PauseAnswer>\n /**\n * The aggregate as structured data for the native Remote stream.\n * @param signal - cancellation for the tree reads this read performs.\n * @returns the aggregate as of this read.\n */\n readState(signal?: AbortSignal, walks?: ReadonlyMap<SessionId, TreeWalk>): Promise<GlobalStatus>\n /**\n * Report one session a press could not register, so the aggregate names it.\n * The whole-app action records that fact for its own targets; a press of a\n * session's tree reports the members the registry refused through here.\n * @param sessionId - the session a press tried and failed to register.\n * @param unrecorded - whether it is now known to be unparked for that reason.\n */\n markUnrecorded(sessionId: SessionId, unrecorded: boolean): void\n}\n\n/** One tree walk as this aggregate reads it: the members, and the two ways a walk can be doubted. */\ninterface TreeReading {\n /** Catalog descendants of the walked session, direct children and deeper. */\n readonly members: readonly SessionId[]\n /** Whether any branch holds a route a restart cannot resume. */\n readonly unresumable: boolean\n /** Whether any branch could not be read, so the tree is not vouched for. */\n readonly unread: boolean\n /** Whether an unresumable member is still doing work. */\n readonly running: boolean\n}\n\n/** Fold one shared tree walk into the two doubts this report publishes. */\nfunction readingOf(walk: TreeWalk, watch: ParkWatch): TreeReading {\n return {\n members: walk.members.map(member => member.id),\n // A route no restart can resume, either because the catalog says so or\n // because it holds a mode this build cannot read.\n unresumable: walk.members.some(member => member.mode !== 'continuable')\n || walk.unread.some(branch => branch.reason === 'unsupported'),\n // `unsupported` was traversed, so its members are known; the other reasons\n // mean that branch was not read at all.\n unread: walk.unreadWhole || walk.unread.some(branch => branch.reason !== 'unsupported'),\n running: walk.members.some(member => {\n if (member.mode === 'continuable') return false\n const activity = watch.activity(member.id)\n return activity.turnOpen || activity.runningTools.length > 0\n }),\n }\n}\n\n/**\n * Mount the whole-app park.\n * @param ctx - host context carrying the agent store and the optional subagent\n * and job services.\n * @param watch - the activity fold that says whether a session has unfinished work.\n * @param registry - the durable list this aggregate is a promise about.\n * @param deps - the per-session press and the live phase of every mark.\n * @returns the one-press action and the aggregate read.\n */\nexport function mountGlobalPause(\n ctx: Context,\n watch: ParkWatch,\n registry: ParkRegistry,\n deps: GlobalPauseDeps,\n): GlobalPause {\n /**\n * Sessions this action tried to press and could not record. They are NOT\n * parked and they keep running, so the aggregate counts them and names them\n * instead of quietly leaving them out of a park it then calls safe.\n */\n const unrecorded = new Set<SessionId>()\n // Routes a whole-app press found running but could not register; named in the\n // aggregate so the user sees what the press did not stop.\n const notResumable = new Set<SessionId>()\n /** Record or clear one session's \"pressed and the registry refused\" state. */\n const markUnrecorded = (sessionId: SessionId, flag: boolean): void => {\n if (flag) unrecorded.add(sessionId)\n else unrecorded.delete(sessionId)\n }\n /** Per-episode tree walks; an entry lives exactly as long as the park owns it. */\n const walks = new Map<SessionId, TreeReading>()\n\n /**\n * Whether a session has unfinished work right now. Three real answers, any\n * of which is enough: the loop says it is driving the session, the activity\n * fold sees a tool in flight or a turn open, or the inbox holds a delivery\n * the loop has not claimed yet.\n */\n const running = (agent: Agent): boolean => {\n if (agent.status === 'running') return true\n const activity = watch.activity(agent.id)\n if (activity.turnOpen || activity.runningTools.length > 0) return true\n return agent.inbox.nextTurn.length + agent.inbox.nextStep.length > 0\n }\n\n /**\n * Read one session's subagent tree through the shared walker and remember the\n * reading for this park episode. The tree is a fact about the episode, not\n * about the moment it was asked, and the aggregate reads several sessions per\n * answer; the cache keeps that read from repeating a catalog round-trip it\n * has already paid for.\n */\n const walkTree = async (\n sessionId: SessionId,\n signal?: AbortSignal,\n shared?: ReadonlyMap<SessionId, TreeWalk>,\n ): Promise<TreeReading> => {\n signal?.throwIfAborted()\n const provided = shared?.get(sessionId)\n if (provided !== undefined) return readingOf(provided, watch)\n const cached = walks.get(sessionId)\n if (cached !== undefined) return cached\n // No subagent service at all: the walk would answer \"no members\", which is\n // not the same claim as \"no children\", and this report may not make it.\n const reading = ctx.get('subagents') === undefined\n ? { members: [], unresumable: false, unread: true, running: false }\n : readingOf(await walkDescendants(ctx, sessionId, signal), watch)\n walks.set(sessionId, reading)\n return reading\n }\n\n /**\n * Every running session and every descendant of one, minus the sessions this\n * pause already owns. Descendants are walked whether or not they are live\n * right now: the tree is the unit the user asked to stop. A member this host\n * has no live session for has nothing to hold and nothing to flush, and the\n * press leaves it alone rather than record a park that could never finish\n * (see `./control.ts`).\n */\n const runningTargets = async (signal?: AbortSignal): Promise<readonly SessionId[]> => {\n const found = new Set<SessionId>()\n for (const agent of ctx.agents.list()) if (running(agent)) found.add(agent.id)\n // The roots are fixed before the walk: a descendant discovered here is a\n // member of a tree, not a second tree to walk from.\n for (const root of [...found]) {\n for (const member of (await walkTree(root, signal)).members) found.add(member)\n }\n return [...found].filter(sessionId => !deps.isPaused(sessionId))\n }\n\n /**\n * Live jobs of the registered trees, plus the unowned ones every caller sees.\n *\n * One registry walk per owner is the whole answer the jobs contract allows:\n * `list()` with no caller answers with unowned jobs only, and `list(owner)`\n * answers for exactly one owner, because the registry preflights ownership\n * instead of letting a reader enumerate another session's work.\n */\n const liveJobs = (owners: Iterable<SessionId>): readonly string[] => {\n const jobs = ctx.get('jobs')\n if (jobs === undefined) return []\n const seen = new Set<string>()\n const collect = (owner?: SessionId): void => {\n for (const view of owner === undefined ? jobs.list() : jobs.list(owner)) {\n if (view.status !== 'running' && view.status !== 'stopping') continue\n seen.add(String(view.id))\n }\n }\n collect()\n for (const owner of owners) collect(owner)\n return [...seen]\n }\n\n /**\n * The aggregate. Every registered session is accounted for: the ones on\n * their way, the ones that will not get there in this process, the trees\n * that hold work a restart cannot resume, and the jobs nothing here can stop.\n * The park also names the session it belongs to, so a client can address its\n * reads there rather than at whatever session it is showing.\n */\n const status = async (\n signal?: AbortSignal,\n sharedWalks?: ReadonlyMap<SessionId, TreeWalk>,\n ): Promise<GlobalStatus> => {\n const phases = new Map<SessionId, LivePark>()\n // The durable list is the promise; a live mark is the same episode's\n // fresher phase, and a press that could not be recorded is neither.\n for (const [sessionId, record] of registry.parked) phases.set(sessionId, { phase: record.phase, moving: false })\n // Read out of the accessor's own snapshot rather than through it per mark,\n // so this read asks the live state once however many sessions it holds.\n const live = deps.live()\n for (const [sessionId, park] of live) phases.set(sessionId, park)\n for (const sessionId of unrecorded) phases.set(sessionId, { phase: 'draining', moving: false })\n const unheld = deps.unheld()\n // Walks are per episode: a session the park no longer owns drops its entry,\n // so a later press reads the tree again instead of reporting a stale one.\n for (const sessionId of [...walks.keys()]) if (!phases.has(sessionId)) walks.delete(sessionId)\n\n let safe = 0\n let parking = 0\n const draining: string[] = []\n const stalled: string[] = []\n for (const [sessionId, park] of phases) {\n // A record this process could not hold is not a park here, whatever its\n // durable phase says: nothing is standing at the gate for it, so counting\n // it as safe would promise a hold that does not exist.\n if (unheld.has(sessionId)) {\n stalled.push(String(sessionId))\n continue\n }\n if (park.phase === 'safe') {\n safe += 1\n continue\n }\n const bucket = park.moving ? draining : stalled\n bucket.push(String(sessionId))\n // Standing at the park point with the flush still pending is counted\n // apart from the sessions still draining: both are unfinished, and the\n // pill's sentence is about how much of the wait is left rather than about\n // what is still running.\n if (park.moving && park.phase === 'parking') parking += 1\n }\n\n const unresumable: string[] = []\n const unreadTrees: string[] = []\n const owners = [...phases.keys()]\n // The session this park belongs to: the first registered session this\n // process holds, so a client can address its reads at the park rather than\n // at whatever session the user is looking at. The buckets above name a\n // session only while it has work left, so a park that reached `safe` — the\n // clean, ordinary case — would otherwise name no session at all. A record\n // this process cannot hold is named only when there is nothing else to\n // name: it is a true fact about the park, but it cannot answer a read.\n const parkedAt = owners.find(sessionId => !unheld.has(sessionId)) ?? owners[0]\n // One walk is one catalog read, and the walks do not depend on each other:\n // a read of N registered sessions must not take N catalog round-trips end\n // to end. `walkTree` answers from its per-episode cache when it has one.\n const trees = await Promise.all(owners.map(async sessionId => [sessionId, await walkTree(sessionId, signal, sharedWalks)] as const))\n for (const [sessionId, tree] of trees) {\n if (tree.unresumable) unresumable.push(String(sessionId))\n if (tree.unread) unreadTrees.push(String(sessionId))\n }\n\n const blind: GlobalBlindSpot[] = []\n if (ctx.get('subagents') === undefined) blind.push('trees')\n if (ctx.get('jobs') === undefined) blind.push('jobs')\n\n const registered = phases.size\n // A tree nobody could read might hold a running descendant this park never\n // registered, so an unread tree is not a clean bill even when the session\n // itself is flushed.\n const verdict: GlobalVerdict = registered === 0\n ? 'idle'\n : safe === registered && unreadTrees.length === 0 && !trees.some(([, tree]) => tree.running)\n ? 'safe'\n : 'waiting'\n\n return {\n verdict,\n registered,\n safe,\n ...parkedAt === undefined ? {} : { parkedAt: String(parkedAt) },\n draining: draining.sort(),\n parking,\n stalled: stalled.sort(),\n unresumable: [...new Set(unresumable)].sort(),\n unreadTrees: unreadTrees.sort(),\n jobs: liveJobs(owners),\n blind,\n }\n }\n\n return {\n pressAll: async (signal) => {\n for (let round = 0; round < PRESS_ROUNDS; round += 1) {\n const targets = await runningTargets(signal)\n if (targets.length === 0) break\n for (const sessionId of targets) {\n signal?.throwIfAborted()\n // The press is a durable promise per session: one that cannot be\n // recorded is rolled back there and named here. A session whose\n // route is not resumable, or has no live log, is left out of the\n // registry rather than promised a wake the morning path must refuse.\n const pressed = await deps.press(sessionId, signal)\n switch (pressed) {\n case 'registered':\n notResumable.delete(sessionId)\n markUnrecorded(sessionId, false)\n deps.look(sessionId)\n break\n case 'unrecordable':\n markUnrecorded(sessionId, true)\n break\n case 'not-resumable':\n notResumable.add(sessionId)\n break\n default:\n break\n }\n }\n }\n const aggregate = await status(signal)\n const answer = aggregate.verdict === 'safe'\n ? 'Every session is paused.'\n : aggregate.verdict === 'idle'\n ? 'Nothing is running.'\n : 'Pausing is in progress.'\n const blocked = notResumable.size\n return unrecorded.size === 0 && blocked === 0\n ? { kind: 'success', text: answer }\n : {\n kind: 'error',\n text: `${answer} ${String(blocked)} session${blocked === 1 ? '' : 's'} could not be paused because its route is not resumable.`,\n }\n },\n readState: async (signal, walks) => await status(signal, walks),\n markUnrecorded,\n }\n}\n","/**\n * The order a batch release walks: parent before child, shallowest first.\n *\n * A single tree release already has the pre-order from its own walk, so this\n * module serves the batch path, where the recorded sessions may span several\n * trees. `listDescendants` stamps each row with its distance from the listing\n * root, so one catalog read per recorded root places every member. A session\n * the walk never reached is its own root and releases with the first level.\n * @module dsh-pause-session/release-order\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\nimport { describeError } from './shared.js'\n\n/** How a batch release is sequenced. */\nexport interface ReleaseOrder {\n /** One list per depth, shallowest first. */\n readonly levels: readonly (readonly SessionId[])[]\n}\n\n/**\n * Sequence a batch release parent before child.\n * @param ctx - host context carrying the optional subagent catalog.\n * @param sessionIds - every recorded session, in registry order.\n * @param signal - cancellation for each catalog read.\n * @returns one list per depth level, shallowest first.\n */\nexport async function releaseOrder(\n ctx: Context,\n sessionIds: readonly SessionId[],\n signal?: AbortSignal,\n): Promise<ReleaseOrder> {\n const depth = new Map<SessionId, number>()\n const subagents = ctx.get('subagents')\n if (subagents !== undefined) {\n const owned = new Set(sessionIds)\n for (const sessionId of sessionIds) {\n signal?.throwIfAborted()\n // A member an earlier listing already placed has its depth; walking it\n // again would read the same catalogs for nothing.\n if (depth.has(sessionId)) continue\n depth.set(sessionId, 0)\n try {\n for (const row of await subagents.listDescendants(sessionId, signal)) {\n if (row.kind !== 'child' || !owned.has(row.id)) continue\n depth.set(row.id, row.depth)\n }\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error\n ctx.logger.warn(`pause: could not read the descendants of \"${sessionId}\": ${describeError(error)}`)\n }\n }\n }\n const levels = new Map<number, SessionId[]>()\n for (const sessionId of sessionIds) {\n const level = depth.get(sessionId) ?? 0\n const bucket = levels.get(level)\n if (bucket === undefined) levels.set(level, [sessionId])\n else bucket.push(sessionId)\n }\n return {\n levels: [...levels.keys()].sort((left, right) => left - right)\n .map(level => levels.get(level) ?? []),\n }\n}\n","/**\n * What a parked Session needs to come back as itself.\n *\n * A root Session is loaded directly: the Agent preset it ran supplies the\n * scoped tools, prompt sections, and skills, so the resume mounts that preset\n * before the Agent is published. A subagent child is not loaded here at all —\n * the subagent runtime owns its activation, composition, and tree membership,\n * and cold-resumes it when this plugin delivers the wake through\n * `ctx.subagents.prompt`.\n *\n * The two cases are told apart by one read of the Session's own log, which is\n * the same read the press-time resumability check already pays for.\n * @module dsh-pause-session/composition\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { AgentSetup } from '@deepseek-ai/dsh-agent'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: pulls the optional session projection read and preset roster faces.\nimport type {} from '@deepseek-ai/dsh-session-query'\nimport type {} from '@deepseek-ai/dsh-agent-preset-registry'\nimport { foldSubagentDescriptor } from '@deepseek-ai/dsh-subagent'\n\n/** How one parked Session comes back. */\nexport type ParkedComposition =\n | {\n /** An ordinary Session the plugin loads itself. */\n readonly kind: 'root'\n /** The scoped Agent world to mount before the Agent is published. */\n readonly setup?: AgentSetup\n }\n | {\n /**\n * A subagent child. The subagent runtime restores its composition from the\n * persisted descriptor and preset projection, so the plugin only delivers\n * the wake.\n */\n readonly kind: 'child'\n }\n\n/**\n * Whether a local Session may be parked under a promise of a later resume.\n *\n * A normal root Session has no child descriptor and is resumable. A local\n * subagent child is resumable only when its own durable descriptor is\n * `continuable`; a historical `one-shot` child is terminal and must never be\n * registered as a park the morning path would later have to refuse. External\n * executions have no local Session, so callers never reach this check.\n *\n * A profile without the optional Session-query service cannot prove the\n * descriptor's mode; it keeps the previous permissive behavior rather than\n * dropping every subagent. The web profile mounts the query service.\n * @param ctx - host context carrying the optional Session-query service.\n * @param sessionId - the local Session to inspect.\n * @param signal - cancellation for the observation read.\n * @returns whether a park of this Session may be resumed.\n * @throws the cancellation reason when `signal` aborts the read.\n */\nexport async function isResumableSession(\n ctx: Context,\n sessionId: SessionId,\n signal?: AbortSignal,\n): Promise<boolean> {\n const query = ctx.get('sessionQuery')\n if (query === undefined) return true\n const observation = await query.observeSession(sessionId, {\n ...signal === undefined ? {} : { signal },\n })\n try {\n const ownEvents = observation.events.slice(observation.inheritedEventCount)\n return foldSubagentDescriptor(ownEvents)?.mode !== 'one-shot'\n } finally {\n observation[Symbol.dispose]()\n }\n}\n\n/**\n * Read one parked Session's own log and decide how it comes back.\n *\n * A child returns as `child` with no setup: the subagent runtime restores the\n * composition from the descriptor and the preset projection, and mounting a\n * second one here would start a different Agent on the same history.\n *\n * A root resolves the preset it ran — the projection, never the creation-time\n * header alone, because a blank Session may select another preset before its\n * first turn — and hands back the setup that mounts it. A profile with no\n * preset roster has no scoped composition to restore and resumes unchanged.\n * @param ctx - host context carrying the optional preset and Session-query services.\n * @param sessionId - the parked Session being resumed.\n * @param signal - cancellation for the observation read.\n * @returns how this Session comes back.\n * @throws when a mounted preset roster cannot read the Session projection.\n */\nexport async function readParkedComposition(\n ctx: Context,\n sessionId: SessionId,\n signal?: AbortSignal,\n): Promise<ParkedComposition> {\n const presets = ctx.get('agentPresets')\n const query = ctx.get('sessionQuery')\n if (query === undefined) {\n // Without the query service nothing can prove the Session's composition; a\n // profile that mounts a preset roster must not start a different Agent.\n if (presets !== undefined) {\n throw new Error('resuming a composed Agent requires the session-query service')\n }\n return { kind: 'root' }\n }\n const observation = await query.observeSession(sessionId, {\n ...signal === undefined ? {} : { signal },\n })\n try {\n const ownEvents = observation.events.slice(observation.inheritedEventCount)\n // A durable descriptor is the child's own record; `origin` covers a child\n // whose descriptor predates the current format. An ordinary fork also\n // carries `parentSession`, so that field alone is not a child signal.\n if (foldSubagentDescriptor(ownEvents) !== undefined || observation.header.origin === 'subagent') {\n return { kind: 'child' }\n }\n if (presets === undefined) return { kind: 'root' }\n const projections = observation.projections\n if (projections === undefined) {\n throw new Error('resuming a composed Agent requires projected Session state')\n }\n // A root names its own preset; a Session that never selected one follows\n // the roster's default, exactly as the official Session controller does.\n const resolved = await presets.resolve(projections.values.agentPreset ?? undefined)\n return {\n kind: 'root',\n setup: async (agentCtx) => { await presets.mount(agentCtx, resolved.id) },\n }\n } finally {\n observation[Symbol.dispose]()\n }\n}\n","/**\n * User-facing copy and result shapes for the clean resume and tree release.\n * @module dsh-pause-session/resume-text\n */\n\nimport type { SessionId } from '@deepseek-ai/dsh-session'\nimport type { PauseAnswer } from './shared.js'\n\n/** Route knowledge the clean resume path needs. */\nexport interface ResumeOptions {\n /** Provider routes whose request conversion drops an empty user message. */\n readonly silentWakeRoutes: readonly string[]\n}\n\n/**\n * The one wake content a route that keeps an empty user message is given.\n */\nexport const WAKE_MARKER = '[pause: resumed from a pause]'\n\n/** How one resume asks the loop for the request it owes the session. */\nexport type WakeRequest =\n | { readonly kind: 'silent' }\n | { readonly kind: 'message'; readonly text: string }\n | { readonly kind: 'none' }\n\n/** Result copy for a resumed session; the model never sees it. */\nexport const RESUMED_TEXT = 'This session resumed from where it paused.'\n\n/** Result copy for a registered session whose park did not finish cleanly. */\nexport const UNCLEAN_TEXT = 'pause did not resume this session: its pause did not finish cleanly,'\n + ' so it needs your decision.'\n\n/** Result copy when nothing is parked under that session. */\nexport const NOT_PARKED_TEXT = 'pause has no pause recorded for this session.'\n\n/** Result copy for a session that is parked but could not be loaded back. */\nexport const ACTIVATION_FAILED_TEXT = 'pause could not reopen this session, so it stayed paused.'\n\n/** Result copy for a one-shot subagent route. */\nexport const UNRESUMABLE_TEXT = 'pause did not resume this session: it is a one-shot subagent route and cannot be resumed.'\n\n/** Result copy for the review decision. */\nexport const REVIEW_TEXT = 'pause did not activate this session: it is still parked, and nothing was sent to a model.'\n + ' Open it from your session list when you want to read it.'\n\n/** Copy for one goal opt-out change. */\nexport const REARM_GOAL_ON_TEXT = 'pause will re-open this session\\'s goal when it resumes it.'\nexport const REARM_GOAL_OFF_TEXT = 'pause will leave this session\\'s goal closed when it resumes it.'\n\n/** Why one record did not produce a running session. */\nexport type ResumeRefusal = 'not-parked' | 'not-clean' | 'activation-failed' | 'unrecorded' | 'not-activated' | 'unresumable'\n\n/** What one resume attempt did. */\nexport interface ResumeOutcome {\n readonly sessionId: SessionId\n readonly resumed: boolean\n readonly refusal?: ResumeRefusal\n}\n\n/** One resume attempt plus the answer the caller receives. */\nexport interface ResumeAttempt extends ResumeOutcome {\n readonly answer: PauseAnswer\n}\n\n/** What releasing one session's registered tree did. */\nexport interface ReleaseTreeOutcome {\n readonly members: number\n readonly resumed: number\n readonly refusals: readonly { readonly sessionId: SessionId; readonly refusal: ResumeRefusal }[]\n readonly answer: PauseAnswer\n}\n\n/**\n * How one tree-wide release answers the person who pressed it.\n * @param released - the sessions this release covered, in release order.\n * @returns one sentence about what came back and what did not.\n */\nexport function summarizeTreeRelease(released: readonly ResumeAttempt[]): string {\n const resumed = released.filter(attempt => attempt.resumed).length\n const head = resumed === 0\n ? 'pause resumed nothing from this tree'\n : resumed === 1\n ? 'pause resumed 1 session from where it stopped'\n : `pause resumed ${resumed} sessions from where they stopped`\n const refused = released.filter(attempt => !attempt.resumed)\n if (refused.length === 0) return `${head}.`\n const named = refused\n .filter(attempt => attempt.refusal === 'not-clean')\n .map(attempt => `\"${String(attempt.sessionId)}\"`)\n const rest = named.length === 0\n ? `${String(refused.length)} could not be resumed`\n : refused.length === named.length\n ? `${named.join(', ')} did not pause completely`\n : `${named.join(', ')} did not pause completely, and ${String(refused.length - named.length)} more could not be resumed`\n return `${head}; ${rest}.`\n}\n","/**\n * Reactivate one parked Session and deliver the wake it owes.\n *\n * The two Session kinds come back through different owners. An ordinary\n * Session is loaded by this plugin and given the route its own log recorded.\n * A subagent child belongs to the subagent runtime: delivering the wake through\n * `ctx.subagents.prompt` is what cold-resumes it as a managed activation, under\n * its live tree root, with its persisted composition and tree membership\n * restored by the runtime itself.\n *\n * Both paths keep the same release order: the durable record goes first, then\n * the gate, so a record that cannot be dropped leaves the Session parked.\n * @module dsh-pause-session/resume-activation\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Agent } from '@deepseek-ai/dsh-agent'\nimport { createUserMessage } from '@deepseek-ai/dsh-llm'\nimport type { ContentBlock, LlmCallConfig } from '@deepseek-ai/dsh-llm'\nimport type { Session, SessionId } from '@deepseek-ai/dsh-session'\nimport type {} from '@deepseek-ai/dsh-goal'\nimport type { SubagentPromptRequestId } from '@deepseek-ai/dsh-subagent'\nimport { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'\nimport type { PauseGate } from './gate.js'\nimport { readParkedComposition } from './composition.js'\nimport type { ParkedComposition } from './composition.js'\nimport { owesReply } from './history.js'\nimport type { ParkRegistry } from './registry.js'\nimport { describeError, UNRECORDED_TEXT } from './shared.js'\nimport type { PauseAnswer } from './shared.js'\nimport {\n ACTIVATION_FAILED_TEXT, RESUMED_TEXT, UNRESUMABLE_TEXT, WAKE_MARKER,\n} from './resume-text.js'\nimport type { ResumeAttempt, ResumeOptions, WakeRequest } from './resume-text.js'\n\n/** The activation entry points the resume path composes. */\nexport interface Activator {\n activate(sessionId: SessionId, request: WakeRequest, rearm: boolean, signal?: AbortSignal): Promise<ResumeAttempt>\n ensureRoute(agent: Agent): void\n wake(agent: Agent, request: WakeRequest): void\n rearmGoal(agent: Agent): void\n}\n\n/**\n * Build the activation closure.\n * @param ctx - host context carrying the agent store, subagent runtime, and goal service.\n * @param gate - the pause gate released after activation.\n * @param registry - the durable list the release forgets from.\n * @param options - the route knowledge the root wake content depends on.\n * @returns the activation methods the resume path uses.\n */\nexport function createActivator(\n ctx: Context,\n gate: PauseGate,\n registry: ParkRegistry,\n options: ResumeOptions,\n): Activator {\n const routeOf = (session: Session): LlmCallConfig | undefined => session.requestHeader()?.config\n\n const ensureRoute = (agent: Agent): void => {\n if (agent.model.current() !== undefined) return\n const config = routeOf(agent.session)\n if (config === undefined) return\n agent.model.select({\n provider: config.provider,\n model: config.model,\n ...config.reasoningEffort === undefined ? {} : { reasoningEffort: config.reasoningEffort },\n })\n }\n\n const wakeContent = (provider: string): ContentBlock[] =>\n options.silentWakeRoutes.includes(provider) ? [] : [{ type: 'text', text: WAKE_MARKER }]\n\n const wake = (agent: Agent, request: WakeRequest): void => {\n if (request.kind === 'none') return\n if (gate.isHeld(agent.id) || agent.status !== 'idle') return\n if (!owesReply(agent.session.deriveMessages())) return\n const provider = routeOf(agent.session)?.provider\n if (provider === undefined) return\n const content: ContentBlock[] = request.kind === 'message'\n ? [{ type: 'text', text: request.text }]\n : wakeContent(provider)\n agent.followup(createUserMessage({ content, source: { kind: 'pause' } }))\n }\n\n const rearmGoal = (agent: Agent): void => {\n const goals = ctx.get('goals')\n if (goals === undefined) return\n try {\n const goal = goals.get(agent)\n if (goal === undefined || goal.phase !== 'active' || goal.activation !== 'disarmed') return\n if (goal.roundsStarted >= goal.maxGoalRounds) return\n goals.resume(agent, { id: goal.id, revision: goal.revision })\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not re-open the goal of \"${agent.id}\": ${describeError(error)}`)\n }\n }\n\n /** Drop the durable record, then the gate: the order every release keeps. */\n const releaseRecord = async (sessionId: SessionId): Promise<PauseAnswer | undefined> => {\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { kind: 'error', text: UNRECORDED_TEXT }\n }\n gate.resume(sessionId)\n return undefined\n }\n\n /** Load an ordinary Session, give it its recorded route, and deliver the wake. */\n const activateRoot = async (\n sessionId: SessionId,\n request: WakeRequest,\n rearm: boolean,\n composition: Extract<ParkedComposition, { kind: 'root' }>,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n let agent: Agent\n try {\n agent = (await ctx.root.agents.resume({\n resumeSessionId: sessionId,\n ...composition.setup === undefined ? {} : { setup: composition.setup },\n ...signal === undefined ? {} : { signal },\n })).agent\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not open \"${sessionId}\" from its stored log: ${describeError(error)}`)\n return {\n sessionId,\n resumed: false,\n refusal: 'activation-failed',\n answer: { kind: 'error', text: ACTIVATION_FAILED_TEXT },\n }\n }\n ensureRoute(agent)\n wake(agent, request)\n const refused = await releaseRecord(sessionId)\n if (refused !== undefined) return { sessionId, resumed: false, refusal: 'unrecorded', answer: refused }\n if (rearm) rearmGoal(agent)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n\n /**\n * Hand a subagent child the wake through the subagent runtime.\n *\n * The runtime owns the activation, so it is also the only thing that can\n * cold-resume the child under its live tree root. An empty message would be\n * dropped by a route whose conversion discards it, and the route is unknown\n * before the child is live, so the marker is the content that always wakes it.\n */\n const activateChild = async (\n sessionId: SessionId,\n request: WakeRequest,\n rearm: boolean,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const subagents = ctx.get('subagents')\n if (subagents === undefined) {\n // A profile without the subagent runtime owns no managed tree to join;\n // the child can only come back as an independent Session.\n ctx.logger.warn(`pause: subagent runtime is not mounted; opening \"${sessionId}\" as a root Session`)\n return await activateRoot(sessionId, request, rearm, { kind: 'root' }, signal)\n }\n // A wake is the only thing that cold-resumes a child, so `none` — the\n // review answer — never reaches this path: review returns before activating.\n const content = [{ type: 'text' as const, text: request.kind === 'message' ? request.text : WAKE_MARKER }]\n try {\n await subagents.prompt({\n requestId: randomUUID() as SubagentPromptRequestId,\n childSessionId: sessionId,\n delivery: 'queue',\n content,\n }, signal ?? new AbortController().signal)\n } catch (error: unknown) {\n if (remoteErrorOf(error)?.code === 'subagent/not-resumable') {\n return {\n sessionId,\n resumed: false,\n refusal: 'unresumable',\n answer: { kind: 'error', text: UNRESUMABLE_TEXT },\n }\n }\n ctx.logger.warn(`pause: could not continue subagent \"${sessionId}\": ${describeError(error)}`)\n return {\n sessionId,\n resumed: false,\n refusal: 'activation-failed',\n answer: { kind: 'error', text: ACTIVATION_FAILED_TEXT },\n }\n }\n const refused = await releaseRecord(sessionId)\n if (refused !== undefined) return { sessionId, resumed: false, refusal: 'unrecorded', answer: refused }\n const agent = ctx.agents.get(sessionId)\n if (rearm && agent !== undefined) rearmGoal(agent)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n\n const activate = async (\n sessionId: SessionId,\n request: WakeRequest,\n rearm: boolean,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n let composition: ParkedComposition\n try {\n composition = await readParkedComposition(ctx, sessionId, signal)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not read the composition of \"${sessionId}\": ${describeError(error)}`)\n return {\n sessionId,\n resumed: false,\n refusal: 'activation-failed',\n answer: { kind: 'error', text: ACTIVATION_FAILED_TEXT },\n }\n }\n return composition.kind === 'child'\n ? await activateChild(sessionId, request, rearm, signal)\n : await activateRoot(sessionId, request, rearm, composition, signal)\n }\n\n return { activate, ensureRoute, wake, rearmGoal }\n}\n","/**\n * The clean resume path: bring back a session a previous process parked, and\n * let it answer the input that was already pending when it stopped.\n *\n * A resume invents no turn. At the park point the loop had claimed the\n * session's pending input onto the surface and built the request the gate then\n * held; after a restart that input is still the last thing the session says and\n * the model still owes it an answer. What is missing is only a reason for the\n * loop to build a request again, and the documented way to ask for one is a\n * wake input — a user message the loop claims.\n *\n * The wake is invisible only on the routes the composition declares in\n * `silentWakeRoutes`, whose conversion drops an empty user message. On every\n * other route the loop's request carries one visible marker instead, and the\n * marker reaches the model; that is an accepted cost of resuming at all.\n * Whether an empty user message survives is a property of the route's\n * conversion rather than of the loop, so the route is resolved per session —\n * from the request header the session itself persisted.\n *\n * The wake message is attributed to this plugin, never to the user: it is text\n * this module synthesized on the session's behalf, in the session's own log and\n * in front of the model, and a downstream consumer has to be able to tell it\n * apart from something the human typed.\n *\n * Only a cleanly parked session takes this path. A record that never reached\n * the park point, or whose flush did not finish, describes work whose durable\n * state nobody knows; waking it would spend its owner's money on a guess, and\n * deciding those sessions is the morning list's job, not this path's. That\n * holds whether or not the recorded session happens to be live in this process:\n * liveness is not the park outcome, and a session can be live without ever\n * having stopped.\n * @module dsh-pause-session/resume\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: brings `ctx.get('goals')` and the goal view this path reads.\nimport type {} from '@deepseek-ai/dsh-goal'\nimport type { PauseGate } from './gate.js'\nimport type { ParkRegistry } from './registry.js'\nimport { describeError, UNRECORDED_TEXT } from './shared.js'\nimport { walkDescendants } from './tree.js'\nimport { releaseOrder } from './release-order.js'\nimport { createActivator } from './resume-activation.js'\nimport type { PauseAnswer } from './shared.js'\nexport * from './resume-text.js'\nimport {\n NOT_PARKED_TEXT, REARM_GOAL_OFF_TEXT, REARM_GOAL_ON_TEXT,\n RESUMED_TEXT, REVIEW_TEXT, summarizeTreeRelease, UNCLEAN_TEXT,\n} from './resume-text.js'\nimport type { ReleaseTreeOutcome, ResumeAttempt, ResumeOptions, ResumeRefusal, WakeRequest } from './resume-text.js'\n\ndeclare module '@deepseek-ai/dsh-llm' {\n interface MessageSourceMap {\n /**\n * Text dsh-pause-session synthesized on the session's behalf: the wake this\n * path delivers and the unknown-outcome note a decision adds. Declared so\n * the log, and anything reading it, can tell plugin-authored input from the\n * user's own.\n */\n 'pause': { kind: 'pause' }\n }\n}\n\n/** How this path is wired into the session control that owns the pause state. */\nexport interface ResumeDeps {\n /**\n * Take a live session's pause back: the durable record goes first, then the\n * gate. Owned by the session control so there is one release order.\n */\n release(sessionId: SessionId, text: string): Promise<PauseAnswer>\n /**\n * Whether the pause this process holds was adopted from the registry rather\n * than pressed here. Only an adopted pause is a restart: a press this process\n * made and took back must never wake a model.\n */\n adopted(sessionId: SessionId): boolean\n}\n\n/** The clean resume path, as the session control drives it. */\nexport interface ResumePath {\n /**\n * Resume one session the registry remembers.\n * @param sessionId - the session to resume.\n * @param request - which wake this resume delivers; see {@link WakeRequest}.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n resumeSession(\n sessionId: SessionId,\n request: WakeRequest,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt>\n /**\n * Resume one session the decision list officially owns — a record that is\n * known NOT to be clean, which the clean path above must refuse and this entry\n * exists to carry out. Same release order, same wake vocabulary.\n * @param sessionId - the listed session to bring back.\n * @param request - which wake the decision delivers; see {@link WakeRequest}.\n * @param finalize - whether the decision continues the session (`'release'`)\n * or only answers it (`'review'`). A review activates nothing and writes\n * nothing: opening a session to read it is the user's own action, not this\n * plugin's.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n decideUnclean(\n sessionId: SessionId,\n request: WakeRequest,\n finalize: 'release' | 'review',\n signal?: AbortSignal,\n ): Promise<ResumeAttempt>\n /**\n * Resume every session the registry remembers, clean ones only. One session's\n * failure never stops the others.\n * @param signal - cancellation checked between release levels and forwarded to each load.\n * @returns one attempt per recorded session, in registry order.\n */\n resumeParked(signal?: AbortSignal): Promise<readonly ResumeAttempt[]>\n /**\n * Record or clear one session's goal opt-out.\n * @param sessionId - the parked session to change.\n * @param rearmGoal - whether the clean resume path may re-arm its goal.\n * @returns the copy for whoever asked.\n */\n setRearmGoal(sessionId: SessionId, rearmGoal: boolean): Promise<PauseAnswer>\n /**\n * Release one parked session and the registered members of its tree: the\n * counterpart of a press that registered a whole tree.\n * @param sessionId - the session whose tree the user released.\n * @param signal - cancellation for the tree read and each member's load.\n * @returns what happened to the tree, and the copy for whoever asked.\n */\n releaseTree(sessionId: SessionId, signal?: AbortSignal): Promise<ReleaseTreeOutcome>\n}\n\n/**\n * Build the clean resume path.\n * @param ctx - host context carrying the agent store, the session store, and the goal service when one is mounted.\n * @param gate - the pause gate the resume releases.\n * @param registry - the durable list this path reads and forgets from.\n * @param options - the route knowledge the wake content depends on.\n * @param deps - the control-owned release order and the adopted-pause fact.\n * @returns the path the session control exposes.\n */\nexport function installResumePath(\n ctx: Context,\n gate: PauseGate,\n registry: ParkRegistry,\n options: ResumeOptions,\n deps: ResumeDeps,\n): ResumePath {\n /** The route the session's next request will use, as its own log recorded it. */\n const { activate, ensureRoute, wake, rearmGoal } = createActivator(ctx, gate, registry, options)\n\n /**\n * Load one unclean session back from its stored log.\n *\n * This is the morning list's own entry point, and it exists precisely because\n * the clean path must keep refusing these records: the refusal is the clean\n * path's guarantee, not a guard this entry can borrow. A user who has read the\n * row and chosen one of the decisions that resume it has made the decision the\n * clean path is not allowed to make for them. The release order is the same\n * one: the durable record goes first, then the gate — so a record that cannot\n * be dropped leaves the session parked.\n *\n * A record this process holds no gate for is still this list's to clear: the\n * session is live, so the decision's own wake is what asks for its request,\n * and dropping the record is what stops a later process from re-holding a\n * session the user has already decided.\n * @param sessionId - the listed session to bring back.\n * @param request - which wake the decision delivers; see {@link WakeRequest}.\n * @param finalize - whether the decision continues the session (`'release'`)\n * or only answers it (`'review'`); a review activates nothing and writes\n * nothing.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n const decideUnclean = async (\n sessionId: SessionId,\n request: WakeRequest,\n finalize: 'release' | 'review',\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const record = registry.get(sessionId)\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // Opening a session to read it is the user's own action in their session\n // list; this decision only answers, so it never activates an agent and\n // never writes. That is what makes it the safe choice on a session whose\n // side effects may already have happened.\n if (finalize === 'review') {\n return { sessionId, resumed: false, refusal: 'not-activated', answer: { kind: 'success', text: REVIEW_TEXT } }\n }\n const live = ctx.agents.get(sessionId)\n if (live === undefined) return await activate(sessionId, request, record.rearmGoal, signal)\n if (!gate.isPaused(sessionId)) {\n // This process holds nothing for the session — its record was found\n // unreadable at mount, or something else already released it. The session\n // is live, so the decision's own wake is what asks for the request it is\n // owed, and the record goes on the release path's own order.\n ensureRoute(live)\n wake(live, request)\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: { kind: 'error', text: UNRECORDED_TEXT } }\n }\n if (record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n const adopted = deps.adopted(sessionId)\n // An adopted pause with nothing standing at the gate has no continuation to\n // release: the process that built that request is gone, so the decision's\n // own wake is what asks for one.\n if (adopted) {\n ensureRoute(live)\n wake(live, request)\n }\n const released = await deps.release(sessionId, RESUMED_TEXT)\n if (released.kind === 'error') {\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: released }\n }\n if (adopted && record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: released }\n }\n\n /**\n * Resume one recorded session: the clean path's only entry point.\n *\n * The clean-only rule holds on every branch, live or not: a record that is\n * not `safe` is not a park that finished, so this path neither wakes it nor\n * drops it — whoever holds it stays held, and the row reaches the morning\n * list. A pause this process pressed itself is not a record at all: taking it\n * back is the press's own undo, and it delivers no wake.\n * @param sessionId - the recorded session to resume.\n * @param request - which wake this resume delivers; see {@link WakeRequest}.\n * @param signal - cancellation for the cold load and setup.\n * @returns what happened, and the copy for whoever asked.\n */\n const resumeSession = async (\n sessionId: SessionId,\n request: WakeRequest,\n signal?: AbortSignal,\n ): Promise<ResumeAttempt> => {\n const record = registry.get(sessionId)\n const live = ctx.agents.get(sessionId)\n\n if (live !== undefined) {\n if (!gate.isPaused(sessionId)) {\n // This process holds nothing here. An unclean record belongs to the\n // morning list, and refusing it here is what keeps it there.\n if (record !== undefined && record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // A clean record nothing is holding any more: the session is already\n // back (it became readable after this mount could not hold it), so the\n // park is over. The record goes first — a park this process cannot drop\n // is not one it may continue — and then the session is handed the\n // request it stopped in front of, which is this path's own job.\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: { kind: 'error', text: UNRECORDED_TEXT } }\n }\n ensureRoute(live)\n wake(live, request)\n if (record.rearmGoal) rearmGoal(live)\n return { sessionId, resumed: true, answer: { kind: 'success', text: RESUMED_TEXT } }\n }\n // A pause this process did NOT press is another process's verdict: only a\n // finished park may be woken from it. A press of this process's own is\n // the pause the user is taking back, and it delivers no wake either way.\n const adopted = deps.adopted(sessionId)\n if (adopted && record !== undefined && record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n // An adopted pause with nothing standing at the gate has no continuation\n // to release: the process that built that request is gone.\n if (adopted) {\n ensureRoute(live)\n wake(live, request)\n }\n const released = await deps.release(sessionId, RESUMED_TEXT)\n if (released.kind === 'error') {\n return { sessionId, resumed: false, refusal: 'unrecorded', answer: released }\n }\n // Re-arming spends money on the session's own, so the record's opt-out\n // decides it — and only an adopted pause can have lost the activation.\n if (adopted && (record?.rearmGoal ?? true)) rearmGoal(live)\n return { sessionId, resumed: true, answer: released }\n }\n\n if (record === undefined) {\n return { sessionId, resumed: false, refusal: 'not-parked', answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // The one rule of this path: a record that is not `safe` is not a park that\n // finished, and waking it would act on a durable state nobody knows.\n if (record.phase !== 'safe') {\n return { sessionId, resumed: false, refusal: 'not-clean', answer: { kind: 'error', text: UNCLEAN_TEXT } }\n }\n return await activate(sessionId, request, record.rearmGoal, signal)\n }\n\n return {\n resumeSession,\n decideUnclean,\n releaseTree: async (sessionId, signal) => {\n // The tree is read here, not taken from the press: the members that were\n // registered are exactly the members whose records exist, and a member\n // the walk cannot reach has no record to release either.\n const rooted = [sessionId, ...(await walkDescendants(ctx, sessionId, signal)).members.map(member => member.id)]\n const registered = rooted.filter(member => registry.get(member) !== undefined)\n if (registered.length === 0) {\n return { members: 0, resumed: 0, refusals: [], answer: { kind: 'success', text: NOT_PARKED_TEXT } }\n }\n // One control, two jobs. A tree with nothing clean in it is a press being\n // taken back: the user is undoing the pause they just asked for, and the\n // clean-only rule must not stand in the way of that — it would leave a\n // session the user cancelled still parked, which is the one thing the\n // press-again button must never do. A tree that holds anything clean is\n // the release, and there the rule holds per record: the members that did\n // not stop cleanly keep their pause and are never silently woken.\n const cancelling = registered.every(member => {\n const record = registry.get(member)\n return record !== undefined && record.phase !== 'safe' && !deps.adopted(member)\n })\n const released: ResumeAttempt[] = []\n if (cancelling) {\n for (const member of registered) {\n signal?.throwIfAborted()\n const answer = await deps.release(member, RESUMED_TEXT)\n released.push(answer.kind === 'error'\n ? { sessionId: member, resumed: false, refusal: 'unrecorded', answer }\n : { sessionId: member, resumed: true, answer })\n }\n } else {\n // The walk is already pre-order, so walking it in order releases the\n // parent before the child without a second catalog read.\n for (const member of registered) {\n signal?.throwIfAborted()\n // The rule, applied here rather than left to the entry points below:\n // a pause this process pressed is normally the user's own to take\n // back, so the clean path releases it without asking its phase. In a\n // tree release it is not — a member that did not stop cleanly keeps\n // its pause, and the caller names it.\n const record = registry.get(member)\n if (record !== undefined && record.phase !== 'safe') {\n released.push({\n sessionId: member,\n resumed: false,\n refusal: 'not-clean',\n answer: { kind: 'error', text: UNCLEAN_TEXT },\n })\n continue\n }\n released.push(await resumeSession(member, { kind: 'silent' }, signal))\n }\n }\n const root = released.find(attempt => attempt.sessionId === sessionId)\n const refusals = released\n .filter((attempt): attempt is ResumeAttempt & { refusal: ResumeRefusal } => !attempt.resumed && attempt.refusal !== undefined)\n .map(attempt => ({ sessionId: attempt.sessionId, refusal: attempt.refusal }))\n return {\n members: released.length,\n resumed: released.filter(attempt => attempt.resumed).length,\n refusals,\n // The root's own answer is what the person who pressed sees: their\n // session either came back or it did not, and the tree's other members\n // are named in the count and in the refusals.\n answer: root === undefined\n ? { kind: 'success', text: NOT_PARKED_TEXT }\n : { kind: refusals.length === 0 && root.resumed ? 'success' : 'error', text: summarizeTreeRelease(released) },\n }\n },\n resumeParked: async (signal) => {\n const release = await releaseOrder(ctx, registry.list().map(entry => entry.sessionId), signal)\n const attempts: ResumeAttempt[] = []\n for (const level of release.levels) {\n signal?.throwIfAborted()\n // One release per level and no concurrency cap inside it: the levels\n // are the milliseconds a child's wake needs, not a queue.\n attempts.push(...await Promise.all(level.map(sessionId =>\n resumeSession(sessionId, { kind: 'silent' }, signal))))\n }\n return attempts\n },\n setRearmGoal: async (sessionId, rearmGoal) => {\n try {\n await registry.setRearmGoal(sessionId, rearmGoal)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not record the goal opt-out of \"${sessionId}\": ${describeError(error)}`)\n return { kind: 'error', text: UNRECORDED_TEXT }\n }\n return { kind: 'success', text: rearmGoal ? REARM_GOAL_ON_TEXT : REARM_GOAL_OFF_TEXT }\n },\n }\n}\n","/**\n * The morning decision list: every session that did not stop cleanly, the facts\n * its own log carries about where it stopped, and the listed verbs the user can\n * take on it.\n *\n * A session gets here by NOT being on the clean path. A record whose phase is\n * `safe` is woken silently by `./resume.ts` and never appears here; the other\n * three phases describe work nobody can vouch for, and guessing on them would\n * spend the owner's money on a guess. This module is the other half of that\n * rule, not a second copy of it: the reading is taken from the session's own\n * stored log through a read handle that claims no ownership and writes nothing,\n * so asking what happened cannot itself change what happened.\n *\n * WHAT ONE READ COSTS. The list is read when the panel opens and when a native\n * action changes the park, and what it publishes is about the end of a log that\n * only grows, so `./coldlog.ts` bounds each row's read to that end. A row whose\n * facts the bound cannot hold reports itself unreadable rather than publishing\n * a fragment.\n *\n * WHAT A ROW REPORTS. \"Where it stopped\" is the last event of the stored log\n * with the turn and step it belongs to. \"Which tool was in flight\" is the\n * `tool/call` events that no durable `tool/result` answers — a call event is\n * only written once the loop reached the dispatch, so an unanswered one is\n * exactly a tool the real world ran and the log never heard back from. The\n * harness turns each of those into a synthetic `TOOL_OUTCOME_UNKNOWN` error\n * result when it next reads the log; this module derives the same answer from\n * the stored events instead of writing it back. Nothing here is inferred from\n * the fact that the session is listed.\n *\n * WHAT THE ROW PROMISES. `wake` says the host can hand this session the request\n * it owes, and the wake that keeps that promise is `./resume.ts`'s. Both read\n * one rule from `./history.ts` over the same derivation the loop reads, so a\n * promise this list publishes is a promise that wake keeps. When the log cannot\n * be folded into that derivation, the promise is withheld and the reading is\n * reported; a withheld promise costs a user nothing, a broken one spends their\n * money.\n *\n * WHO MAY DECIDE. A decision carries the calling session and its target as wire\n * input, so this list acts only on rows its most recent read published to the\n * calling session: the caller's own read is what makes a target actionable.\n * Anything else is refused, not looked up.\n *\n * TWO OF THE FIVE VERBS PUT THE SESSION BACK ON THE CLEAN PATH. Continue and\n * continue-with-the-note go through `ResumePath.decideUnclean`, which activates\n * the session, wakes it and drops the durable record before the gate opens.\n *\n * THE OTHER THREE TOUCH NOTHING. Leave writes nothing at all: the record stays,\n * the session stays held, and the row is still on tomorrow's list. Review\n * answers without activating anything or writing anything — opening a session\n * to read it is the user's own action in their session history, and starting an\n * agent on their behalf is precisely what they declined. Goal flips the row's\n * own re-arm opt-out: it is the user path for the one thing a resume can spend\n * without the user watching it, and it takes effect on whatever resume comes\n * next. All five carry the same binding — the row must be one this caller was\n * shown — because each changes, or declines to change, durable state of a\n * parked session.\n * @module dsh-pause-session/triage\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` read seam and its handle.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport { coldRead, logFacts, positionOf, TRIAGE_WINDOW_EVENTS } from './coldlog.js'\nimport type { ParkRegistry, ParkedSession } from './registry.js'\nimport type { ResumePath } from './resume.js'\nimport type { TriageAction, TriageList, TriageRow } from './shared.js'\nimport type { PauseAnswer } from './shared.js'\n\n/** The route a wake input has to be addressed to; see `./resume.ts`. */\ntype WakeOf = Parameters<ResumePath['resumeSession']>[1]\n\n/** Result copy for a decision the list itself could not carry out. */\n/** Result copy for leaving one listed session alone. */\nexport const TRIAGE_LEFT_TEXT = 'pause left this session paused; it is still on the list.'\n\n/** Result copy for a decision aimed at a session that is not on the list. */\nexport const TRIAGE_NOT_LISTED_TEXT = 'pause has no unfinished pause for this session.'\n\n/**\n * Result copy for a decision aimed at a row the calling session was not shown.\n * The list is the only thing that legitimizes a target, so a target nobody read\n * is refused rather than acted on.\n */\nexport const TRIAGE_UNSHOWN_TEXT = 'pause did not act: this Session has not been shown that row.'\n + ' Read the decision list first.'\n\n/**\n * Model-facing text that goes with a wake when the user chooses to continue\n * with a note.\n *\n * It is written the way the harness writes its own recovery text: the same two\n * facts the synthetic tool result states (the call's outcome was never recorded;\n * verify before retrying anything with side effects), in the same voice, with no\n * mention of the stop mechanism. The harness's own version lives in the tool\n * result for that call, which is why this one does not repeat the call's name —\n * it warns about the step as a whole, and the specific result sits directly\n * above it in the history.\n */\nexport const UNKNOWN_OUTCOME_NOTE = 'A tool call from this step was interrupted: its call was recorded but no '\n + 'result was durably recorded, so its outcome is unknown. If it had side effects, verify the external state '\n + 'before doing anything else, and do not retry it blindly. Then continue with what I asked for.'\n\n/**\n * One registry entry this list owns: every park outcome but `safe`, which is\n * the silent path's business and never appears here.\n */\ntype ListedPark = Omit<ParkedSession, 'phase'> & { readonly phase: TriageRow['phase'] }\n\n/**\n * One row's facts, as the session's own log reports them.\n * @param ctx - host context carrying the registered message projections.\n * @param entry - the durable park record this row is built from.\n * @param events - the events the bounded read returned, in sequence order.\n * @param complete - whether those events are the whole stored log.\n * @returns the row, ready to publish, or `undefined` when the read cannot support it.\n */\nfunction rowOf(\n ctx: Context,\n entry: ListedPark,\n events: readonly SessionEvent[],\n complete: boolean,\n): TriageRow | undefined {\n const { sessionId, phase, updatedAt, rearmGoal } = entry\n const facts = logFacts(events, complete, ctx.sessions.messageProjections)\n // A log this read cannot fold into those facts says nothing rather than\n // something: the row reports itself unreadable, which is what a list that\n // must not lie does when it does not know.\n if (facts === undefined) return undefined\n const { last, traces, owed, wakeable } = facts\n return {\n sessionId: String(sessionId),\n phase,\n updatedAt,\n reading: 'read',\n ...last === undefined ? {} : { lastEvent: last.type, ...positionOf(last) },\n traces,\n owed,\n rearmGoal,\n // Two facts have to hold before this host can hand the session its owed\n // request: the log must name the route the wake would be addressed to, and\n // there must be an answer still owed for the wake to ask for.\n wakeable,\n }\n}\n\n/** What mounting the decision list hands back. */\nexport interface TriageListService {\n /**\n * Read the list for one calling session, and record the rows it was shown.\n * @param caller - the session the read was addressed to.\n * @param signal - cancellation for the stored-log reads.\n * @returns every session that did not stop cleanly, with the facts its own log carries.\n */\n read(caller: SessionId, signal?: AbortSignal): Promise<TriageList>\n /**\n * Carry out one decision on one listed session, for the caller that read it.\n * @param caller - the session the decision was addressed to.\n * @param sessionId - the listed session to act on.\n * @param action - which of the listed decisions was taken.\n * @param signal - cancellation for the cold load and setup.\n * @returns the outcome for whoever asked.\n */\n decide(\n caller: SessionId,\n sessionId: SessionId,\n action: TriageAction,\n signal?: AbortSignal,\n ): Promise<PauseAnswer>\n}\n\n/**\n * Mount the decision list over the durable registry and the clean resume path.\n *\n * The list is the registry's own complement: it asks for every record whose\n * phase is not `safe`, so a session that cleaned up is off the list by the same\n * fact that puts it on the silent path, and nothing has to be reconciled.\n * @param ctx - host context carrying the optional persistence service.\n * @param registry - the durable list of park outcomes.\n * @param resumePath - the path all three resuming decisions go through.\n * @param windowEvents - how many events of each stored log one read may fold.\n * @returns the list read and the decision verbs.\n */\nexport function mountTriageList(\n ctx: Context,\n registry: ParkRegistry,\n resumePath: ResumePath,\n windowEvents: number = TRIAGE_WINDOW_EVENTS,\n): TriageListService {\n /**\n * The rows each calling session was last shown by its own read. A decision\n * names its target as wire input, and the channel carries no\n * origin of its own, so this is what ties a target to the list the user read:\n * only a row this caller has been shown is acted on.\n */\n const shown = new Map<SessionId, ReadonlySet<string>>()\n\n const unreadable = (entry: ListedPark): TriageRow => ({\n sessionId: String(entry.sessionId),\n phase: entry.phase,\n updatedAt: entry.updatedAt,\n reading: 'unreadable',\n traces: [],\n owed: false,\n wakeable: false,\n rearmGoal: entry.rearmGoal,\n })\n\n const readable = async (entry: ListedPark, signal?: AbortSignal): Promise<TriageRow> => {\n const read = await coldRead(ctx, entry.sessionId, windowEvents, signal)\n // A log this host could not read says so. An empty row would claim the\n // session left no traces, which is precisely the verdict nobody has.\n if (read.kind === 'unreadable') return unreadable(entry)\n // Facts this bounded read cannot support say the same thing: the row is\n // reported unreadable rather than filled with a guess.\n return rowOf(ctx, entry, read.events, read.complete) ?? unreadable(entry)\n }\n\n /**\n * One decision, as the resume path's own verbs plus the two local verbs that\n * change nothing but the record. Review is the list's own \"do not run it\"\n * answer: it activates nothing and writes nothing, so the park is exactly as\n * it was. The goal toggle is the user path for the one thing a resume can\n * spend unasked.\n */\n const decide = async (\n caller: SessionId,\n sessionId: SessionId,\n action: TriageAction,\n signal?: AbortSignal,\n ): Promise<PauseAnswer> => {\n if (!(shown.get(caller)?.has(String(sessionId)) ?? false)) return { kind: 'error', text: TRIAGE_UNSHOWN_TEXT }\n const record = registry.get(sessionId)\n // `safe` is not a decision this list owns: that session belongs to the\n // silent path, and acting here would be a second resume.\n if (record === undefined || record.phase === 'safe') {\n return { kind: 'error', text: TRIAGE_NOT_LISTED_TEXT }\n }\n if (action === 'leave') return { kind: 'success', text: TRIAGE_LEFT_TEXT }\n if (action === 'goal') return await resumePath.setRearmGoal(sessionId, !record.rearmGoal)\n // Review is its own kind of decision, not a wake-less resume: it leaves the\n // park exactly as it was, so the row is still there for tomorrow and\n // nothing has been continued — or even activated — on the user's behalf.\n if (action === 'review') {\n return (await resumePath.decideUnclean(sessionId, { kind: 'none' }, 'review', signal)).answer\n }\n const wake: WakeOf = action === 'continue' ? { kind: 'silent' } : { kind: 'message', text: UNKNOWN_OUTCOME_NOTE }\n return (await resumePath.decideUnclean(sessionId, wake, 'release', signal)).answer\n }\n\n return {\n read: async (caller, signal) => {\n signal?.throwIfAborted()\n const rows = await Promise.all([...registry.parked.values()]\n .flatMap(entry => entry.phase === 'safe' ? [] : [readable({ ...entry, phase: entry.phase }, signal)]))\n shown.set(caller, new Set(rows.map(row => row.sessionId)))\n return { rows }\n },\n decide,\n }\n}\n","/**\n * Pure projection of the durable park and each live tree into the native\n * snapshot. This module owns the phase fold and the goal opt-out; it does not\n * press, release, or decide anything.\n * @module dsh-pause-session/snapshot\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport { walkDescendants } from './tree.js'\nimport type { TreeWalk } from './tree.js'\nimport { foldTreePhases } from './shared.js'\nimport type { ParkMark } from './park-state.js'\nimport type { GlobalStatus, ParkPhase, ParkStatus, PauseSnapshot } from './shared.js'\n\n/** What the snapshot projection needs from the control. */\nexport interface SnapshotDeps {\n readonly ctx: Context\n readonly marks: ReadonlyMap<SessionId, ParkMark>\n readonly watch: ParkWatch\n readonly registry: ParkRegistry\n /** Read the whole-app aggregate, reusing the walks this projection already paid for. */\n readGlobal(signal?: AbortSignal, walks?: ReadonlyMap<SessionId, TreeWalk>): Promise<GlobalStatus>\n}\n\n/** The phase a durable record holds for one session, live or adopted. */\nfunction recordedPhase(deps: SnapshotDeps, sessionId: SessionId): ParkPhase | undefined {\n return deps.marks.get(sessionId)?.phase ?? deps.registry.get(sessionId)?.phase\n}\n\n/** Fold one session's mark and its registered descendants into the header reading. */\nasync function treeStatus(\n deps: SnapshotDeps,\n sessionId: SessionId,\n mark: ParkMark,\n signal?: AbortSignal,\n knownWalk?: TreeWalk,\n): Promise<ParkStatus> {\n const walk = knownWalk ?? await walkDescendants(deps.ctx, sessionId, signal)\n const phases: ParkPhase[] = [mark.phase]\n const extraRunning: string[] = []\n for (const member of walk.members) {\n const phase = recordedPhase(deps, member.id)\n if (phase !== undefined) {\n phases.push(phase)\n continue\n }\n // A member this press could not register (one-shot/external route) is\n // still part of the tree. If it is doing work, the tree is not safe.\n if (member.mode !== 'continuable') {\n const activity = deps.watch.activity(member.id)\n if (activity.turnOpen || activity.runningTools.length > 0) {\n phases.push('draining')\n extraRunning.push(...activity.runningTools)\n }\n }\n }\n // An unread branch is unknown: it is neither clean nor a settled phase.\n for (let at = 0; at < walk.unread.length + (walk.unreadWhole ? 1 : 0); at += 1) phases.push('parking')\n\n const folded = foldTreePhases(phases)\n const runningTools = [\n ...(mark.phase === 'draining' ? deps.watch.activity(sessionId).runningTools : []),\n ...walk.members.flatMap(member => recordedPhase(deps, member.id) === 'draining'\n ? deps.watch.activity(member.id).runningTools\n : []),\n ...extraRunning,\n ]\n const record = deps.registry.get(sessionId)\n return {\n phase: folded.phase,\n safe: folded.safe,\n runningTools,\n ...record === undefined ? {} : { rearmGoal: record.rearmGoal },\n }\n}\n\n/**\n * Read every live mark and the whole-app aggregate in one pass.\n * @param deps - the control-owned marks, watch, registry, and aggregate reader.\n * @param signal - cancellation for the tree walks and aggregate.\n * @returns the native snapshot.\n */\nexport async function readSnapshot(\n deps: SnapshotDeps,\n signal?: AbortSignal,\n): Promise<PauseSnapshot> {\n const sessions: Record<string, ParkStatus> = {}\n const walks = new Map<SessionId, TreeWalk>()\n await Promise.all([...deps.marks].map(async ([sessionId, mark]) => {\n const walk = await walkDescendants(deps.ctx, sessionId, signal)\n // The aggregate has its own blind-spot reading for a host with no subagent\n // service; sharing an empty walk would erase that fact.\n if (deps.ctx.get('subagents') !== undefined) walks.set(sessionId, walk)\n sessions[String(sessionId)] = await treeStatus(deps, sessionId, mark, signal, walk)\n }))\n return { global: await deps.readGlobal(signal, walks), sessions }\n}\n","/**\n * One session's park episode: the durable phase writes, the activity walk that\n * settles them, and the press/release lifecycle. The control composes this with\n * the aggregate, the resume path, and the decision list.\n * @module dsh-pause-session/park-state\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { SessionId } from '@deepseek-ai/dsh-session'\nimport type { PauseGate } from './gate.js'\nimport type { LivePark, PressResult } from './global.js'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport { isResumableSession } from './composition.js'\nimport { describeError, UNRECORDED_TEXT } from './shared.js'\nimport type { ParkPhase, PauseAnswer } from './shared.js'\n\n/** One session's park in progress. */\nexport interface ParkMark {\n /** The phase the durable registry confirms for this session. */\n phase: ParkPhase\n /** Generation of the attempt that owns this mark. */\n generation: number\n /** Whether this mark is a live press or a record adopted at mount. */\n origin: 'press' | 'registry'\n /** Whether a settle attempt is already walking this mark. */\n advancing: boolean\n}\n\n/** The park lifecycle the session control composes. */\nexport interface ParkState {\n readonly marks: Map<SessionId, ParkMark>\n readonly unrecorded: Set<SessionId>\n readonly unheld: Set<SessionId>\n subscribe(listener: () => void): () => void\n notify(): void\n press(sessionId: SessionId, signal?: AbortSignal, knownResumable?: boolean): Promise<PressResult>\n /** Adopt one durable record as a mark that never advances in this process. */\n adopt(sessionId: SessionId, phase: ParkPhase): void\n look(sessionId: SessionId): void\n drop(sessionId: SessionId): void\n sync(sessionId: SessionId): void\n release(sessionId: SessionId, text: string): Promise<PauseAnswer>\n markUnrecorded(sessionId: SessionId, unrecorded: boolean): void\n live(): Map<SessionId, LivePark>\n cleanup(): void\n}\n\n/**\n * Mount the park episode state machine over the gate, registry, and activity fold.\n * @param ctx - host context carrying the session store and logger.\n * @param gate - the pause gate this state drives.\n * @param registry - the durable pause list every phase write goes to.\n * @param watch - the activity fold the park predicate reads.\n * @returns the park lifecycle the control composes.\n */\nexport function installParkState(\n ctx: Context,\n gate: PauseGate,\n registry: ParkRegistry,\n watch: ParkWatch,\n): ParkState {\n const marks = new Map<SessionId, ParkMark>()\n const unrecorded = new Set<SessionId>()\n const unheld = new Set<SessionId>()\n const listeners = new Set<() => void>()\n let notification: ReturnType<typeof setTimeout> | undefined\n let generation = 0\n\n /** One snapshot per burst of host changes, not one per phase write. */\n const notify = (): void => {\n if (notification !== undefined) return\n notification = setTimeout(() => {\n notification = undefined\n for (const listener of [...listeners]) listener()\n }, 20)\n }\n\n const drop = (sessionId: SessionId): void => {\n marks.delete(sessionId)\n notify()\n }\n\n const commit = async (sessionId: SessionId, mark: ParkMark, phase: ParkPhase): Promise<boolean> => {\n try {\n await registry.record(sessionId, phase)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not record the park state of \"${sessionId}\" as ${phase}: ${describeError(error)}`)\n return false\n }\n const changed = mark.phase !== phase\n mark.phase = phase\n if (changed) notify()\n return true\n }\n\n const parked = (sessionId: SessionId): boolean => {\n const activity = watch.activity(sessionId)\n if (activity.runningTools.length > 0) return false\n return gate.isHeld(sessionId) || !activity.turnOpen\n }\n\n const advance = (sessionId: SessionId): boolean => {\n const mark = marks.get(sessionId)\n if (mark === undefined || mark.phase !== 'draining' || mark.advancing) return false\n if (mark.origin === 'registry') return false\n if (!parked(sessionId)) return false\n mark.advancing = true\n return true\n }\n\n const settle = async (sessionId: SessionId, attempt: number): Promise<void> => {\n const mark = marks.get(sessionId)\n if (mark === undefined || mark.generation !== attempt) return\n const owned = (): boolean => marks.get(sessionId) === mark && mark.generation === attempt\n const session = ctx.sessions.get(sessionId)\n try {\n if (session === undefined) {\n await commit(sessionId, mark, 'parking')\n return\n }\n await commit(sessionId, mark, 'parking')\n if (!owned()) return\n try {\n const flushed = await ctx.sessions.flush(session)\n if (!flushed) {\n await commit(sessionId, mark, 'flush-failed')\n return\n }\n } catch (error: unknown) {\n if (!owned()) return\n ctx.logger.warn(`pause: flush failed for \"${sessionId}\": ${describeError(error)}`)\n await commit(sessionId, mark, 'flush-failed')\n return\n }\n if (!owned()) return\n await commit(sessionId, mark, 'safe')\n } finally {\n if (marks.get(sessionId) === mark && mark.phase === 'draining') mark.advancing = false\n }\n }\n\n const look = (sessionId: SessionId): void => {\n if (marks.has(sessionId)) notify()\n if (!advance(sessionId)) return\n const attempt = marks.get(sessionId)?.generation\n if (attempt !== undefined) void settle(sessionId, attempt)\n }\n\n const press = async (\n sessionId: SessionId,\n signal?: AbortSignal,\n knownResumable = false,\n ): Promise<PressResult> => {\n if (ctx.sessions.get(sessionId) === undefined) return 'not-live'\n if (!knownResumable && !await isResumableSession(ctx, sessionId, signal)) return 'not-resumable'\n if (!gate.pause(sessionId)) return 'already-paused'\n const mark: ParkMark = { phase: 'draining', generation: ++generation, origin: 'press', advancing: false }\n marks.set(sessionId, mark)\n if (await commit(sessionId, mark, 'draining')) {\n unheld.delete(sessionId)\n notify()\n return 'registered'\n }\n drop(sessionId)\n gate.resume(sessionId)\n return 'unrecordable'\n }\n\n const release = async (sessionId: SessionId, text: string): Promise<PauseAnswer> => {\n if (!gate.isPaused(sessionId)) return { kind: 'success', text }\n try {\n await registry.forget(sessionId)\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not drop \"${sessionId}\" from the pause registry: ${describeError(error)}`)\n return { kind: 'error', text: UNRECORDED_TEXT }\n }\n gate.resume(sessionId)\n notify()\n return { kind: 'success', text }\n }\n\n const sync = (sessionId: SessionId): void => {\n if (!gate.isPaused(sessionId)) {\n drop(sessionId)\n return\n }\n }\n\n return {\n marks,\n unrecorded,\n unheld,\n subscribe: (listener) => {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n notify,\n press,\n adopt: (sessionId, phase) => {\n marks.set(sessionId, { phase, generation: ++generation, origin: 'registry', advancing: false })\n },\n look,\n drop,\n sync,\n release,\n markUnrecorded: (sessionId, blocked) => {\n if (blocked) unrecorded.add(sessionId)\n else unrecorded.delete(sessionId)\n },\n live: () => new Map<SessionId, LivePark>([...marks].map(([sessionId, mark]) => [sessionId, {\n phase: mark.phase,\n moving: mark.origin === 'press' && mark.phase !== 'flush-failed' && ctx.sessions.get(sessionId) !== undefined,\n }])),\n cleanup: () => {\n if (notification !== undefined) clearTimeout(notification)\n marks.clear()\n listeners.clear()\n },\n }\n}\n","/**\n * The session control the browser half drives: the pause verb, the park state\n * machine behind it, and the live state channel that accompanies both.\n *\n * The host exposes this control through the native Typert Remote namespace:\n * `observe` streams snapshots and `act` / `triage` / `decide` drive the UI. No\n * slash command is registered. Phase changes wake the Remote stream through a\n * local listener set; the durable registry remains the source of truth for what\n * the snapshot reports.\n * @module dsh-pause-session/control\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { SessionId } from '@deepseek-ai/dsh-session'\n// Type-only: the optional `ctx.sessionPersistence` observation seam.\nimport type {} from '@deepseek-ai/dsh-session-persistence'\nimport type { PauseGate } from './gate.js'\nimport { mountGlobalPause } from './global.js'\nimport type { GlobalPause } from './global.js'\nimport type { ParkWatch } from './park.js'\nimport type { ParkRegistry } from './registry.js'\nimport { installResumePath, NOT_PARKED_TEXT } from './resume.js'\nimport type { ReleaseTreeOutcome, ResumeAttempt, ResumeOptions, ResumePath } from './resume.js'\nimport { mountTriageList } from './triage.js'\nimport type { TriageListService } from './triage.js'\nimport type { TriageAction } from './shared.js'\nimport { describeError } from './shared.js'\nimport type { PauseSnapshot, TriageList } from './shared.js'\nimport { readSnapshot as readSnapshotOf } from './snapshot.js'\nimport { installParkState } from './park-state.js'\nimport { walkDescendants } from './tree.js'\nimport type { PauseAnswer } from './shared.js'\n\n/**\n * Whether this host can read one recorded session at all — a live session, or a\n * stored log the persistence backend can observe.\n * @param ctx - host context carrying the session store and the optional persistence service.\n * @param sessionId - the recorded session to look for.\n * @returns whether the session can be acted on here.\n */\nasync function sessionReadable(ctx: Context, sessionId: SessionId): Promise<boolean> {\n if (ctx.sessions.get(sessionId) !== undefined) return true\n const persistence = ctx.get('sessionPersistence')\n if (persistence === undefined) return false\n try {\n return await persistence.stat(sessionId) !== undefined\n } catch (error: unknown) {\n ctx.logger.warn(`pause: could not look for the stored log of \"${sessionId}\": ${describeError(error)}`)\n return false\n }\n}\n\n/** What one tree-wide press registered, and what it could not. */\nexport interface PressTreeOutcome {\n /** How many sessions this press covered: the session plus its descendants. */\n readonly members: number\n /** How many of them now have a durable park record behind them. */\n readonly registered: number\n /** Whether the pressed session itself is one of them. */\n readonly rootRegistered: boolean\n /** The members whose record could not be written; they are not paused. */\n readonly unrecorded: readonly SessionId[]\n /** Whether any branch was unread, so the subagent tree was not fully covered. */\n readonly unread: boolean\n /** Tree members on a one-shot or unreadable route; they were not registered. */\n readonly unresumable: readonly SessionId[]\n /** Branch ids the tree walk could not read. */\n readonly unreadBranches: readonly SessionId[]\n}\n\n/** What mounting the session control hands back to the plugin entry. */\nexport interface SessionControl {\n /**\n * Hold every session the registry remembers and can read, and publish the\n * marker for the ones that already have a live agent. Called once, right\n * after mount.\n */\n adopt(): Promise<void>\n /**\n * Read the whole app and every live paused session as one JSON-safe snapshot.\n * The native snapshot serves this; it never enters a Session log.\n * @param signal - cancellation for the tree reads this snapshot performs.\n * @returns the whole-app verdict and the paused sessions this process holds.\n */\n readSnapshot(signal?: AbortSignal): Promise<PauseSnapshot>\n /** Wake a listener after any visible status change. */\n subscribe(listener: () => void): () => void\n /** Press one Session tree. */\n pressTree(sessionId: SessionId, signal?: AbortSignal): Promise<PressTreeOutcome>\n /** Release one Session's registered tree. */\n releaseTree(sessionId: SessionId, signal?: AbortSignal): Promise<ReleaseTreeOutcome>\n /** Press every running Session tree. */\n pauseAll(signal?: AbortSignal): Promise<PauseAnswer>\n /** Resume every cleanly paused Session. */\n resumeAll(signal?: AbortSignal): Promise<PauseAnswer>\n /** Release every registered tree, cancelling a press that is still draining. */\n cancelAll(signal?: AbortSignal): Promise<PauseAnswer>\n /** Flip one Session's goal re-arm opt-out. */\n toggleGoal(sessionId: SessionId, signal?: AbortSignal): Promise<PauseAnswer>\n /** Read the morning list for one calling Session. */\n readTriage(caller: SessionId, signal?: AbortSignal): Promise<TriageList>\n /** Take one listed decision for one calling Session. */\n decideTriage(\n caller: SessionId,\n target: SessionId,\n action: TriageAction,\n signal?: AbortSignal,\n ): Promise<PauseAnswer>\n}\n\n/** How resuming all parked sessions answers the caller. */\nexport function summarizeResume(attempts: readonly ResumeAttempt[]): string {\n const resumed = attempts.filter(attempt => attempt.resumed).length\n const refused = attempts.length - resumed\n const head = resumed === 0\n ? 'pause resumed no session'\n : resumed === 1\n ? 'pause resumed 1 session from where it stopped'\n : `pause resumed ${resumed} sessions from where they stopped`\n if (refused === 0) return `${head}.`\n const unclean = attempts.filter(attempt => attempt.refusal === 'not-clean').length\n const rest = unclean === refused\n ? `${refused} did not pause completely and need your decision`\n : `${refused} could not be resumed`\n return `${head}; ${rest}.`\n}\n\n/**\n * Everything the session control needs from the composition: the clean resume\n * path's route knowledge, plus the bound the decision list reads each parked\n * log under.\n */\nexport interface SessionControlOptions extends ResumeOptions {\n /** Events of each stored log the decision list may fold per row; see `./coldlog.ts`. */\n readonly maxListedLogEvents?: number\n}\n\n/**\n * Contribute the session control: the pause verb, the per-session status read,\n * the global resume verb, and — for every paused session that has a live agent\n * — the resume marker.\n * @param ctx - host context carrying the agent store, the\n * session store, and the projection registry.\n * @param gate - the pause gate this control drives and reflects.\n * @param watch - the per-session activity fold the park predicate reads.\n * @param registry - the durable pause list every phase change is written to.\n * @param options - route knowledge the clean resume path needs, and the decision list's read bound.\n * @returns the mount-time adoption step and the morning resume path.\n */\nexport function installSessionControl(\n ctx: Context,\n gate: PauseGate,\n watch: ParkWatch,\n registry: ParkRegistry,\n options: SessionControlOptions,\n): SessionControl {\n const park = installParkState(ctx, gate, registry, watch)\n\n /**\n * Press one session's pause and register its whole tree behind it.\n *\n * \"One session's work\" includes the subagents it delegated to, nested ones\n * included, and the user does not pause them one by one. So the press walks\n * the session's own catalog tree and gives every member the same durable\n * treatment the session gets: {@link press} registers what it can hold, and a\n * member whose record cannot be written is named here rather than counted as\n * paused.\n *\n * A tree the walk could not read is the one case where the press does less\n * than the tree: the session itself is still registered — the user pressed a\n * session, and its park is real — and the answer says the subagents were not\n * covered rather than counting a tree nobody read.\n * @param sessionId - the session whose tree the user pressed.\n * @returns which members are registered and which could not be.\n */\n const pressTree = async (sessionId: SessionId, signal?: AbortSignal): Promise<PressTreeOutcome> => {\n // Nothing live means nothing this press could hold; the check is also what\n // keeps a session disposed in the middle of a press on the path that\n // records the park point it reached.\n if (ctx.sessions.get(sessionId) === undefined) {\n return { members: 0, registered: 0, rootRegistered: false, unrecorded: [], unread: false, unresumable: [], unreadBranches: [] }\n }\n const walk = await walkDescendants(ctx, sessionId, signal)\n // A tree this press could not read is not a reason to drop the session\n // itself: the user pressed a session, and its own park is still real. What\n // is refused is the claim about the tree, which is why the count stays at\n // the one member this press actually covers.\n if (walk.unreadWhole) {\n const pressed = await park.press(sessionId, signal)\n // The look is unconditional on purpose only after a mark exists: a\n // session that left the store during this press has a mark and cannot be\n // flushed, which is the one way its park still reaches a durable \"not\n // clean\" instead of a phase that never settles.\n if (pressed === 'registered') park.look(sessionId)\n globalPause.markUnrecorded(sessionId, pressed === 'unrecordable')\n return {\n members: pressed === 'registered' ? 1 : 0,\n registered: pressed === 'registered' ? 1 : 0,\n rootRegistered: pressed === 'registered',\n unrecorded: pressed === 'unrecordable' ? [sessionId] : [],\n unread: true,\n unresumable: [],\n unreadBranches: [],\n }\n }\n const registered: SessionId[] = []\n const unrecorded: SessionId[] = []\n const unresumable = walk.members.filter(member => member.mode !== 'continuable').map(member => member.id)\n const unreadBranches = walk.unread.map(branch => branch.id)\n const members = walk.members.filter(member => member.mode === 'continuable')\n for (const id of [sessionId, ...members.map(member => member.id)]) {\n // The press is per member on purpose: one member that cannot be recorded\n // is named while the rest keep the pause they got. A member on a\n // one-shot or external route is never registered: there is no later\n // resume that could honestly satisfy that promise.\n const pressed = await park.press(id, signal, id !== sessionId)\n if (pressed === 'registered') {\n registered.push(id)\n // A member this host has nothing live for has no mark to walk; one that\n // does is looked at now, exactly as the session's own press looked at\n // itself.\n park.look(id)\n } else if (pressed === 'unrecordable') {\n unrecorded.push(id)\n }\n // The aggregate owns the same fact, so a member the registry refused is\n // named by the whole-app report too. A skipped member is not a failed\n // promise and is not named as one.\n globalPause.markUnrecorded(id, pressed === 'unrecordable')\n }\n return {\n members: registered.length + unrecorded.length,\n registered: registered.length,\n rootRegistered: registered.includes(sessionId),\n unrecorded,\n unread: unreadBranches.length > 0,\n unresumable,\n unreadBranches,\n }\n }\n\n // The resume verb, the global resume verb, and the goal opt-out ride one\n // path. It is mounted here because the release order and the marked origin\n // are this control's own facts.\n const resumePath: ResumePath = installResumePath(ctx, gate, registry, options, {\n release: park.release,\n adopted: sessionId => park.marks.get(sessionId)?.origin === 'registry',\n })\n\n // The decision list reads the same durable registry and acts through the same\n // resume path, so a session is on exactly one of the two morning paths: the\n // silent one when its record says `safe`, this one otherwise.\n const triage: TriageListService = mountTriageList(ctx, registry, resumePath, options.maxListedLogEvents)\n\n // The whole-app park reads the marks through this one accessor, so the\n // aggregate and the per-session control can never describe different states.\n const globalPause: GlobalPause = mountGlobalPause(ctx, watch, registry, {\n isPaused: sessionId => gate.isPaused(sessionId),\n press: park.press,\n look: park.look,\n live: park.live,\n unheld: () => park.unheld,\n })\n\n const readSnapshot = (signal?: AbortSignal): Promise<PauseSnapshot> =>\n readSnapshotOf({ ctx, marks: park.marks, watch, registry, readGlobal: globalPause.readState }, signal)\n\n // One effect owns the subscriptions and the mark cleanup. There is no\n // status is the Remote stream and actions are Remote methods.\n ctx.effect(function* () {\n yield gate.onChange(sessionId => { park.sync(sessionId) })\n yield gate.onHeld(sessionId => {\n queueMicrotask(() => { park.look(sessionId) })\n })\n yield watch.onChange(sessionId => { park.look(sessionId) })\n yield ctx.on('agent/created', ({ agent }) => {\n park.sync(agent.id)\n return undefined\n })\n yield async () => {\n park.cleanup()\n }\n }, 'pause: session events')\n\n return {\n readSnapshot,\n subscribe: park.subscribe,\n pressTree: async (sessionId, signal) => await pressTree(sessionId, signal),\n releaseTree: async (sessionId, signal) => await resumePath.releaseTree(sessionId, signal),\n pauseAll: async (signal) => {\n const answer = await globalPause.pressAll(signal)\n park.notify()\n return answer\n },\n cancelAll: async (signal) => {\n const outcomes = []\n // A tree can mix a clean member with a press that is still draining. The\n // clean release then resumes the clean member and keeps the draining one;\n // repeat so the remaining all-unclean tree can take the cancel path.\n for (let round = 0; round < 3; round += 1) {\n const before = registry.parked.size\n for (const sessionId of [...registry.parked.keys()]) {\n signal?.throwIfAborted()\n outcomes.push((await resumePath.releaseTree(sessionId, signal)).answer)\n }\n if (registry.parked.size === before) break\n }\n park.notify()\n const failed = outcomes.filter(answer => answer.kind === 'error')\n return failed.length === 0\n ? { kind: 'success', text: 'Every pause was released.' }\n : { kind: 'error', text: failed.map(answer => answer.text).join(' ') }\n },\n resumeAll: async (signal) => {\n const answer = { kind: 'success' as const, text: summarizeResume(await resumePath.resumeParked(signal)) }\n park.notify()\n return answer\n },\n toggleGoal: async (sessionId) => {\n const record = registry.get(sessionId)\n if (record === undefined) return { kind: 'error', text: NOT_PARKED_TEXT }\n const answer = await resumePath.setRearmGoal(sessionId, !record.rearmGoal)\n park.notify()\n return answer\n },\n readTriage: async (caller, signal) => await triage.read(caller, signal),\n decideTriage: async (caller, target, action, signal) => {\n const answer = await triage.decide(caller, target, action, signal)\n park.notify()\n return answer\n },\n adopt: async () => {\n for (const entry of registry.list()) {\n // Every registered session this host can read is held again, whether or\n // not anything is live: the request that wakes it is the one this gate\n // must catch, and the wake-up may come from a scheduled task or a\n // webhook long after this mount. A record whose session cannot be read\n // is not held — the id would be pinned with nothing behind it and no\n // verb could ever clear it — and is reported instead.\n if (!await sessionReadable(ctx, entry.sessionId)) {\n park.unheld.add(entry.sessionId)\n ctx.logger.warn(`pause: the pause recorded for \"${entry.sessionId}\" is not held:`\n + ' this host cannot read that session')\n continue\n }\n if (gate.pause(entry.sessionId)) park.adopt(entry.sessionId, entry.phase)\n }\n // A session that was already live when this instance mounted still owes\n // the browser the marker: the registry holds the list, and nothing else\n // re-publishes it for agents that predate this mount.\n for (const agent of ctx.agents.list()) park.sync(agent.id)\n },\n }\n}\n","/**\n * The pause gate: one session's hold at the model-request waterfall.\n *\n * The hold sits on the `llm/stream` waterfall, which is the only point that\n * satisfies all four conditions at once: the previous step's tool calls have\n * drained, the request is already built, the session's pending input is\n * committed, and — because nothing pulls the adapter — no timer is armed\n * anywhere. Machine sleep, network loss, and the adapter's stream-idle\n * watchdog therefore cannot touch a parked request.\n * @module dsh-pause-session/gate\n */\n\nimport type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\n\n/** Message of the failure a held request ends with when its plugin unloads first. */\nexport const PLUGIN_UNLOADED = 'pause: plugin lifetime ended while a request was held'\n\n/** One latch a paused request waits on. */\ninterface Latch {\n readonly promise: Promise<void>\n readonly open: () => void\n}\n\n/** One paused session and the latch every request of that session waits on. */\ninterface Pause {\n readonly sessionId: SessionId\n readonly released: Promise<void>\n release(): void\n /**\n * Requests of this session waiting at the gate right now. Membership in\n * `paused` says the session is marked; this count says a request has\n * actually arrived and stopped, which is a different fact — the one the\n * park state is built from.\n */\n holding: number\n}\n\n/** The abort signal's own error, or a stable one when the abort carried no reason. */\nfunction abortError(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('pause: request aborted while held')\n}\n\n/** A latch without `Promise.withResolvers`, which this project's ES2022 lib predates. */\nfunction latch(): Latch {\n let open: () => void = () => {}\n const promise = new Promise<void>((resolve) => { open = resolve })\n return { promise, open }\n}\n\n/**\n * Resolve once the session is released or any of `signals` aborts. No timer is\n * armed: the wait ends only by release or by an abort, so a parked request\n * cannot be woken by the clock. Listeners are removed once the wait settles.\n */\nfunction raceRelease(released: Promise<void>, signals: readonly AbortSignal[]): Promise<void> {\n const wake = latch()\n const onAbort = (): void => { wake.open() }\n const watching = signals.filter(signal => !signal.aborted)\n if (watching.length !== signals.length) wake.open()\n for (const signal of watching) signal.addEventListener('abort', onAbort, { once: true })\n return Promise.race([released, wake.promise]).finally(() => {\n for (const signal of watching) signal.removeEventListener('abort', onAbort)\n })\n}\n\n/** Options of {@link PauseGate}. */\nexport interface PauseGateOptions {\n /** Aborted when the owning plugin unloads; a held request must never delegate after that. */\n readonly lifetime: AbortSignal\n}\n\n/** Per-session pause state and the waterfall listener that enforces it. */\nexport class PauseGate {\n /** Paused sessions; membership IS the pause state. */\n private readonly paused = new Map<SessionId, Pause>()\n private readonly listeners = new Set<(sessionId: SessionId, paused: boolean) => void>()\n private readonly heldListeners = new Set<(sessionId: SessionId, held: boolean) => void>()\n\n /**\n * @param options - the plugin-lifetime signal every held request races.\n */\n constructor(private readonly options: PauseGateOptions) {}\n\n /**\n * Subscribe to pause-state changes.\n * @param listener - called with the session whose state changed and its new state.\n * @returns the unsubscribe function.\n */\n onChange(listener: (sessionId: SessionId, paused: boolean) => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /**\n * Subscribe to \"a request arrived at / left the gate\" for a paused session.\n *\n * This is the moment a session can become parked, so it is also the moment a\n * park observer has to look. Listeners run synchronously inside the request\n * path: they must only record that a look is owed.\n * @param listener - called with the session and whether it now holds a request.\n * @returns the unsubscribe function.\n */\n onHeld(listener: (sessionId: SessionId, held: boolean) => void): () => void {\n this.heldListeners.add(listener)\n return () => { this.heldListeners.delete(listener) }\n }\n\n /**\n * @param sessionId - session to inspect.\n * @returns whether the session is currently paused.\n */\n isPaused(sessionId: SessionId): boolean {\n return this.paused.has(sessionId)\n }\n\n /**\n * @param sessionId - session to inspect.\n * @returns whether a model request of that session is waiting at the gate\n * right now. False for a session that is paused but whose loop has not built\n * its next request yet — a running tool, for instance, is still draining.\n */\n isHeld(sessionId: SessionId): boolean {\n return (this.paused.get(sessionId)?.holding ?? 0) > 0\n }\n\n /**\n * Enter one request that is about to wait. The count rises synchronously with\n * {@link hold}, so an observer notified at that moment reads `isHeld` as true.\n * @param pause - the session's pause record.\n * @returns the exit that lowers the count again.\n */\n private enter(pause: Pause): () => void {\n pause.holding += 1\n this.notifyHeld(pause.sessionId, true)\n return () => {\n pause.holding -= 1\n if (pause.holding === 0) this.notifyHeld(pause.sessionId, false)\n }\n }\n\n /**\n * Pause one session: its next model request is held until {@link resume}. A\n * request already past the waterfall keeps running — pausing never\n * interrupts a model call or a tool call that has started.\n * @param sessionId - session to pause.\n * @returns whether this call changed the state.\n */\n pause(sessionId: SessionId): boolean {\n if (this.paused.has(sessionId)) return false\n const release = latch()\n this.paused.set(sessionId, { sessionId, released: release.promise, release: release.open, holding: 0 })\n this.notify(sessionId, true)\n return true\n }\n\n /**\n * Resume one session: release everything it holds and stop holding what\n * follows. The requests sent from then on are the ones built before the\n * hold, so a resumed session continues from the point it stopped.\n * @param sessionId - session to resume.\n * @returns whether this call changed the state.\n */\n resume(sessionId: SessionId): boolean {\n const pause = this.paused.get(sessionId)\n if (pause === undefined) return false\n this.paused.delete(sessionId)\n pause.release()\n this.notify(sessionId, false)\n return true\n }\n\n /**\n * The wait one model request must finish before it may delegate, and the exit\n * that reports the request as no longer waiting.\n * @param request - the request reaching the waterfall.\n * @returns the wait plus its release, or `undefined` when this gate does not\n * hold that request.\n */\n hold(request: GenerateOptions): { readonly wait: Promise<void>; readonly left: () => void } | undefined {\n // Context compaction and session titling are background model work inside\n // the same session and carry a purpose; a session pause must never freeze\n // them. Only a request the loop built for its own turn is gateable.\n if (request.purpose !== undefined) return undefined\n const sessionId = request.sessionId\n if (sessionId === undefined) return undefined\n const pause = this.paused.get(sessionId)\n if (pause === undefined) return undefined\n const left = this.enter(pause)\n const wait = raceRelease(\n pause.released,\n request.signal === undefined ? [this.options.lifetime] : [request.signal, this.options.lifetime],\n )\n return { wait, left }\n }\n\n /**\n * The `llm/stream` listener.\n *\n * `ctx.waterfall` composes listeners synchronously, so this returns an async\n * iterator synchronously — an `async` listener would hand the loop a promise\n * where it iterates a stream. One generator body reaches `next()` at most\n * once, which is what makes delegation single-shot per HELD request; a flag\n * shared across requests would also short-circuit everything issued after\n * the release.\n * @param request - the full request, already built and frozen by the loop.\n * @param next - the downstream waterfall chain.\n * @returns the stream, delayed until this request may delegate.\n */\n readonly stream = (request: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk> => {\n const gate = this\n return (async function* gated(): AsyncIterable<StreamChunk> {\n const held = gate.hold(request)\n try {\n if (held !== undefined) await held.wait\n // The race decides when to stop waiting; the signals decide whether\n // delegating is allowed at this exact moment. A request whose phase\n // aborted, or whose plugin unloaded, must never reach the adapter.\n if (request.signal?.aborted === true) throw abortError(request.signal)\n if (gate.options.lifetime.aborted) throw new Error(PLUGIN_UNLOADED)\n yield* next()\n } finally {\n // Leaving is what tells an observer the session stopped standing at\n // the park point — whether it delegated or aborted.\n held?.left()\n }\n })()\n }\n\n private notify(sessionId: SessionId, paused: boolean): void {\n for (const listener of [...this.listeners]) listener(sessionId, paused)\n }\n\n private notifyHeld(sessionId: SessionId, held: boolean): void {\n for (const listener of [...this.heldListeners]) listener(sessionId, held)\n }\n}\n","/**\n * What a session is doing between \"pause was asked for\" and \"its next model\n * request is standing still at the gate\".\n *\n * Two facts decide that, and each has its own owner:\n *\n * - Whether a turn is open is derived state the harness already maintains:\n * the agent registry owns the `turnBoundary` session projection, which folds\n * `turn/start`, `turn/end`, `step/start`, and `step/end` into\n * `openTurnStartSeq`. Reading it through `ctx.agents` is a synchronous\n * projection read, not a scan of the event log (synchronous history reads are\n * prohibited for new code).\n * - Which tool is in flight is a log-derived per-session fact, so it is a\n * session projection too. The projection folds each committed\n * `tool/call` → `tool/result` pairing and keeps the calls in order; the\n * framework owns replay, caching, and disposal. This module listens only to\n * committed events so it can wake the control, never to keep a second copy\n * of the state itself.\n * @module dsh-pause-session/park\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { Session, SessionId } from '@deepseek-ai/dsh-session'\nimport type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'\nimport { z } from 'zod'\n// Type-only: pulls the agent registry's Context merge (ctx.agents).\nimport type {} from '@deepseek-ai/dsh-agent'\n\n/** One tool call the session has committed but not yet answered. */\nconst openCallSchema = z.object({\n /** The provider-issued id pairing the call with the result that has not arrived. */\n callId: z.string(),\n /** The tool's own name, as the model called it. */\n name: z.string(),\n})\n\n/** The host-only projection state: open calls in call order. */\ntype OpenToolCall = z.infer<typeof openCallSchema>\n\n/**\n * The host-only fold behind {@link ParkWatch}. It is registered on the\n * plugin's fiber; unload removes it with every other registration.\n */\nconst parkActivityProjection: ProjectionDefinition<'dsh-pause-session/activity', OpenToolCall[]> = {\n key: 'dsh-pause-session/activity',\n stateSchema: z.array(openCallSchema),\n init: () => [],\n apply: (state, event) => {\n if (event.type === 'tool/call') {\n return [...state, { callId: String(event.data.callId), name: event.data.name }]\n }\n if (event.type === 'tool/result') {\n const callId = String(event.data.message.toolCallId)\n const at = state.findIndex(call => call.callId === callId)\n if (at < 0) return state\n return [...state.slice(0, at), ...state.slice(at + 1)]\n }\n return state\n },\n stateVersion: 1,\n}\n\n/** One session's current activity, as the control reports it. */\nexport interface ParkActivity {\n /** Whether the session is inside a turn — including one waiting for this very park. */\n readonly turnOpen: boolean\n /** Names of the calls currently in flight, in call order; empty when no tool is running. */\n readonly runningTools: readonly string[]\n}\n\n/** Both halves of the activity question for one session. */\nexport interface ParkWatch {\n /**\n * @param sessionId - session to inspect.\n * @returns the session's current turn and tool activity.\n */\n activity(sessionId: SessionId): ParkActivity\n /**\n * Subscribe to activity changes.\n * @param listener - called with the session whose activity changed; it must\n * only record that a look is owed, never re-enter the session.\n * @returns the unsubscribe function.\n */\n onChange(listener: (sessionId: SessionId) => void): () => void\n}\n\n/** Whether the agent registry's `turnBoundary` fold reports an open turn. */\nfunction turnOpen(ctx: Context, session: Session): boolean {\n return ctx.agents.turnBoundaryOf(session).openTurnStartSeq !== null\n}\n\n/**\n * Mount the per-session activity projection and its notification edge.\n * @param ctx - host context carrying the session store and the projection registry.\n * @returns the watch the park state is derived from.\n */\nexport async function mountParkWatch(ctx: Context): Promise<ParkWatch> {\n const activity = await ctx.sessionProjections.register(parkActivityProjection)\n const listeners = new Set<(sessionId: SessionId) => void>()\n\n const notify = (sessionId: SessionId): void => {\n for (const listener of [...listeners]) listener(sessionId)\n }\n\n ctx.effect(() => ctx.on('session/event', (session, event) => {\n switch (event.type) {\n case 'tool/call':\n case 'tool/result':\n // The projection owns the open-call list; the committed event only\n // says the control owes a look at it.\n notify(session.id)\n return\n case 'turn/start':\n case 'turn/end':\n // Turn state itself is read from the projection, but a turn boundary is\n // the other half of \"is this session standing still\": a turn that ends\n // with no further request can never produce the hold a park would wait\n // for, so the park predicate has to look again here.\n notify(session.id)\n return\n default:\n return\n }\n }), 'pause: session activity changes')\n\n return {\n activity: (sessionId) => {\n const session = ctx.sessions.get(sessionId)\n if (session === undefined) {\n // A session with no live log cannot be mid-turn; the park predicate\n // owns what that means.\n return { turnOpen: false, runningTools: [] }\n }\n return {\n turnOpen: turnOpen(ctx, session),\n runningTools: activity.stateOf(session).map(call => call.name),\n }\n },\n onChange: (listener) => {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n }\n}\n","/**\n * Native Remote transport for the pause controls.\n *\n * This module is deliberately self-contained: it imports only Cordis and the\n * Typert decorators. The Typert generator analyzes this file alone so a\n * third-party package can emit its Host reflection and Client contribution\n * without pulling the plugin's DSH-typed modules into the generation face.\n *\n * The UI uses this namespace on Web and Desktop alike. No slash command is\n * registered; the stream is the live status channel and the methods below are\n * the button actions.\n * @module dsh-pause-session/remote\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport { principalOf, Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { RemoteInvocation } from '@deepseek-ai/dsh-typert-protocol'\n\ndeclare module '@deepseek-ai/dsh-typert-protocol' {\n interface RemoteErrorDetailsMap {\n /** The pause control is personal; only the owner may read or drive it. */\n 'pause/owner-only': {}\n }\n}\n\n/** The phase a Session reports to the controls. */\nexport type PausePhaseWire = 'active' | 'draining' | 'parking' | 'safe' | 'flush-failed'\n\n/** One Session's pause state. */\nexport interface SessionStatusWire {\n readonly phase: PausePhaseWire\n readonly runningTools: readonly string[]\n readonly rearmGoal?: boolean\n}\n\n/** The whole-app verdict. */\nexport interface GlobalStatusWire {\n readonly verdict: 'idle' | 'waiting' | 'safe'\n readonly registered: number\n readonly safe: number\n readonly parkedAt?: string\n readonly draining: readonly string[]\n readonly parking: number\n readonly stalled: readonly string[]\n readonly unresumable: readonly string[]\n readonly unreadTrees: readonly string[]\n readonly jobs: readonly string[]\n readonly blind: readonly string[]\n}\n\n/** One live status snapshot: the aggregate plus every held Session. */\nexport interface PauseSnapshotWire {\n readonly global: GlobalStatusWire\n readonly sessions: Readonly<Record<string, SessionStatusWire>>\n}\n\n/** One row in the morning decision list. */\nexport interface TriageRowWire {\n readonly sessionId: string\n readonly phase: 'draining' | 'parking' | 'flush-failed'\n readonly updatedAt: number\n readonly reading: 'read' | 'unreadable'\n readonly lastEvent?: string\n readonly turn?: number\n readonly step?: number\n readonly traces: readonly { readonly tool: string; readonly callId: string }[]\n readonly owed: boolean\n readonly wakeable: boolean\n readonly rearmGoal: boolean\n}\n\n/** One button action. */\nexport interface PauseActionRequest {\n readonly action: 'pause' | 'resume' | 'pause-all' | 'resume-all' | 'cancel-all' | 'goal'\n /** The Session the control is addressed through. */\n readonly sessionId: string\n}\n\n/** One action outcome. */\nexport interface PauseActionResultWire {\n readonly ok: boolean\n readonly message: string\n}\n\n/** One decision-list read. */\nexport interface PauseTriageRequest {\n readonly caller: string\n}\n\n/** One decision on a listed Session. */\nexport interface PauseDecisionRequest {\n readonly caller: string\n readonly target: string\n readonly decision: 'continue' | 'note' | 'review' | 'leave' | 'goal'\n}\n\n/** Host callbacks the Remote service delegates to. */\n/**\n * Refuse a Remote call made by a principal other than the owner. In-process\n * callers (tests, the Host itself) have no invocation and are trusted.\n * @param ctx - the service Context.\n */\nfunction ownerOnly(ctx: Context): void {\n const invocation = (ctx as Context & { readonly invocation?: RemoteInvocation }).invocation\n if (invocation === undefined) return\n if (invocation.peer.principal?.kind !== 'owner') {\n throw new RemoteError('pause/owner-only', 'pause controls are available only to the owner', {})\n }\n}\n\n/**\n * Record one state-changing Remote action in the host log. The principal is\n * the owning user for owner calls and trusted in-process invocations; no wire\n * field can forge it.\n * @param ctx - the service Context.\n * @param action - the action pressed.\n * @param sessionId - the addressed Session.\n */\nfunction logAction(ctx: Context, action: string, sessionId: string): void {\n const invocation = (ctx as Context & { readonly invocation?: RemoteInvocation }).invocation\n const principal = principalOf(invocation)\n ctx.logger.info(`pause: ${action} \"${sessionId}\"`, {\n action,\n sessionId,\n principal: principal?.kind ?? 'owner',\n })\n}\n\nexport interface PauseRemoteApi {\n snapshot(signal?: AbortSignal): Promise<PauseSnapshotWire>\n subscribe(listener: () => void): () => void\n act(request: PauseActionRequest, signal?: AbortSignal): Promise<PauseActionResultWire>\n triage(request: PauseTriageRequest, signal?: AbortSignal): Promise<readonly TriageRowWire[]>\n decide(request: PauseDecisionRequest, signal?: AbortSignal): Promise<PauseActionResultWire>\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n pauseSession: PauseRemote\n }\n}\n\n/** Host service exposed to the browser as the `pauseSession` Remote namespace. */\nexport class PauseRemote extends TypertRemoteService {\n /** One snapshot per host version, shared by every connected observer. */\n private snapshotCache: { readonly version: number; readonly value: Promise<PauseSnapshotWire> } | undefined\n /** Bumped by every host notification; each observer compares it to its own cursor. */\n private version = 0\n /** Observers waiting for the next version. */\n private readonly waiters = new Set<() => void>()\n private readonly unsubscribe: () => void\n\n /**\n * @param ctx - host context owning the service.\n * @param api - the pause engine behind the wire methods.\n */\n constructor(ctx: Context, private readonly api: PauseRemoteApi) {\n super(ctx, 'pauseSession', { namespace: 'pauseSession' })\n this.unsubscribe = this.api.subscribe(() => {\n this.version += 1\n this.snapshotCache = undefined\n for (const wake of [...this.waiters]) wake()\n this.waiters.clear()\n })\n ctx.effect(() => this.unsubscribe, 'pause: remote updates')\n }\n\n /**\n * One exact snapshot, used by tests and by a reconnect that cannot wait for\n * the first stream item.\n * @param signal - carrier cancellation.\n * @returns the current aggregate and session states.\n */\n @Remote('snapshot')\n async snapshot(signal?: AbortSignal): Promise<PauseSnapshotWire> {\n ownerOnly(this.ctx)\n return await this.api.snapshot(signal)\n }\n\n /**\n * The snapshot for one host version, computed once even when several clients\n * are watching. A new notification discards the cached promise.\n * @param version - the host version this snapshot answers.\n * @param signal - stream cancellation.\n * @returns the shared snapshot promise.\n */\n private snapshotFor(version: number, signal?: AbortSignal): Promise<PauseSnapshotWire> {\n if (this.snapshotCache?.version === version) return this.snapshotCache.value\n const value = this.api.snapshot(signal)\n this.snapshotCache = { version, value }\n return value\n }\n\n /**\n * Live status: the opening snapshot, then one new snapshot whenever the host\n * reports a change.\n * @param signal - stream cancellation.\n * @returns the snapshot stream.\n */\n @Remote({ mode: 'stream' })\n async *observe(signal?: AbortSignal): AsyncIterable<PauseSnapshotWire> {\n ownerOnly(this.ctx)\n let seen = -1\n for (;;) {\n signal?.throwIfAborted()\n if (this.version !== seen) {\n seen = this.version\n yield await this.snapshotFor(seen, signal)\n continue\n }\n await new Promise<void>((resolve, reject) => {\n const finish = (): void => {\n signal?.removeEventListener('abort', onAbort)\n this.waiters.delete(finish)\n resolve()\n }\n const onAbort = (): void => {\n this.waiters.delete(finish)\n signal?.removeEventListener('abort', onAbort)\n reject(signal?.reason)\n }\n this.waiters.add(finish)\n signal?.addEventListener('abort', onAbort, { once: true })\n // A notification may have landed between the version check and the\n // waiter assignment. It still owes this loop one read.\n if (this.version !== seen) finish()\n })\n }\n }\n\n /**\n * Pause, resume, press every tree, resume every clean park, or flip one\n * Session's goal re-arm.\n * @param request - the button action.\n * @param signal - carrier cancellation.\n * @returns the user-facing outcome.\n */\n @Remote('act')\n async act(request: PauseActionRequest, signal?: AbortSignal): Promise<PauseActionResultWire> {\n ownerOnly(this.ctx)\n logAction(this.ctx, request.action, request.sessionId)\n return await this.api.act(request, signal)\n }\n\n /**\n * Read the morning decision list.\n * @param request - the calling Session whose list is read.\n * @param signal - read cancellation.\n * @returns the listed rows.\n */\n @Remote('triage')\n async triage(request: PauseTriageRequest, signal?: AbortSignal): Promise<readonly TriageRowWire[]> {\n ownerOnly(this.ctx)\n return await this.api.triage(request, signal)\n }\n\n /**\n * Take one decision on one listed Session.\n * @param request - the caller, target, and decision.\n * @param signal - load cancellation.\n * @returns the user-facing outcome.\n */\n @Remote('decide')\n async decide(request: PauseDecisionRequest, signal?: AbortSignal): Promise<PauseActionResultWire> {\n ownerOnly(this.ctx)\n logAction(this.ctx, request.decision, request.target)\n return await this.api.decide(request, signal)\n }\n}\n","/**\n * Wire adapter between the pause engine and the generated Remote namespace.\n *\n * The engine speaks in branded Session ids and user-facing answer copy; the Remote\n * contract is plain JSON. Mapping lives here so neither side depends on the\n * other's types.\n * @module dsh-pause-session/remote-adapter\n */\n\nimport { SessionId } from '@deepseek-ai/dsh-session'\nimport type { SessionControl, PressTreeOutcome } from './control.js'\nimport type {\n GlobalStatusWire, PauseActionRequest, PauseActionResultWire, PauseDecisionRequest,\n PauseRemoteApi, PauseSnapshotWire, PauseTriageRequest, SessionStatusWire, TriageRowWire,\n} from './remote.js'\nimport type { GlobalStatus, ParkStatus, TriageAction, TriageRow } from './shared.js'\n\n/** Map the engine's aggregate status to the wire shape. */\nfunction globalOf(status: GlobalStatus): GlobalStatusWire {\n return {\n verdict: status.verdict,\n registered: status.registered,\n safe: status.safe,\n ...status.parkedAt === undefined ? {} : { parkedAt: String(status.parkedAt) },\n draining: status.draining.map(String),\n parking: status.parking,\n stalled: status.stalled.map(String),\n unresumable: status.unresumable.map(String),\n unreadTrees: status.unreadTrees.map(String),\n jobs: status.jobs.map(String),\n blind: [...status.blind],\n }\n}\n\n/** Map one engine status to the wire shape. */\nfunction sessionOf(status: ParkStatus): SessionStatusWire {\n return {\n phase: status.phase,\n runningTools: [...status.runningTools],\n ...status.rearmGoal === undefined ? {} : { rearmGoal: status.rearmGoal },\n }\n}\n\n/** Map one engine triage row to the wire shape. */\nfunction triageRowOf(row: TriageRow): TriageRowWire {\n return {\n sessionId: row.sessionId,\n phase: row.phase,\n updatedAt: row.updatedAt,\n reading: row.reading,\n ...row.lastEvent === undefined ? {} : { lastEvent: row.lastEvent },\n ...row.turn === undefined ? {} : { turn: row.turn },\n ...row.step === undefined ? {} : { step: row.step },\n traces: row.traces.map(trace => ({\n tool: trace.tool,\n callId: trace.callId,\n })),\n owed: row.owed,\n wakeable: row.wakeable,\n rearmGoal: row.rearmGoal,\n }\n}\n\n/** Map the engine's snapshot to the wire shape. */\nexport function snapshotOf(snapshot: { global: GlobalStatus; sessions: Readonly<Record<string, ParkStatus>> }): PauseSnapshotWire {\n return {\n global: globalOf(snapshot.global),\n sessions: Object.fromEntries(Object.entries(snapshot.sessions).map(([id, status]) => [id, sessionOf(status)])),\n }\n}\n\n/** Convert one engine answer to the wire outcome. */\nfunction outcomeOf(answer: { kind: 'success' | 'error'; text?: string }): PauseActionResultWire {\n return { ok: answer.kind === 'success', message: answer.text ?? (answer.kind === 'success' ? 'Done.' : 'Pause action failed.') }\n}\n\n/** Convert a press outcome to the wire outcome. */\nfunction pressOutcomeOf(outcome: PressTreeOutcome): PauseActionResultWire {\n const names = (ids: readonly SessionId[]): string => ids.map(id => `\"${String(id)}\"`).join(', ')\n if (outcome.unrecorded.length > 0) {\n return { ok: false, message: `pause could not record ${names(outcome.unrecorded)}.` }\n }\n if (!outcome.rootRegistered) return { ok: false, message: 'pause did not register this session.' }\n const caveats: string[] = []\n if (outcome.unresumable.length > 0) {\n caveats.push(`these subagents cannot be resumed: ${names(outcome.unresumable)}.`)\n }\n if (outcome.unreadBranches.length > 0) {\n caveats.push(`these subagent branches could not be read: ${names(outcome.unreadBranches)}.`)\n }\n if (outcome.unread && outcome.unreadBranches.length === 0) {\n caveats.push('The subagent tree could not be fully read.')\n }\n return {\n ok: true,\n message: `Paused ${String(outcome.registered)} session${outcome.registered === 1 ? '' : 's'}.`\n + (caveats.length === 0 ? '' : ` ${caveats.join(' ')}`),\n }\n}\n\n/**\n * Build the Remote callbacks over the mounted pause control.\n * @param control - the engine's host control surface.\n * @returns the wire API.\n */\nexport function remoteApiOf(control: SessionControl): PauseRemoteApi {\n return {\n snapshot: async signal => snapshotOf(await control.readSnapshot(signal)),\n subscribe: listener => control.subscribe(listener),\n act: async (request: PauseActionRequest, signal) => {\n const sessionId = SessionId(request.sessionId)\n switch (request.action) {\n case 'pause':\n return pressOutcomeOf(await control.pressTree(sessionId, signal))\n case 'resume':\n return outcomeOf((await control.releaseTree(sessionId, signal)).answer)\n case 'pause-all':\n return outcomeOf(await control.pauseAll(signal))\n case 'resume-all':\n return outcomeOf(await control.resumeAll(signal))\n case 'cancel-all':\n return outcomeOf(await control.cancelAll(signal))\n case 'goal':\n return outcomeOf(await control.toggleGoal(sessionId, signal))\n }\n },\n triage: async (request: PauseTriageRequest, signal) => {\n const list = await control.readTriage(SessionId(request.caller), signal)\n return list.rows.map(triageRowOf)\n },\n decide: async (request: PauseDecisionRequest, signal) => {\n const action = request.decision as TriageAction\n return outcomeOf(await control.decideTriage(\n SessionId(request.caller),\n SessionId(request.target),\n action,\n signal,\n ))\n },\n }\n}\n","/**\n * The durable park registry: which sessions are paused, and how cleanly each\n * one stopped.\n *\n * The registry lives in the profile's storage — `ctx.storageDomain`, the\n * schema-validated domain data form over whichever backend the composition\n * routes it to — and never in the session log: a pause is not part of a\n * session's history, and the list has to be readable by a process that has not\n * opened any session yet. The domain's own backend route is configuration, so\n * this module names no path and performs no IO of its own.\n *\n * The domain keeps the DEFAULT whole-unit layout on purpose. The per-record\n * layout reads a malformed or stale record document as an absent record, which\n * here would silently drop one paused session; a whole-unit file turns any\n * corruption into a failed `open`, and a failed open fails the plugin's mount\n * loudly instead of resuming everything.\n * @module dsh-pause-session/registry\n */\n\nimport { z } from 'zod'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { SessionId } from '@deepseek-ai/dsh-session'\nimport { defineDomain, domainTable, DomainError } from '@deepseek-ai/dsh-storage-domain'\nimport type { Domain } from '@deepseek-ai/dsh-storage-domain'\nimport { PARK_PHASES } from './shared.js'\nimport type { ParkPhase } from './shared.js'\n\n/** Domain and backend-unit name; one document per profile under the storage root. */\nexport const PARK_REGISTRY_DOMAIN = 'pause_registry'\n\n/** One session's stored park outcome. */\nexport const parkRecord = z.object({\n /** How far that session got before its process ended; only `safe` is clean. */\n phase: z.enum(PARK_PHASES),\n /** When this record was last written — a phase change, or a goal opt-out flip. */\n updatedAt: z.number().int().nonnegative(),\n /**\n * Whether resuming this session may re-arm its goal. Re-arming is opt-OUT\n * because a re-armed goal continues on its own and spends the owner's money;\n * a record written before this field existed reads as `true`, which is the\n * product default (a goal session comes back running its rounds).\n *\n * Every phase write of one park episode folds through the stored record, so\n * the opt-out a user sets while the session is still draining survives the\n * `parking` and `safe` writes that follow it.\n */\n rearmGoal: z.boolean().default(true),\n})\n\n/** One session's stored park outcome. */\nexport type ParkRecord = z.infer<typeof parkRecord>\n\n/**\n * The registry declaration. `version: 1` and no `compatibleVersions`: the\n * domain layer rejects a stored unit stamped with any other version rather than\n * migrating it, so an unrecognized registry fails loud at open. Validation is\n * the rejecting default for the same reason — a record this build cannot read\n * must stop the mount, not disappear from the list.\n */\nexport const parkRegistryDomain = defineDomain({\n name: PARK_REGISTRY_DOMAIN,\n version: 1,\n tables: { sessions: domainTable<SessionId, ParkRecord>(parkRecord) },\n})\n\n/** One session the registry remembers. */\nexport interface ParkedSession {\n readonly sessionId: SessionId\n readonly phase: ParkPhase\n /** When the phase was written. */\n readonly updatedAt: number\n /** Whether resuming this session may re-arm its goal; see {@link parkRecord}. */\n readonly rearmGoal: boolean\n}\n\n/** The durable pause list, opened over the profile's storage. */\nexport interface ParkRegistry {\n /**\n * @returns every remembered session, in insertion order.\n */\n list(): readonly ParkedSession[]\n /**\n * The same list as {@link list}, as a lazily kept read-only view.\n *\n * The whole-app aggregate and the decision list read this once per status\n * read, and a status read can happen on every native-stream notification; the\n * view is rebuilt only when the stored entries actually changed, so a read\n * that changes nothing does not build a second copy of the list to look at.\n * A caller must not mutate it.\n */\n readonly parked: ReadonlyMap<SessionId, ParkedSession>\n /**\n * @param sessionId - session to look up.\n * @returns the remembered session, or `undefined` when nothing is parked under that id.\n */\n get(sessionId: SessionId): ParkedSession | undefined\n /**\n * Write one session's outcome; resolves once it is durable. The goal opt-out\n * already recorded for that session survives a phase change — the phases of\n * one park episode all describe the same parked session.\n * @param sessionId - the session being parked.\n * @param phase - how far the park has got.\n */\n record(sessionId: SessionId, phase: ParkPhase): Promise<void>\n /**\n * Set one session's goal opt-out; resolves once it is durable.\n * @param sessionId - the parked session to change.\n * @param rearmGoal - whether the clean resume path may re-arm its goal.\n * @throws when no record exists for that session — the flag describes a\n * parked session, and inventing a record would invent a park.\n */\n setRearmGoal(sessionId: SessionId, rearmGoal: boolean): Promise<void>\n /**\n * Drop one session from the list, so a later process does not re-hold it.\n * @param sessionId - the session being released.\n */\n forget(sessionId: SessionId): Promise<void>\n}\n\n/**\n * Open the registry over the mounted storage. The caller owns the handle's\n * lifetime; this registers the close as an effect on `ctx`, so the unit is\n * released with the plugin.\n * @param ctx - plugin context carrying `storageDomain`.\n * @returns the open registry.\n * @throws whatever `open` rejects with — a `StorageError` for an unreadable\n * unit, a `DomainError('invalid-record')` for a record this build cannot read.\n */\nexport async function openParkRegistry(ctx: Context): Promise<ParkRegistry> {\n const domain: Domain<typeof parkRegistryDomain> = await ctx.storageDomain.open(parkRegistryDomain)\n ctx.effect(() => () => domain.close(), 'pause: registry')\n const sessions = domain.table('sessions')\n /** The entries the cached view was built from; replaced whenever they differ. */\n let cachedFrom: readonly ParkedSession[] = []\n let cached: ReadonlyMap<SessionId, ParkedSession> | undefined\n const entryOf = (sessionId: SessionId): ParkedSession | undefined => {\n const record = sessions.get(sessionId)\n return record === undefined\n ? undefined\n : { sessionId, phase: record.phase, updatedAt: record.updatedAt, rearmGoal: record.rearmGoal }\n }\n const entriesNow = (): readonly ParkedSession[] => [...sessions.entries()].map(([sessionId, record]) => ({\n sessionId,\n phase: record.phase,\n updatedAt: record.updatedAt,\n rearmGoal: record.rearmGoal,\n }))\n return {\n list: entriesNow,\n get parked() {\n const entries = entriesNow()\n // Same length and same records: the view the last read built still\n // describes this list, so hand that one back rather than another copy.\n if (cached !== undefined && entries.length === cachedFrom.length\n && entries.every((entry, at) => {\n const previous = cachedFrom[at]\n return previous !== undefined\n && entry.sessionId === previous.sessionId\n && entry.phase === previous.phase\n && entry.updatedAt === previous.updatedAt\n && entry.rearmGoal === previous.rearmGoal\n })) return cached\n cachedFrom = entries\n cached = new Map(entries.map(entry => [entry.sessionId, entry]))\n return cached\n },\n get: entryOf,\n record: async (sessionId, phase) => {\n const updatedAt = Date.now()\n // A record already there is folded through `update`, so the phase write\n // and the record's own goal opt-out are decided in the backend's job\n // order rather than from a read taken before this write was queued.\n if (sessions.get(sessionId) !== undefined) {\n try {\n await sessions.update(sessionId, current => ({ ...current, phase, updatedAt }))\n return\n } catch (error: unknown) {\n // Released between that read and this job: fall through and start the\n // record again, which is what the park phase is asking for.\n if (!(error instanceof DomainError) || error.code !== 'missing-key') throw error\n }\n }\n // A fresh object per write: domain records are the stored objects and are\n // never mutated in place.\n await sessions.put(sessionId, { phase, updatedAt, rearmGoal: true })\n },\n setRearmGoal: async (sessionId, rearmGoal) => {\n // `update` decides existence and reads the current record inside the\n // backend's own job order, so a phase write queued beside it cannot be\n // clobbered by a stale read.\n await sessions.update(sessionId, current => ({ ...current, rearmGoal, updatedAt: Date.now() }))\n },\n forget: async (sessionId) => {\n await sessions.delete(sessionId)\n },\n }\n}\n","/**\n * Host half of dsh-pause-session.\n *\n * Mounts the pause gate — the per-session hold at the model-request waterfall —\n * the activity fold the park predicate reads, the durable park registry, the\n * session control the browser half drives, and the clean resume path that\n * answers the input a parked session left pending.\n *\n * The registry is read before anything is gated, so a session a previous\n * process left parked is held again by this one without any user action; a\n * registry this build cannot read fails the mount loudly rather than silently\n * resuming every session it named.\n * @module dsh-pause-session\n */\n\nimport { z } from 'zod'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { TRIAGE_LOG_MAX_EVENTS, TRIAGE_WINDOW_EVENTS } from './coldlog.js'\nimport { installSessionControl } from './control.js'\nimport { PauseGate } from './gate.js'\nimport { mountParkWatch } from './park.js'\nimport { PauseRemote } from './remote.js'\nimport { remoteApiOf } from './remote-adapter.js'\nimport { openParkRegistry } from './registry.js'\nexport { PauseRemote } from './remote.js'\n\n/** Plugin name; the Loader row and the bundle patch both use it. */\nexport const name = 'dsh-pause-session'\n\n/**\n * Required host services: the model runtime to gate, the stores the control\n * needs, the projection registry the park predicate reads turn state from, and\n * the profile's storage, which holds the pause registry.\n */\nexport const inject = ['llm', 'agents', 'sessions', 'sessionProjections', 'storageDomain']\n\n/**\n * Provider routes whose request conversion drops an empty user message, so the\n * clean resume path's wake input never reaches the model on them.\n *\n * The list is deployment knowledge, not a constant this plugin may assume:\n * whether an empty user message survives is decided by the adapter (and, for\n * the pi-ai family, by the provider API that adapter is configured for), and a\n * plugin cannot introspect an adapter. The default names the routes the shipped\n * DeepSeek adapter registers; a profile that routes a session elsewhere gets\n * the visible marker instead of a silent empty turn until it declares that\n * route here.\n */\nexport const DEFAULT_SILENT_WAKE_ROUTES = ['deepseek-official', 'deepseek-account'] as const\n\n/** Validated plugin configuration. */\nconst ConfigShape = z.object({\n /** Provider routes known to drop an empty user message; see {@link DEFAULT_SILENT_WAKE_ROUTES}. */\n silentWakeRoutes: z.array(z.string()).default([...DEFAULT_SILENT_WAKE_ROUTES]),\n /**\n * How many events of a parked session's stored log the morning list folds per\n * row, counted back from the end. The list reads a session's stop position\n * and its unknown tool outcomes, both facts about the log's end, so this\n * bounds what a panel left open costs without changing what a row says about\n * a log that fits inside it. A row whose log needs more than this is reported\n * unreadable rather than guessed at; see `./coldlog.ts`.\n */\n maxListedLogEvents: z.number().int().min(1).max(TRIAGE_LOG_MAX_EVENTS).default(TRIAGE_WINDOW_EVENTS),\n})\n\n/** Validated plugin configuration. */\nexport type Config = z.infer<typeof ConfigShape>\n\n/**\n * The Loader hands a row's config through this schema, and an empty row config\n * arrives as `undefined`; folding it to `{}` first is what lets the field\n * defaults above apply instead of failing the mount.\n */\nexport const Config = z.preprocess(value => value ?? {}, ConfigShape)\n\n/**\n * Mount the durable pause registry, the pause gate, the activity fold, their\n * session control, and the clean resume path.\n *\n * The registry opens first on purpose: if it cannot be read, nothing else is\n * registered, so a failed mount leaves no half-installed control behind.\n * @param ctx - the plugin's host context.\n * @param config - validated route knowledge for the resume path's wake content.\n */\nexport async function apply(ctx: Context, config: Config = Config.parse({})): Promise<void> {\n const registry = await openParkRegistry(ctx)\n const lifetime = new AbortController()\n // The gate races this signal, so unloading releases every held request\n // instead of stranding a generator no one can reach again.\n ctx.effect(() => () => { lifetime.abort(new Error('pause: plugin unloaded')) }, 'pause: lifetime')\n const gate = new PauseGate({ lifetime: lifetime.signal })\n ctx.effect(\n () => ctx.on('llm/stream', (request, next) => gate.stream(request, next)),\n 'pause: pause gate',\n )\n const watch = await mountParkWatch(ctx)\n const control = installSessionControl(ctx, gate, watch, registry, {\n silentWakeRoutes: config.silentWakeRoutes,\n maxListedLogEvents: config.maxListedLogEvents,\n })\n new PauseRemote(ctx, remoteApiOf(control))\n await control.adopt()\n}\n"],"mappings":"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"}