negotium 0.1.49 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-helpers.js +58 -74
  3. package/dist/agent-helpers.js.map +15 -15
  4. package/dist/background-bash.js +22 -10
  5. package/dist/background-bash.js.map +3 -3
  6. package/dist/browser-runtime.js +35 -68
  7. package/dist/browser-runtime.js.map +7 -7
  8. package/dist/{chunk-5rpw9d1f.js → chunk-kc5kmr95.js} +28 -11
  9. package/dist/{chunk-5rpw9d1f.js.map → chunk-kc5kmr95.js.map} +4 -4
  10. package/dist/hosted-agent.js +36 -18
  11. package/dist/hosted-agent.js.map +7 -7
  12. package/dist/main.js +562 -98
  13. package/dist/main.js.map +21 -19
  14. package/dist/mcp-factories.js +62 -78
  15. package/dist/mcp-factories.js.map +16 -16
  16. package/dist/prompts.js +22 -10
  17. package/dist/prompts.js.map +3 -3
  18. package/dist/query-runtime.js +22 -10
  19. package/dist/query-runtime.js.map +4 -4
  20. package/dist/registry.js +5 -12
  21. package/dist/registry.js.map +5 -5
  22. package/dist/rollout.js +1 -1
  23. package/dist/runtime/src/agents/maestro-registry.ts +1 -1
  24. package/dist/runtime/src/agents/rollout/shared.ts +12 -4
  25. package/dist/runtime/src/mcp/mcp-helpers.ts +1 -1
  26. package/dist/runtime/src/mcp/wiki-server.ts +1 -1
  27. package/dist/runtime/src/migration/single-user.ts +621 -0
  28. package/dist/runtime/src/platform/config.ts +24 -11
  29. package/dist/runtime/src/platform/mcp-config.ts +4 -4
  30. package/dist/runtime/src/platform/playwright/manager.ts +10 -28
  31. package/dist/runtime/src/runtime/attachments.ts +3 -3
  32. package/dist/runtime/src/storage/browser-profiles.ts +11 -9
  33. package/dist/runtime/src/storage/conversations.ts +5 -18
  34. package/dist/runtime/src/storage/storage-host.ts +1 -1
  35. package/dist/runtime/src/storage/tasks.ts +2 -14
  36. package/dist/runtime/src/storage/vault.ts +3 -2
  37. package/dist/runtime/src/version.ts +1 -1
  38. package/dist/runtime-helpers.js +22 -10
  39. package/dist/runtime-helpers.js.map +5 -5
  40. package/dist/storage.js +5 -19
  41. package/dist/storage.js.map +5 -5
  42. package/dist/types/packages/core/src/mcp/mcp-helpers.d.ts +1 -1
  43. package/dist/types/packages/core/src/platform/config.d.ts +9 -1
  44. package/dist/types/packages/core/src/runtime/attachments.d.ts +1 -1
  45. package/dist/types/packages/core/src/storage/conversations.d.ts +2 -2
  46. package/dist/types/packages/core/src/version.d.ts +1 -1
  47. package/dist/vault.js +25 -12
  48. package/dist/vault.js.map +4 -4
  49. package/install-browser-rs.mjs +1 -1
  50. package/package.json +2 -2
@@ -4,15 +4,15 @@
4
4
  "sourcesContent": [
5
5
  "import { existsSync, mkdirSync, readdirSync, renameSync, unlinkSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentRegistry, AgentRegistryOperations } from \"#agents/contracts\";\nimport { encodeClaudeCwd, writeClaudeRollout } from \"#agents/rollout/claude\";\nimport { MODEL_FABLE, MODEL_OPUS, MODEL_SONNET } from \"#platform/config\";\nimport { logger } from \"#platform/logger\";\nimport { CLAUDE_EFFORT_VALUES, type EffortLevel } from \"#types\";\n\nconst ALIAS_MAP: Record<string, string> = {\n sonnet: MODEL_SONNET,\n opus: MODEL_OPUS,\n fable: MODEL_FABLE,\n};\n\nconst VALID_ALIASES = new Set(Object.keys(ALIAS_MAP));\nconst VALID_EFFORTS = new Set<EffortLevel>(CLAUDE_EFFORT_VALUES);\n\nexport const claudeRegistry: AgentRegistry = {\n kind: \"claude\",\n defaultModel: \"sonnet\",\n defaultEffort: \"high\",\n\n expandModelAlias(s) {\n return ALIAS_MAP[s] ?? s;\n },\n\n validateModel(s) {\n return VALID_ALIASES.has(s);\n },\n\n validEfforts: CLAUDE_EFFORT_VALUES,\n validateEffort(s) {\n return VALID_EFFORTS.has(s);\n },\n\n footerLabel(model, effort) {\n return effort ? `${model} · ${effort}` : model;\n },\n};\n\nexport const claudeRegistryOperations: AgentRegistryOperations = {\n writeRollout(opts) {\n // `reuseSessionId` (if any) becomes the SDK resume key + path component,\n // so claude→codex→claude lands at the original `~/.claude/projects/<dir>/<id>.jsonl`\n // instead of orphaning a fresh UUID every switch.\n const { sessionId, rolloutPath } = writeClaudeRollout({\n cwd: opts.cwd,\n entries: opts.entries,\n model: claudeRegistry.expandModelAlias(opts.model ?? claudeRegistry.defaultModel),\n ...(opts.reuseSessionId ? { sessionId: opts.reuseSessionId } : {}),\n });\n return { sessionId, rolloutPath };\n },\n\n // Claude has a native fork API in @anthropic-ai/claude-agent-sdk that produces\n // a byte-equivalent copy of the parent rollout. Preserves tool history\n // structurally, so the fork is indistinguishable from the parent on resume.\n //\n // SDK ≥0.3.19x semantics: `dir` is where to FIND the parent session (lookup\n // scope), NOT the destination — passing the derived topic's cwd made lookup\n // fail (\"Session … not found in project directory\"). The fork file is always\n // written NEXT TO the parent rollout, so after forking we move it into the\n // derived cwd's project dir where a resume from that cwd expects it.\n async forkSession({ parentSessionId, cwd, title }) {\n const { forkSession } = await import(\"@anthropic-ai/claude-agent-sdk\");\n // Omit `dir`: the parent session lives under the SOURCE topic's project\n // dir, which this registry never receives — global search finds it.\n const result = await forkSession(parentSessionId, {\n ...(title ? { title } : {}),\n });\n const projectsRoot = join(homedir(), \".claude\", \"projects\");\n const destDir = join(projectsRoot, encodeClaudeCwd(cwd));\n const destPath = join(destDir, `${result.sessionId}.jsonl`);\n if (!existsSync(destPath)) {\n const sourcePath = readdirSync(projectsRoot)\n .map((d) => join(projectsRoot, d, `${result.sessionId}.jsonl`))\n .find((p) => existsSync(p));\n if (!sourcePath) {\n throw new Error(\n `claude forkSession: fork rollout ${result.sessionId}.jsonl not found under ${projectsRoot}`,\n );\n }\n mkdirSync(destDir, { recursive: true });\n renameSync(sourcePath, destPath);\n }\n return {\n forkId: result.sessionId,\n // Same project-dir layout as cleanupRollouts below — NOT under\n // `cwd/.claude/sessions/`. A wrong path here makes cleanupAgentFork's\n // existsSync always false, so silent-fork rollouts accumulate forever.\n rolloutPath: destPath,\n };\n },\n\n // Each Claude rollout lives at `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`.\n // The encoded-cwd is deterministic so we can compute the path directly\n // without globbing — fast and robust against the SDK changing date layout.\n async cleanupRollouts({ cwd, sessionIds }) {\n const projectsDir = join(homedir(), \".claude\", \"projects\", encodeClaudeCwd(cwd));\n const failures: unknown[] = [];\n for (const sid of sessionIds) {\n const path = join(projectsDir, `${sid}.jsonl`);\n try {\n unlinkSync(path);\n } catch (e) {\n if ((e as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n logger.warn({ err: e, path }, \"claude cleanupRollouts: unlink failed\");\n failures.push(e);\n }\n }\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, \"claude cleanupRollouts failed\");\n }\n },\n};\n",
6
6
  "import { existsSync, unlinkSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentRegistry, AgentRegistryOperations } from \"#agents/contracts\";\nimport { writeCodexRollout } from \"#agents/rollout/codex\";\nimport { logger } from \"#platform/logger\";\nimport { readConversation } from \"#storage/conversations\";\nimport { CODEX_EFFORT_VALUES, type EffortLevel } from \"#types\";\n\nconst VALID_EFFORTS = new Set<EffortLevel>(CODEX_EFFORT_VALUES);\n\n// Codex CLI's own empirical default is gpt-5.6-sol (`codex exec` 2026-07-10 →\n// \"model: gpt-5.6-sol\"), but we deliberately default to gpt-5.6-luna — the\n// cheapest/fastest GPT-5.6 tier — for a general always-on assistant where most\n// queries are light. Heavier work escalates to terra/sol via set_model. This\n// value is passed explicitly to the SDK (see event-processor resolveDefaultModel),\n// so the footer and the actual model stay in sync.\nexport const codexRegistry: AgentRegistry = {\n kind: \"codex\",\n defaultModel: \"gpt-5.6-luna\",\n // defaultEffort intentionally omitted — Codex SDK treats absence as\n // \"reasoning off\". Setting \"high\"/etc. would silently flip on reasoning.\n\n expandModelAlias(s) {\n return s;\n },\n\n validateModel(s) {\n // Codex doesn't publish a closed model list and OpenAI ships new IDs\n // (gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, o3, ...) frequently. Best-effort: accept any\n // non-empty string. Bad IDs surface at SDK call time with a clear error.\n return typeof s === \"string\" && s.length > 0;\n },\n\n validEfforts: CODEX_EFFORT_VALUES,\n validateEffort(s) {\n // GPT-5.6 accepts low/medium/high/xhigh/max; 'minimal'\n // was removed because the Codex API rejects it when default tools\n // (image_gen, web_search) are active.\n return VALID_EFFORTS.has(s);\n },\n\n footerLabel(model, effort) {\n // Codex omits effort to mean \"reasoning off\". Show `(off)` explicitly so\n // the user can distinguish from claude (which always has a default).\n return `${model} · ${effort ?? \"(off)\"}`;\n },\n};\n\nexport const codexRegistryOperations: AgentRegistryOperations = {\n writeRollout(opts) {\n // Codex SDK exposes the resume key as `threadId`; AgentRegistry unifies\n // the name to `sessionId` so callers don't branch on agent.\n // `reuseSessionId` (if any) is forwarded as `threadId` so claude→codex→claude\n // round-trips also keep one continuous codex thread instead of orphaning a\n // fresh uuidv7 on every switch.\n const { threadId, rolloutPath } = writeCodexRollout({\n cwd: opts.cwd,\n entries: opts.entries,\n model: opts.model ?? codexRegistry.defaultModel,\n ...(opts.effort ? { effort: opts.effort } : {}),\n ...(opts.reuseSessionId ? { threadId: opts.reuseSessionId } : {}),\n });\n return { sessionId: threadId, rolloutPath };\n },\n\n // Codex SDK has no fork API. We synthesize a rollout from the\n // provider-agnostic conversation log (same path as `set_agent` cross-agent\n // bridging). Caveats: extractChatPairs folds tool_use/tool_result into\n // assistant text as `[Tool: ...]` annotations, so structural tool history\n // is lost.\n async forkSession({ cwd, userId, topicName, model, effort }) {\n const entries = readConversation(userId, topicName);\n const { threadId, rolloutPath } = writeCodexRollout({\n cwd,\n entries,\n model: model ?? codexRegistry.defaultModel,\n ...(effort ? { effort } : {}),\n });\n return { forkId: threadId, rolloutPath };\n },\n\n // Codex stores rollouts at `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<threadId>.jsonl`.\n // The date prefix is unknown at cleanup time (it's the *original* write\n // timestamp, not \"now\"), so we glob across the whole sessions tree by\n // threadId suffix. With at most a few thousand files in active use this\n // is well under a millisecond on a warm filesystem.\n async cleanupRollouts({ sessionIds }) {\n if (sessionIds.length === 0) return;\n const sessionsDir = join(process.env.CODEX_HOME || join(homedir(), \".codex\"), \"sessions\");\n // No sessions directory means there are no provider rollouts left to remove.\n if (!existsSync(sessionsDir)) return;\n const failures: unknown[] = [];\n // One Glob per threadId so a single corrupt entry can't poison the rest.\n // Bun.Glob's `scan` yields paths relative to its base dir.\n for (const tid of sessionIds) {\n try {\n const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);\n for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {\n const path = join(sessionsDir, rel);\n try {\n unlinkSync(path);\n } catch (e) {\n if ((e as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n logger.warn({ err: e, path }, \"codex cleanupRollouts: unlink failed\");\n failures.push(e);\n }\n }\n }\n } catch (e) {\n logger.warn({ err: e, threadId: tid }, \"codex cleanupRollouts: scan failed\");\n failures.push(e);\n }\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, \"codex cleanupRollouts failed\");\n }\n },\n};\n",
7
- "import {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { appendJsonlLine } from \"#platform/jsonl\";\nimport { logger } from \"#platform/logger\";\nimport { sanitizeTopicName } from \"#security/sanitize\";\nimport { resolveStorageDataDir } from \"#storage/storage-host\";\nimport type { AgentKind, UnifiedEvent } from \"#types\";\n\n/**\n * Per-topic conversation log (UnifiedEvent stream) used as the\n * **provider-agnostic source of truth** for cross-agent portability.\n *\n * Storage layout:\n * {DATA_DIR}/conversations/{userId}/{sanitizedTopicName}.jsonl\n * {DATA_DIR}/conversations/{userId}/{sanitizedTopicName}.active.jsonl\n *\n * The first file is append-only and retains every yielded UnifiedEvent for\n * archive and teardown. The optional active file is a replaceable provider\n * projection: compaction writes its summary there and later turns append to\n * both streams. Agent switches and rollout repair read the active projection.\n *\n * The Claude/Codex SDK rollouts (~/.claude/projects/, ~/.codex/sessions/) are\n * intentionally treated as opaque side-effects of the SDKs. The raw stream\n * remains the forensic source of truth.\n */\nexport interface ConversationEntry {\n ts: string;\n /**\n * Agent that produced this event, frozen at write time. A topic that has\n * been switched (via `set_agent`) will have a mixed-agent log: earlier\n * entries keep the agent that originally generated them. Replay code\n * (rollout-codec `extractChatPairs`) intentionally ignores this field —\n * the cross-agent rollout's whole point is to feed past dialogue, no\n * matter who produced it, into the *new* SDK as if it were native.\n */\n agent: AgentKind;\n event: UnifiedEvent;\n}\n\n/**\n * Validate that a userId stringifies to a safe path component. Rejects empties,\n * slashes, and traversal sequences before they touch the filesystem. The\n * conversation log is local-only so we keep this strict but simple if a\n * caller sends something unexpected it is their bug to fix, not ours to coerce.\n */\nfunction safeUserIdComponent(userId: number | string): string {\n const str = String(userId);\n if (!str || /[/\\\\]|\\.\\./.test(str)) {\n throw new Error(`conversations: refusing unsafe userId path component: ${str}`);\n }\n return str;\n}\n\nfunction conversationDir(userId: number | string): string {\n return join(resolveStorageDataDir(), \"conversations\", safeUserIdComponent(userId));\n}\n\nfunction topicFilename(topicName: string): string {\n const t = sanitizeTopicName(topicName, true);\n return `${t}.jsonl`;\n}\n\n/** Compute the absolute path for a given user/topic conversation log. */\nexport function getConversationPath(userId: number | string, topicName: string): string {\n return join(conversationDir(userId), topicFilename(topicName));\n}\n\n/** Replaceable provider context derived from the append-only raw conversation log. */\nexport function getActiveConversationPath(userId: number | string, topicName: string): string {\n const rawPath = getConversationPath(userId, topicName);\n return rawPath.endsWith(\".jsonl\")\n ? `${rawPath.slice(0, -\".jsonl\".length)}.active.jsonl`\n : `${rawPath}.active`;\n}\n\nexport function hasActiveConversation(userId: number | string, topicName: string): boolean {\n return existsSync(getActiveConversationPath(userId, topicName));\n}\n\n/**\n * Append a single UnifiedEvent for the given topic. Creates the parent\n * directory and the file as needed. Best-effort: I/O failures are logged but\n * never throw, since recording must not break the live stream to Telegram.\n *\n * **Concurrency note (review item M2, revised):** within the bot process the\n * single-threaded event loop already serializes writes — but this module is\n * NOT single-process: the self-config MCP server (a separate stdio process,\n * via `topic-agent-switch`) and provider bridge helpers append to the same\n * topic logs.\n * Cross-process interleaving on macOS is real for lines beyond PIPE_BUF\n * (512B), and a torn line is silently dropped by `readConversation` —\n * corrupting the canonical source for cross-agent rollout reconstruction.\n * Writes therefore go through `appendJsonlLine` (sidecar `.lock` via O_EXCL,\n * stale-lock reclaim, and a `JsonlLockTimeoutError` when the lock stays busy —\n * nothing is written unlocked, so a contended append fails instead of risking\n * an interleaved line that `readConversationPath` would later discard).\n *\n * The append stays SYNCHRONOUS on purpose: a previous attempt at a\n * Promise-chained per-topic queue made writes async (durability gap before a\n * synchronous `readConversation`) and broke `set_agent` in the bridge tests.\n */\nexport function appendConversationEvent(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): boolean {\n try {\n appendConversationEventStrict(userId, topicName, agent, event);\n return true;\n } catch (err) {\n logger.warn(\n { err, userId, topicName, eventType: event.type },\n \"appendConversationEvent: write failed\",\n );\n return false;\n }\n}\n\n/**\n * The raw manifest accepted an entry that the active projection rejected.\n *\n * Distinct from a plain write failure because the two logs are now out of sync\n * rather than merely un-updated: `raw` records the event, `active` — the log\n * replayed to the provider — does not.\n */\nexport class ConversationLogDivergedError extends Error {\n constructor(\n readonly rawPath: string,\n readonly activePath: string,\n override readonly cause: unknown,\n ) {\n super(\n `conversation logs diverged: the entry is in the raw manifest (${rawPath}) ` +\n `but the active projection (${activePath}) rejected it`,\n );\n this.name = \"ConversationLogDivergedError\";\n }\n}\n\n/**\n * Strict variant for state transitions where the conversation log is a manifest\n * rather than telemetry. Throws on I/O failure so callers can avoid committing\n * DB state that points at an unmanifested SDK session.\n */\nexport function appendConversationEventStrict(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n const path = getConversationPath(userId, topicName);\n const entry: ConversationEntry = {\n ts: new Date().toISOString(),\n agent,\n event,\n };\n const line = JSON.stringify(entry);\n mkdirSync(dirname(path), { recursive: true });\n appendJsonlLine(path, line);\n const activePath = getActiveConversationPath(userId, topicName);\n if (existsSync(activePath)) {\n try {\n appendJsonlLine(activePath, line);\n } catch (cause) {\n // The two appends are not atomic. Raw already has the entry, so failing\n // here leaves the logs permanently split: the provider replays `active`\n // and will never see this event, while the raw manifest says it happened.\n // We cannot un-append, so at least name the inconsistency instead of\n // surfacing a generic write error that hides which side is wrong.\n throw new ConversationLogDivergedError(path, activePath, cause);\n }\n }\n}\n\n/** Append lifecycle metadata to the raw manifest without changing active context. */\nexport function appendRawConversationEventStrict(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n const path = getConversationPath(userId, topicName);\n const entry: ConversationEntry = {\n ts: new Date().toISOString(),\n agent,\n event,\n };\n mkdirSync(dirname(path), { recursive: true });\n appendJsonlLine(path, JSON.stringify(entry));\n}\n\n/**\n * Read one JSONL conversation stream. Malformed lines are skipped so one\n * damaged event does not poison the remaining history.\n */\nfunction readConversationPath(path: string): ConversationEntry[] {\n const out: ConversationEntry[] = [];\n if (!existsSync(path)) return out;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (err) {\n logger.warn({ err, path }, \"readConversation: read failed\");\n return out;\n }\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n out.push(JSON.parse(line) as ConversationEntry);\n } catch (err) {\n logger.warn(\n { err, line: line.slice(0, 200) },\n \"readConversation: malformed JSONL line skipped\",\n );\n }\n }\n return out;\n}\n\n/**\n * Read the context the next provider turn should receive. Before the first\n * compaction this is the raw stream. Afterwards the replaceable active stream\n * contains the compacted summary plus every subsequently recorded event.\n *\n * NOTE(perf): reads the whole file each call. Topics in the kilobyte range\n * are fine; if a single topic ever grows into multi-megabyte territory,\n * consider a streaming reader (`readline`/`Bun.file().stream()`) and a\n * size-bounded tail.\n */\nexport function readConversation(userId: number | string, topicName: string): ConversationEntry[] {\n const activePath = getActiveConversationPath(userId, topicName);\n return readConversationPath(\n existsSync(activePath) ? activePath : getConversationPath(userId, topicName),\n );\n}\n\n/** Read the immutable full-fidelity stream used for archive and teardown. */\nexport function readRawConversation(\n userId: number | string,\n topicName: string,\n): ConversationEntry[] {\n return readConversationPath(getConversationPath(userId, topicName));\n}\n\n/** Atomically replace only the provider's active context projection. */\nexport function replaceConversationStrict(\n userId: number | string,\n topicName: string,\n entries: ConversationEntry[],\n): void {\n replaceConversationPathStrict(getActiveConversationPath(userId, topicName), entries);\n}\n\n/** Atomically seed or restore the append-only stream before it becomes live. */\nexport function replaceRawConversationStrict(\n userId: number | string,\n topicName: string,\n entries: ConversationEntry[],\n): void {\n replaceConversationPathStrict(getConversationPath(userId, topicName), entries);\n}\n\nfunction replaceConversationPathStrict(path: string, entries: ConversationEntry[]): void {\n const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n mkdirSync(dirname(path), { recursive: true });\n try {\n writeFileSync(\n tempPath,\n entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n` : \"\",\n { flag: \"wx\" },\n );\n renameSync(tempPath, path);\n } catch (error) {\n try {\n unlinkSync(tempPath);\n } catch {}\n throw error;\n }\n}\n\n/**\n * Copy the unified conversation log of `srcTopic` into the file path that\n * `dstTopic` will read from. Used by `/fork` so a forked child topic inherits\n * the parent's full cross-agent history, not just the agent's native SDK\n * rollout. `/spawn` intentionally does not call this; it starts with no\n * conversation history. Without this copy, a fork followed immediately by\n * `/agent <other>` would feed the empty child log to `switchTopicAgent` and\n * the new agent would start from zero — see the bug report from 2026-05-24.\n *\n * Semantics:\n * - Copies the parent's raw stream and its active projection when present.\n * - Writes independent files so parent and child can diverge after the fork.\n * - Refuses to overwrite a non-empty destination (returns `{copied:false}`).\n * `/fork`'s only caller runs this immediately after topic creation when\n * the dst file is guaranteed empty, so a non-empty dst means a programmer\n * error somewhere upstream — fail loud rather than silently merging.\n * - On any I/O error: throws. The caller (`createChildTopic`) already has\n * a rollback path that wraps this call.\n */\nexport function cloneConversationLog(opts: {\n userId: number | string;\n srcTopic: string;\n dstTopic: string;\n}): { copied: boolean; entries: number } {\n const { userId, srcTopic, dstTopic } = opts;\n const dstPath = getConversationPath(userId, dstTopic);\n if (existsSync(dstPath) && readFileSync(dstPath, \"utf8\").trim().length > 0) {\n logger.warn(\n { userId, srcTopic, dstTopic, dstPath },\n \"cloneConversationLog: dst already non-empty — refusing to overwrite\",\n );\n return { copied: false, entries: 0 };\n }\n const entries = readRawConversation(userId, srcTopic);\n if (entries.length === 0) {\n return { copied: false, entries: 0 };\n }\n const body = `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n mkdirSync(dirname(dstPath), { recursive: true });\n writeFileSync(dstPath, body);\n const srcActivePath = getActiveConversationPath(userId, srcTopic);\n if (existsSync(srcActivePath)) {\n const dstActivePath = getActiveConversationPath(userId, dstTopic);\n writeFileSync(dstActivePath, readFileSync(srcActivePath));\n }\n return { copied: true, entries: entries.length };\n}\n\n/**\n * Walk the unified log backwards and return the most recent SDK-emitted\n * session id for the given agent — or null if that agent has never run on\n * this topic.\n *\n * Used by `set_agent` to round-trip the SAME native rollout file across\n * agent switches: when the user does claude → codex → claude, we reuse\n * the original claude sessionId so the synthetic rollout lands at the\n * same path the SDK already manages, preserving prompt-cache continuity\n * and avoiding orphan `~/.claude/projects/<dir>/<id>.jsonl` files.\n *\n * Behavior:\n * - First-ever switch to an agent (no prior session events) → null,\n * caller falls back to a fresh randomUUID/uuidv7. Same as before.\n * - Roundtrip switch (prior session exists) → that sessionId.\n *\n * `session` events are emitted on every turn by claude-/codex-provider and\n * captured into the unified log by runAgent's append wrapper. The most\n * recent one for the target agent is by definition the current SDK-side\n * resume key for that agent.\n */\nexport function findLastSessionIdForAgent(\n entries: ConversationEntry[],\n agent: AgentKind,\n): string | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.agent !== agent) continue;\n if (entry.event.type === \"session\") {\n return entry.event.sessionId;\n }\n }\n return null;\n}\n",
7
+ "import {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { appendJsonlLine } from \"#platform/jsonl\";\nimport { logger } from \"#platform/logger\";\nimport { sanitizeTopicName } from \"#security/sanitize\";\nimport { resolveStorageDataDir } from \"#storage/storage-host\";\nimport type { AgentKind, UnifiedEvent } from \"#types\";\n\n/**\n * Per-topic conversation log (UnifiedEvent stream) used as the\n * **provider-agnostic source of truth** for cross-agent portability.\n *\n * Storage layout:\n * {DATA_DIR}/conversations/{sanitizedTopicName}.jsonl\n * {DATA_DIR}/conversations/{sanitizedTopicName}.active.jsonl\n *\n * The first file is append-only and retains every yielded UnifiedEvent for\n * archive and teardown. The optional active file is a replaceable provider\n * projection: compaction writes its summary there and later turns append to\n * both streams. Agent switches and rollout repair read the active projection.\n *\n * The Claude/Codex SDK rollouts (~/.claude/projects/, ~/.codex/sessions/) are\n * intentionally treated as opaque side-effects of the SDKs. The raw stream\n * remains the forensic source of truth.\n */\nexport interface ConversationEntry {\n ts: string;\n /**\n * Agent that produced this event, frozen at write time. A topic that has\n * been switched (via `set_agent`) will have a mixed-agent log: earlier\n * entries keep the agent that originally generated them. Replay code\n * (rollout-codec `extractChatPairs`) intentionally ignores this field —\n * the cross-agent rollout's whole point is to feed past dialogue, no\n * matter who produced it, into the *new* SDK as if it were native.\n */\n agent: AgentKind;\n event: UnifiedEvent;\n}\n\n/** Public userId remains in the API, but standalone storage has one flat local namespace. */\nfunction conversationDir(_userId: number | string): string {\n return join(resolveStorageDataDir(), \"conversations\");\n}\n\nfunction topicFilename(topicName: string): string {\n const t = sanitizeTopicName(topicName, true);\n return `${t}.jsonl`;\n}\n\n/** Compute the absolute path for a given user/topic conversation log. */\nexport function getConversationPath(userId: number | string, topicName: string): string {\n return join(conversationDir(userId), topicFilename(topicName));\n}\n\n/** Replaceable provider context derived from the append-only raw conversation log. */\nexport function getActiveConversationPath(userId: number | string, topicName: string): string {\n const rawPath = getConversationPath(userId, topicName);\n return rawPath.endsWith(\".jsonl\")\n ? `${rawPath.slice(0, -\".jsonl\".length)}.active.jsonl`\n : `${rawPath}.active`;\n}\n\nexport function hasActiveConversation(userId: number | string, topicName: string): boolean {\n return existsSync(getActiveConversationPath(userId, topicName));\n}\n\n/**\n * Append a single UnifiedEvent for the given topic. Creates the parent\n * directory and the file as needed. Best-effort: I/O failures are logged but\n * never throw, since recording must not break the live stream to Telegram.\n *\n * **Concurrency note (review item M2, revised):** within the bot process the\n * single-threaded event loop already serializes writes — but this module is\n * NOT single-process: the self-config MCP server (a separate stdio process,\n * via `topic-agent-switch`) and provider bridge helpers append to the same\n * topic logs.\n * Cross-process interleaving on macOS is real for lines beyond PIPE_BUF\n * (512B), and a torn line is silently dropped by `readConversation` —\n * corrupting the canonical source for cross-agent rollout reconstruction.\n * Writes therefore go through `appendJsonlLine` (sidecar `.lock` via O_EXCL,\n * stale-lock reclaim, and a `JsonlLockTimeoutError` when the lock stays busy —\n * nothing is written unlocked, so a contended append fails instead of risking\n * an interleaved line that `readConversationPath` would later discard).\n *\n * The append stays SYNCHRONOUS on purpose: a previous attempt at a\n * Promise-chained per-topic queue made writes async (durability gap before a\n * synchronous `readConversation`) and broke `set_agent` in the bridge tests.\n */\nexport function appendConversationEvent(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): boolean {\n try {\n appendConversationEventStrict(userId, topicName, agent, event);\n return true;\n } catch (err) {\n logger.warn(\n { err, userId, topicName, eventType: event.type },\n \"appendConversationEvent: write failed\",\n );\n return false;\n }\n}\n\n/**\n * The raw manifest accepted an entry that the active projection rejected.\n *\n * Distinct from a plain write failure because the two logs are now out of sync\n * rather than merely un-updated: `raw` records the event, `active` — the log\n * replayed to the provider — does not.\n */\nexport class ConversationLogDivergedError extends Error {\n constructor(\n readonly rawPath: string,\n readonly activePath: string,\n override readonly cause: unknown,\n ) {\n super(\n `conversation logs diverged: the entry is in the raw manifest (${rawPath}) ` +\n `but the active projection (${activePath}) rejected it`,\n );\n this.name = \"ConversationLogDivergedError\";\n }\n}\n\n/**\n * Strict variant for state transitions where the conversation log is a manifest\n * rather than telemetry. Throws on I/O failure so callers can avoid committing\n * DB state that points at an unmanifested SDK session.\n */\nexport function appendConversationEventStrict(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n const path = getConversationPath(userId, topicName);\n const entry: ConversationEntry = {\n ts: new Date().toISOString(),\n agent,\n event,\n };\n const line = JSON.stringify(entry);\n mkdirSync(dirname(path), { recursive: true });\n appendJsonlLine(path, line);\n const activePath = getActiveConversationPath(userId, topicName);\n if (existsSync(activePath)) {\n try {\n appendJsonlLine(activePath, line);\n } catch (cause) {\n // The two appends are not atomic. Raw already has the entry, so failing\n // here leaves the logs permanently split: the provider replays `active`\n // and will never see this event, while the raw manifest says it happened.\n // We cannot un-append, so at least name the inconsistency instead of\n // surfacing a generic write error that hides which side is wrong.\n throw new ConversationLogDivergedError(path, activePath, cause);\n }\n }\n}\n\n/** Append lifecycle metadata to the raw manifest without changing active context. */\nexport function appendRawConversationEventStrict(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n const path = getConversationPath(userId, topicName);\n const entry: ConversationEntry = {\n ts: new Date().toISOString(),\n agent,\n event,\n };\n mkdirSync(dirname(path), { recursive: true });\n appendJsonlLine(path, JSON.stringify(entry));\n}\n\n/**\n * Read one JSONL conversation stream. Malformed lines are skipped so one\n * damaged event does not poison the remaining history.\n */\nfunction readConversationPath(path: string): ConversationEntry[] {\n const out: ConversationEntry[] = [];\n if (!existsSync(path)) return out;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (err) {\n logger.warn({ err, path }, \"readConversation: read failed\");\n return out;\n }\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n out.push(JSON.parse(line) as ConversationEntry);\n } catch (err) {\n logger.warn(\n { err, line: line.slice(0, 200) },\n \"readConversation: malformed JSONL line skipped\",\n );\n }\n }\n return out;\n}\n\n/**\n * Read the context the next provider turn should receive. Before the first\n * compaction this is the raw stream. Afterwards the replaceable active stream\n * contains the compacted summary plus every subsequently recorded event.\n *\n * NOTE(perf): reads the whole file each call. Topics in the kilobyte range\n * are fine; if a single topic ever grows into multi-megabyte territory,\n * consider a streaming reader (`readline`/`Bun.file().stream()`) and a\n * size-bounded tail.\n */\nexport function readConversation(userId: number | string, topicName: string): ConversationEntry[] {\n const activePath = getActiveConversationPath(userId, topicName);\n return readConversationPath(\n existsSync(activePath) ? activePath : getConversationPath(userId, topicName),\n );\n}\n\n/** Read the immutable full-fidelity stream used for archive and teardown. */\nexport function readRawConversation(\n userId: number | string,\n topicName: string,\n): ConversationEntry[] {\n return readConversationPath(getConversationPath(userId, topicName));\n}\n\n/** Atomically replace only the provider's active context projection. */\nexport function replaceConversationStrict(\n userId: number | string,\n topicName: string,\n entries: ConversationEntry[],\n): void {\n replaceConversationPathStrict(getActiveConversationPath(userId, topicName), entries);\n}\n\n/** Atomically seed or restore the append-only stream before it becomes live. */\nexport function replaceRawConversationStrict(\n userId: number | string,\n topicName: string,\n entries: ConversationEntry[],\n): void {\n replaceConversationPathStrict(getConversationPath(userId, topicName), entries);\n}\n\nfunction replaceConversationPathStrict(path: string, entries: ConversationEntry[]): void {\n const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n mkdirSync(dirname(path), { recursive: true });\n try {\n writeFileSync(\n tempPath,\n entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n` : \"\",\n { flag: \"wx\" },\n );\n renameSync(tempPath, path);\n } catch (error) {\n try {\n unlinkSync(tempPath);\n } catch {}\n throw error;\n }\n}\n\n/**\n * Copy the unified conversation log of `srcTopic` into the file path that\n * `dstTopic` will read from. Used by `/fork` so a forked child topic inherits\n * the parent's full cross-agent history, not just the agent's native SDK\n * rollout. `/spawn` intentionally does not call this; it starts with no\n * conversation history. Without this copy, a fork followed immediately by\n * `/agent <other>` would feed the empty child log to `switchTopicAgent` and\n * the new agent would start from zero — see the bug report from 2026-05-24.\n *\n * Semantics:\n * - Copies the parent's raw stream and its active projection when present.\n * - Writes independent files so parent and child can diverge after the fork.\n * - Refuses to overwrite a non-empty destination (returns `{copied:false}`).\n * `/fork`'s only caller runs this immediately after topic creation when\n * the dst file is guaranteed empty, so a non-empty dst means a programmer\n * error somewhere upstream — fail loud rather than silently merging.\n * - On any I/O error: throws. The caller (`createChildTopic`) already has\n * a rollback path that wraps this call.\n */\nexport function cloneConversationLog(opts: {\n userId: number | string;\n srcTopic: string;\n dstTopic: string;\n}): { copied: boolean; entries: number } {\n const { userId, srcTopic, dstTopic } = opts;\n const dstPath = getConversationPath(userId, dstTopic);\n if (existsSync(dstPath) && readFileSync(dstPath, \"utf8\").trim().length > 0) {\n logger.warn(\n { userId, srcTopic, dstTopic, dstPath },\n \"cloneConversationLog: dst already non-empty — refusing to overwrite\",\n );\n return { copied: false, entries: 0 };\n }\n const entries = readRawConversation(userId, srcTopic);\n if (entries.length === 0) {\n return { copied: false, entries: 0 };\n }\n const body = `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n mkdirSync(dirname(dstPath), { recursive: true });\n writeFileSync(dstPath, body);\n const srcActivePath = getActiveConversationPath(userId, srcTopic);\n if (existsSync(srcActivePath)) {\n const dstActivePath = getActiveConversationPath(userId, dstTopic);\n writeFileSync(dstActivePath, readFileSync(srcActivePath));\n }\n return { copied: true, entries: entries.length };\n}\n\n/**\n * Walk the unified log backwards and return the most recent SDK-emitted\n * session id for the given agent — or null if that agent has never run on\n * this topic.\n *\n * Used by `set_agent` to round-trip the SAME native rollout file across\n * agent switches: when the user does claude → codex → claude, we reuse\n * the original claude sessionId so the synthetic rollout lands at the\n * same path the SDK already manages, preserving prompt-cache continuity\n * and avoiding orphan `~/.claude/projects/<dir>/<id>.jsonl` files.\n *\n * Behavior:\n * - First-ever switch to an agent (no prior session events) → null,\n * caller falls back to a fresh randomUUID/uuidv7. Same as before.\n * - Roundtrip switch (prior session exists) → that sessionId.\n *\n * `session` events are emitted on every turn by claude-/codex-provider and\n * captured into the unified log by runAgent's append wrapper. The most\n * recent one for the target agent is by definition the current SDK-side\n * resume key for that agent.\n */\nexport function findLastSessionIdForAgent(\n entries: ConversationEntry[],\n agent: AgentKind,\n): string | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.agent !== agent) continue;\n if (entry.event.type === \"session\") {\n return entry.event.sessionId;\n }\n }\n return null;\n}\n",
8
8
  "/** Sanitize a topic/session name for use in file paths.\n * Replaces non-alphanumeric characters (excluding Korean) with underscores.\n * Empty input (or input consisting only of disallowed chars) returns \"_\" so\n * callers always get a non-empty single path component.\n * @param name The raw topic or session name.\n * @param lowercase Whether to lowercase the result (default: false).\n * Use true for wiki paths; leave false for log file paths.\n */\nexport function sanitizeTopicName(name: string, lowercase = false): string {\n const safe = name.replace(/[^a-zA-Z0-9가-힣_-]/g, \"_\") || \"_\";\n return lowercase ? safe.toLowerCase() : safe;\n}\n\n/** Sanitize a file name for use in file paths. Allows dots.\n * Pure-dot results (\".\", \"..\") are replaced with \"_\" to block path-traversal\n * when the sanitized value is used as a standalone path component.\n */\nexport function sanitizeFileName(name: string): string {\n const safe = name.replace(/[^a-zA-Z0-9._-]/g, \"_\") || \"_\";\n if (safe === \".\" || safe === \"..\") return \"_\";\n return safe;\n}\n\n/** Sanitize an arbitrary ID (no Korean, no dots) for use in file paths. */\nexport function sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\") || \"_\";\n}\n",
9
- "import { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Database } from \"#storage/sqlite\";\nimport type { StorageDatabase, StorageHostConfig } from \"#storage/storage-contract\";\n\nexport type {\n StorageDatabase,\n StorageDatabaseAdapter,\n StorageDatabaseInput,\n StorageHostConfig,\n StorageHostOptions,\n StorageStatement,\n StorageTransaction,\n} from \"#storage/storage-contract\";\n\nlet configuredHost: Readonly<StorageHostConfig> = {};\ntype InternalStorageDatabase = InstanceType<typeof Database>;\ntype OwnedStorageDatabase = InternalStorageDatabase & { close(): void };\nlet fallbackDatabase: OwnedStorageDatabase | null = null;\nlet fallbackDatabasePath: string | null = null;\n\ninterface StorageHostFrame {\n active: boolean;\n patch: Readonly<StorageHostConfig>;\n}\n\nconst storageHostFrames: StorageHostFrame[] = [];\n\ntype StorageSchemaInitializer = (database: InternalStorageDatabase) => void;\ninterface RegisteredSchemaInitializer {\n initialize: StorageSchemaInitializer;\n priority: number;\n}\n\nconst schemaInitializers: RegisteredSchemaInitializer[] = [];\nconst initializedSchemas = new WeakMap<InternalStorageDatabase, Set<StorageSchemaInitializer>>();\nconst initializingDatabases = new WeakSet<InternalStorageDatabase>();\n\nfunction envPath(name: string, fallback: string): string {\n const value = process.env[name]?.trim();\n return resolve(value || fallback);\n}\n\nfunction defaultStateDir(): string {\n return envPath(\"NEGOTIUM_STATE_DIR\", join(homedir(), \".negotium\"));\n}\n\nfunction defaultDataDir(): string {\n return envPath(\"NEGOTIUM_DATA_DIR\", join(defaultStateDir(), \"data\"));\n}\n\nfunction defaultLogDir(): string {\n return envPath(\"NEGOTIUM_LOG_DIR\", join(defaultStateDir(), \"logs\"));\n}\n\nfunction defaultWorkspaceDir(): string {\n return envPath(\"NEGOTIUM_WORKSPACE_DIR\", join(defaultStateDir(), \"workspace\"));\n}\n\nfunction defaultSessionAsksDir(): string {\n const runDir = envPath(\"NEGOTIUM_RUN_DIR\", join(defaultStateDir(), \"run\"));\n return join(runDir, \"session-asks\");\n}\n\nfunction defaultSessionsDatabasePath(): string {\n return envPath(\"SESSIONS_DB_PATH\", join(resolveStorageDataDir(), \"sessions.db\"));\n}\n\nfunction initializeDatabase(database: InternalStorageDatabase): void {\n // busy_timeout FIRST. Switching to WAL needs a brief exclusive lock, so when\n // two processes open the same database at the same moment — the node daemon\n // and an adapter, or two MCP servers — one of them hits SQLITE_BUSY. With the\n // timeout configured afterwards there was no retry budget in effect for the\n // statement that needed it most, and the loser died with \"database is locked\"\n // during startup. Measured with four processes racing one fresh file: 60/100\n // opens failed in the old order, 0/100 with the timeout set first.\n database.exec(\"PRAGMA busy_timeout = 5000\");\n database.exec(\"PRAGMA journal_mode = WAL\");\n database.exec(\"PRAGMA foreign_keys = ON\");\n database.exec(\"PRAGMA wal_autocheckpoint = 1000\");\n try {\n database.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n } catch {\n // Non-fatal; a concurrent writer may hold the WAL briefly.\n }\n}\n\nfunction defaultDatabase(): InternalStorageDatabase {\n const path = defaultSessionsDatabasePath();\n if (fallbackDatabase && fallbackDatabasePath === path) return fallbackDatabase;\n if (fallbackDatabase) fallbackDatabase.close();\n mkdirSync(dirname(path), { recursive: true });\n fallbackDatabase = new Database(path, { create: true }) as unknown as OwnedStorageDatabase;\n fallbackDatabasePath = path;\n initializeDatabase(fallbackDatabase);\n return fallbackDatabase;\n}\n\nexport function resolveStorageDatabase(): InternalStorageDatabase {\n return (configuredHost.database ?? defaultDatabase()) as InternalStorageDatabase;\n}\n\nexport function resolveStorageDataDir(): string {\n return configuredHost.dataDir ?? defaultDataDir();\n}\n\nexport function resolveStorageLogDir(): string {\n return configuredHost.logDir ?? defaultLogDir();\n}\n\nexport function resolveStorageSessionAsksDir(): string {\n return configuredHost.sessionAsksDir ?? defaultSessionAsksDir();\n}\n\nexport function resolveStorageWorkspaceDir(): string {\n return configuredHost.workspaceDir ?? defaultWorkspaceDir();\n}\n\nexport function resolveStorageSharedWikiDir(): string {\n return configuredHost.sharedWikiDir ?? join(resolveStorageWorkspaceDir(), \"wiki\");\n}\n\nexport function resolveStorageUsersLogDir(): string {\n return configuredHost.usersLogDir ?? join(resolveStorageDataDir(), \"users\");\n}\n\nconst STORAGE_PATH_KEYS = [\n \"dataDir\",\n \"logDir\",\n \"sessionAsksDir\",\n \"workspaceDir\",\n \"sharedWikiDir\",\n \"usersLogDir\",\n] as const;\n\nfunction normalizeStorageHostPatch(options: StorageHostConfig): Readonly<StorageHostConfig> {\n const patch: StorageHostConfig = {};\n if (options.database !== undefined) {\n options.database.exec(\"PRAGMA foreign_keys = ON\");\n patch.database = options.database;\n }\n for (const key of STORAGE_PATH_KEYS) {\n const value = options[key];\n if (value === undefined) continue;\n if (!value.trim()) throw new TypeError(`${key} must not be empty`);\n patch[key] = resolve(value);\n }\n return Object.freeze(patch);\n}\n\nfunction refreshConfiguredHost(): void {\n configuredHost = Object.freeze(\n Object.assign(\n {},\n ...storageHostFrames.filter((frame) => frame.active).map((frame) => frame.patch),\n ),\n );\n}\n\n/**\n * Configure the process-local storage boundary for an embedding host.\n *\n * Resolution is lazy: importing `negotium/storage` never opens a database or\n * touches a filesystem path. The returned disposer restores the exact prior\n * host, which keeps tests and nested embeddings isolated.\n */\nexport function configureStorageHost(options: StorageHostConfig): () => void {\n const frame: StorageHostFrame = { active: true, patch: normalizeStorageHostPatch(options) };\n storageHostFrames.push(frame);\n refreshConfiguredHost();\n return () => {\n if (!frame.active) return;\n frame.active = false;\n const index = storageHostFrames.indexOf(frame);\n if (index >= 0) storageHostFrames.splice(index, 1);\n refreshConfiguredHost();\n };\n}\n\n/** Remove every configured host layer and restore standalone fallbacks. */\nexport function resetStorageHost(): void {\n for (const frame of storageHostFrames) frame.active = false;\n storageHostFrames.length = 0;\n refreshConfiguredHost();\n}\n\n/** Close only Negotium's fallback connection. Injected connections are borrowed. */\nexport function closeStorageDatabase(): void {\n if (!fallbackDatabase) return;\n fallbackDatabase.close();\n fallbackDatabase = null;\n fallbackDatabasePath = null;\n}\n\nexport function registerStorageSchemaInitializer(\n initialize: StorageSchemaInitializer,\n priority = 100,\n): void {\n schemaInitializers.push({ initialize, priority });\n schemaInitializers.sort((a, b) => a.priority - b.priority);\n}\n\nexport function ensureStorageSchemas(\n database: InternalStorageDatabase = resolveStorageDatabase(),\n): void {\n if (initializingDatabases.has(database)) return;\n let initialized = initializedSchemas.get(database);\n if (!initialized) {\n initialized = new Set();\n initializedSchemas.set(database, initialized);\n }\n initializingDatabases.add(database);\n try {\n for (const entry of schemaInitializers) {\n if (initialized.has(entry.initialize)) continue;\n // Mark first so a migration that calls through the public db proxy does\n // not recursively invoke itself. Remove on failure so the next call can retry.\n initialized.add(entry.initialize);\n try {\n entry.initialize(database);\n } catch (error) {\n initialized.delete(entry.initialize);\n throw error;\n }\n }\n } finally {\n initializingDatabases.delete(database);\n }\n}\n\n/** Stable proxy identity used by legacy imports and embedding hosts. */\nexport const internalStorageDatabase = new Proxy({} as InternalStorageDatabase, {\n get(_target, property) {\n const database = resolveStorageDatabase();\n ensureStorageSchemas(database);\n const value = Reflect.get(database as object, property, database);\n return typeof value === \"function\" ? value.bind(database) : value;\n },\n set(_target, property, value) {\n const database = resolveStorageDatabase();\n ensureStorageSchemas(database);\n return Reflect.set(database as object, property, value, database);\n },\n});\n\n/** Structurally typed view intended for embedding hosts. */\nexport const storageDatabase = internalStorageDatabase as unknown as StorageDatabase;\n",
9
+ "import { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Database } from \"#storage/sqlite\";\nimport type { StorageDatabase, StorageHostConfig } from \"#storage/storage-contract\";\n\nexport type {\n StorageDatabase,\n StorageDatabaseAdapter,\n StorageDatabaseInput,\n StorageHostConfig,\n StorageHostOptions,\n StorageStatement,\n StorageTransaction,\n} from \"#storage/storage-contract\";\n\nlet configuredHost: Readonly<StorageHostConfig> = {};\ntype InternalStorageDatabase = InstanceType<typeof Database>;\ntype OwnedStorageDatabase = InternalStorageDatabase & { close(): void };\nlet fallbackDatabase: OwnedStorageDatabase | null = null;\nlet fallbackDatabasePath: string | null = null;\n\ninterface StorageHostFrame {\n active: boolean;\n patch: Readonly<StorageHostConfig>;\n}\n\nconst storageHostFrames: StorageHostFrame[] = [];\n\ntype StorageSchemaInitializer = (database: InternalStorageDatabase) => void;\ninterface RegisteredSchemaInitializer {\n initialize: StorageSchemaInitializer;\n priority: number;\n}\n\nconst schemaInitializers: RegisteredSchemaInitializer[] = [];\nconst initializedSchemas = new WeakMap<InternalStorageDatabase, Set<StorageSchemaInitializer>>();\nconst initializingDatabases = new WeakSet<InternalStorageDatabase>();\n\nfunction envPath(name: string, fallback: string): string {\n const value = process.env[name]?.trim();\n return resolve(value || fallback);\n}\n\nfunction defaultStateDir(): string {\n return envPath(\"NEGOTIUM_STATE_DIR\", join(homedir(), \".negotium\"));\n}\n\nfunction defaultDataDir(): string {\n return envPath(\"NEGOTIUM_DATA_DIR\", join(defaultStateDir(), \"data\"));\n}\n\nfunction defaultLogDir(): string {\n return envPath(\"NEGOTIUM_LOG_DIR\", join(defaultStateDir(), \"logs\"));\n}\n\nfunction defaultWorkspaceDir(): string {\n return envPath(\"NEGOTIUM_WORKSPACE_DIR\", join(defaultStateDir(), \"workspace\"));\n}\n\nfunction defaultSessionAsksDir(): string {\n const runDir = envPath(\"NEGOTIUM_RUN_DIR\", join(defaultStateDir(), \"runtime\"));\n return join(runDir, \"session-asks\");\n}\n\nfunction defaultSessionsDatabasePath(): string {\n return envPath(\"SESSIONS_DB_PATH\", join(resolveStorageDataDir(), \"sessions.db\"));\n}\n\nfunction initializeDatabase(database: InternalStorageDatabase): void {\n // busy_timeout FIRST. Switching to WAL needs a brief exclusive lock, so when\n // two processes open the same database at the same moment — the node daemon\n // and an adapter, or two MCP servers — one of them hits SQLITE_BUSY. With the\n // timeout configured afterwards there was no retry budget in effect for the\n // statement that needed it most, and the loser died with \"database is locked\"\n // during startup. Measured with four processes racing one fresh file: 60/100\n // opens failed in the old order, 0/100 with the timeout set first.\n database.exec(\"PRAGMA busy_timeout = 5000\");\n database.exec(\"PRAGMA journal_mode = WAL\");\n database.exec(\"PRAGMA foreign_keys = ON\");\n database.exec(\"PRAGMA wal_autocheckpoint = 1000\");\n try {\n database.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n } catch {\n // Non-fatal; a concurrent writer may hold the WAL briefly.\n }\n}\n\nfunction defaultDatabase(): InternalStorageDatabase {\n const path = defaultSessionsDatabasePath();\n if (fallbackDatabase && fallbackDatabasePath === path) return fallbackDatabase;\n if (fallbackDatabase) fallbackDatabase.close();\n mkdirSync(dirname(path), { recursive: true });\n fallbackDatabase = new Database(path, { create: true }) as unknown as OwnedStorageDatabase;\n fallbackDatabasePath = path;\n initializeDatabase(fallbackDatabase);\n return fallbackDatabase;\n}\n\nexport function resolveStorageDatabase(): InternalStorageDatabase {\n return (configuredHost.database ?? defaultDatabase()) as InternalStorageDatabase;\n}\n\nexport function resolveStorageDataDir(): string {\n return configuredHost.dataDir ?? defaultDataDir();\n}\n\nexport function resolveStorageLogDir(): string {\n return configuredHost.logDir ?? defaultLogDir();\n}\n\nexport function resolveStorageSessionAsksDir(): string {\n return configuredHost.sessionAsksDir ?? defaultSessionAsksDir();\n}\n\nexport function resolveStorageWorkspaceDir(): string {\n return configuredHost.workspaceDir ?? defaultWorkspaceDir();\n}\n\nexport function resolveStorageSharedWikiDir(): string {\n return configuredHost.sharedWikiDir ?? join(resolveStorageWorkspaceDir(), \"wiki\");\n}\n\nexport function resolveStorageUsersLogDir(): string {\n return configuredHost.usersLogDir ?? join(resolveStorageDataDir(), \"users\");\n}\n\nconst STORAGE_PATH_KEYS = [\n \"dataDir\",\n \"logDir\",\n \"sessionAsksDir\",\n \"workspaceDir\",\n \"sharedWikiDir\",\n \"usersLogDir\",\n] as const;\n\nfunction normalizeStorageHostPatch(options: StorageHostConfig): Readonly<StorageHostConfig> {\n const patch: StorageHostConfig = {};\n if (options.database !== undefined) {\n options.database.exec(\"PRAGMA foreign_keys = ON\");\n patch.database = options.database;\n }\n for (const key of STORAGE_PATH_KEYS) {\n const value = options[key];\n if (value === undefined) continue;\n if (!value.trim()) throw new TypeError(`${key} must not be empty`);\n patch[key] = resolve(value);\n }\n return Object.freeze(patch);\n}\n\nfunction refreshConfiguredHost(): void {\n configuredHost = Object.freeze(\n Object.assign(\n {},\n ...storageHostFrames.filter((frame) => frame.active).map((frame) => frame.patch),\n ),\n );\n}\n\n/**\n * Configure the process-local storage boundary for an embedding host.\n *\n * Resolution is lazy: importing `negotium/storage` never opens a database or\n * touches a filesystem path. The returned disposer restores the exact prior\n * host, which keeps tests and nested embeddings isolated.\n */\nexport function configureStorageHost(options: StorageHostConfig): () => void {\n const frame: StorageHostFrame = { active: true, patch: normalizeStorageHostPatch(options) };\n storageHostFrames.push(frame);\n refreshConfiguredHost();\n return () => {\n if (!frame.active) return;\n frame.active = false;\n const index = storageHostFrames.indexOf(frame);\n if (index >= 0) storageHostFrames.splice(index, 1);\n refreshConfiguredHost();\n };\n}\n\n/** Remove every configured host layer and restore standalone fallbacks. */\nexport function resetStorageHost(): void {\n for (const frame of storageHostFrames) frame.active = false;\n storageHostFrames.length = 0;\n refreshConfiguredHost();\n}\n\n/** Close only Negotium's fallback connection. Injected connections are borrowed. */\nexport function closeStorageDatabase(): void {\n if (!fallbackDatabase) return;\n fallbackDatabase.close();\n fallbackDatabase = null;\n fallbackDatabasePath = null;\n}\n\nexport function registerStorageSchemaInitializer(\n initialize: StorageSchemaInitializer,\n priority = 100,\n): void {\n schemaInitializers.push({ initialize, priority });\n schemaInitializers.sort((a, b) => a.priority - b.priority);\n}\n\nexport function ensureStorageSchemas(\n database: InternalStorageDatabase = resolveStorageDatabase(),\n): void {\n if (initializingDatabases.has(database)) return;\n let initialized = initializedSchemas.get(database);\n if (!initialized) {\n initialized = new Set();\n initializedSchemas.set(database, initialized);\n }\n initializingDatabases.add(database);\n try {\n for (const entry of schemaInitializers) {\n if (initialized.has(entry.initialize)) continue;\n // Mark first so a migration that calls through the public db proxy does\n // not recursively invoke itself. Remove on failure so the next call can retry.\n initialized.add(entry.initialize);\n try {\n entry.initialize(database);\n } catch (error) {\n initialized.delete(entry.initialize);\n throw error;\n }\n }\n } finally {\n initializingDatabases.delete(database);\n }\n}\n\n/** Stable proxy identity used by legacy imports and embedding hosts. */\nexport const internalStorageDatabase = new Proxy({} as InternalStorageDatabase, {\n get(_target, property) {\n const database = resolveStorageDatabase();\n ensureStorageSchemas(database);\n const value = Reflect.get(database as object, property, database);\n return typeof value === \"function\" ? value.bind(database) : value;\n },\n set(_target, property, value) {\n const database = resolveStorageDatabase();\n ensureStorageSchemas(database);\n return Reflect.set(database as object, property, value, database);\n },\n});\n\n/** Structurally typed view intended for embedding hosts. */\nexport const storageDatabase = internalStorageDatabase as unknown as StorageDatabase;\n",
10
10
  "// Runtime-adaptive SQLite façade.\n//\n// Why this exists: Otium normally runs on bun and uses `bun:sqlite` (native,\n// fast, zero-build). But the `codex` agent's MCP servers must run on pure node\n// — codex 0.135's rmcp stdio client can't handshake with bun-spawned servers\n// (see serverLaunch in platform/mcp-config.ts). `bun:sqlite` doesn't exist\n// under node, so any MCP server that touches the DB would crash on import.\n//\n// This module picks the backend at runtime and re-exports it as `Database`:\n// - under bun → the real `bun:sqlite` Database (unchanged behavior)\n// - under node → a thin shim over node:sqlite's DatabaseSync exposing the\n// exact subset Otium uses: new Database(path, {readonly?,create?}),\n// .query()/.prepare() → statement, .exec(), .run(), .transaction(fn),\n// .close(); statements support positional `?` params via .run()/.get()/\n// .all(). (No named-object params / .values()/.iterate()/.as() are used in\n// this codebase, so the shim intentionally omits them.)\n//\n// The TYPE of the exported `Database` is bun:sqlite's own class type (imported\n// type-only, so it's erased at runtime and never resolved under node). That\n// keeps every existing call site — including generic `.query<Row, Params>()`\n// usages — type-checking exactly as before with zero changes.\n//\n// The bun branch uses a dynamic import so node never resolves the `bun:sqlite`\n// specifier (dead code under node), and the node branch's `node:sqlite` import\n// never runs under bun.\n\nimport type { Database as BunDatabase } from \"bun:sqlite\";\n\n// `mock.module()` can replace the Bun global while a test graph is being\n// evaluated. The runtime version flag is stable and is also the documented\n// cross-runtime discriminator.\nconst isBun = typeof process.versions.bun === \"string\";\n\ntype DatabaseCtor = typeof BunDatabase;\n\nlet Database: DatabaseCtor;\n\nif (isBun) {\n ({ Database } = await import(\"bun:sqlite\"));\n} else {\n // node:sqlite has no type declarations under our tsconfig (`types: bun-types`\n // only), and `bun-types` doesn't ship them — so type the dynamic import\n // locally and use a `string` specifier to keep tsc from trying to resolve it.\n type NodeStatement = {\n run(...params: unknown[]): unknown;\n get(...params: unknown[]): unknown;\n all(...params: unknown[]): unknown[];\n };\n type NodeDatabaseSync = {\n prepare(sql: string): NodeStatement;\n exec(sql: string): void;\n close(): void;\n };\n type NodeSqliteModule = {\n DatabaseSync: new (path: string, options?: { readOnly?: boolean }) => NodeDatabaseSync;\n };\n // Keep Bun's resolver from eagerly resolving the Node-only builtin even\n // though this branch is dead there (notably under `bun test` + mock.module).\n const nodeSqliteSpecifier = [\"node\", \"sqlite\"].join(\":\");\n const { DatabaseSync } = (await import(nodeSqliteSpecifier)) as NodeSqliteModule;\n\n type DbOptions = { readonly?: boolean; create?: boolean };\n\n class NodeDatabase {\n #db: NodeDatabaseSync;\n\n constructor(path: string, options: DbOptions = {}) {\n // node:sqlite opens read-write and creates-if-missing by default, which\n // matches bun's `{create:true}`. For readonly we must not create.\n this.#db = options.readonly\n ? new DatabaseSync(path, { readOnly: true })\n : new DatabaseSync(path);\n }\n\n query(sql: string) {\n return this.#db.prepare(sql);\n }\n\n prepare(sql: string) {\n return this.#db.prepare(sql);\n }\n\n exec(sql: string): void {\n this.#db.exec(sql);\n }\n\n run(sql: string, ...params: unknown[]) {\n return this.#db.prepare(sql).run(...params);\n }\n\n transaction<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R {\n // bun's db.transaction(fn) returns a function that runs fn inside a\n // transaction when called. node:sqlite has no helper, so emulate with\n // BEGIN/COMMIT/ROLLBACK. It also exposes deferred/immediate/exclusive\n // variants; mirror those so Node-launched MCP servers can share call\n // sites with Bun.\n const run =\n (begin: \"BEGIN\" | \"BEGIN DEFERRED\" | \"BEGIN IMMEDIATE\" | \"BEGIN EXCLUSIVE\") =>\n (...args: Args): R => {\n this.#db.exec(begin);\n try {\n const result = fn(...args);\n this.#db.exec(\"COMMIT\");\n return result;\n } catch (err) {\n this.#db.exec(\"ROLLBACK\");\n throw err;\n }\n };\n const tx = run(\"BEGIN\") as ((...args: Args) => R) & {\n deferred: (...args: Args) => R;\n immediate: (...args: Args) => R;\n exclusive: (...args: Args) => R;\n };\n tx.deferred = run(\"BEGIN DEFERRED\");\n tx.immediate = run(\"BEGIN IMMEDIATE\");\n tx.exclusive = run(\"BEGIN EXCLUSIVE\");\n return tx;\n }\n\n close(): void {\n this.#db.close();\n }\n }\n\n Database = NodeDatabase as unknown as DatabaseCtor;\n}\n\nexport { Database };\n",
11
- "import { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type { AgentRegistry, AgentRegistryOperations } from \"#agents/contracts\";\nimport { assertUuidLike, ensureCwdExists, extractChatPairs } from \"#agents/rollout/shared\";\nimport { logger } from \"#platform/logger\";\nimport { readConversation } from \"#storage/conversations\";\nimport { MAESTRO_EFFORT_VALUES } from \"#types\";\n\nconst ALIAS_MAP: Record<string, string> = {\n \"deepseek-pro\": \"deepseek-v4-pro\",\n kimi: \"kimi-k3\",\n \"kimi-pro\": \"kimi-k3\",\n \"kimi-code\": \"kimi-k2.7-code\",\n};\nconst VALID_MODELS = new Set([...Object.keys(ALIAS_MAP), ...Object.values(ALIAS_MAP)]);\nconst VALID_EFFORTS = new Set(MAESTRO_EFFORT_VALUES);\n\nexport const maestroRegistry: AgentRegistry = {\n kind: \"maestro\",\n defaultModel: \"deepseek-pro\",\n defaultEffort: \"medium\",\n expandModelAlias(model) {\n return ALIAS_MAP[model] ?? model;\n },\n validateModel(model) {\n return VALID_MODELS.has(model);\n },\n validEfforts: MAESTRO_EFFORT_VALUES,\n validateEffort(effort) {\n return VALID_EFFORTS.has(effort);\n },\n footerLabel(model, effort) {\n return effort ? `${model} · ${effort}` : model;\n },\n};\n\nfunction maestroSessionsDir(): string {\n return join(\n process.env.MAESTRO_DATA_DIR\n ? resolve(process.env.MAESTRO_DATA_DIR)\n : join(homedir(), \".maestro\"),\n \"sessions\",\n );\n}\n\nfunction maestroSessionPath(sessionId: string): string {\n return join(maestroSessionsDir(), `${sessionId}.jsonl`);\n}\n\nfunction maestroActiveSessionPath(sessionId: string): string {\n return join(maestroSessionsDir(), `${sessionId}.active.jsonl`);\n}\n\nfunction existingCreatedAt(path: string): string | undefined {\n if (!existsSync(path)) return undefined;\n try {\n const firstLine = readFileSync(path, \"utf8\").split(\"\\n\", 1)[0];\n const parsed = JSON.parse(firstLine) as { _meta?: { createdAt?: unknown } };\n return typeof parsed._meta?.createdAt === \"string\" ? parsed._meta.createdAt : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeRollout(options: Parameters<AgentRegistryOperations[\"writeRollout\"]>[0]) {\n const sessionId = options.reuseSessionId ?? randomUUID();\n assertUuidLike(\"sessionId\", sessionId);\n ensureCwdExists(options.cwd);\n const path = maestroSessionPath(sessionId);\n mkdirSync(maestroSessionsDir(), { recursive: true });\n const messages = extractChatPairs(options.entries).flatMap((pair) => [\n { role: \"user\", content: pair.userText },\n { role: \"assistant\", content: pair.assistantText },\n ]);\n const lines = [\n {\n _meta: {\n version: 1,\n cwd: options.cwd,\n createdAt: existingCreatedAt(path) ?? new Date().toISOString(),\n sdkVersion: \"0.1.53\",\n },\n },\n ...messages,\n ];\n writeFileSync(path, `${lines.map((line) => JSON.stringify(line)).join(\"\\n\")}\\n`, {\n mode: 0o600,\n });\n return { sessionId, rolloutPath: path };\n}\n\nexport const maestroRegistryOperations: AgentRegistryOperations = {\n writeRollout(options) {\n return writeRollout(options);\n },\n async forkSession(options) {\n const rollout = writeRollout({\n cwd: options.cwd,\n entries: readConversation(options.userId, options.topicName),\n ...(options.model ? { model: options.model } : {}),\n ...(options.effort ? { effort: options.effort } : {}),\n });\n return { forkId: rollout.sessionId, rolloutPath: rollout.rolloutPath };\n },\n async cleanupRollouts(options) {\n // Keep the SDK off the daemon startup path, but use its canonical cleanup\n // once a Maestro session is actually being removed. It also clears memory,\n // task, todo, and in-process file-state sidecars.\n const { deleteMaestroSession } = await import(\"maestro-agent-sdk\");\n const failures: unknown[] = [];\n for (const sessionId of options.sessionIds) {\n try {\n deleteMaestroSession(sessionId);\n const remaining = [\n maestroSessionPath(sessionId),\n maestroActiveSessionPath(sessionId),\n ].filter(existsSync);\n if (remaining.length > 0) {\n throw new Error(`Maestro session files remain after cleanup: ${remaining.join(\", \")}`);\n }\n } catch (error) {\n logger.warn({ err: error, sessionId }, \"maestro cleanupRollouts: cleanup failed\");\n failures.push(error);\n }\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, \"Maestro rollout cleanup failed\");\n }\n },\n};\n",
11
+ "import { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type { AgentRegistry, AgentRegistryOperations } from \"#agents/contracts\";\nimport { assertUuidLike, ensureCwdExists, extractChatPairs } from \"#agents/rollout/shared\";\nimport { logger } from \"#platform/logger\";\nimport { readConversation } from \"#storage/conversations\";\nimport { MAESTRO_EFFORT_VALUES } from \"#types\";\n\nconst ALIAS_MAP: Record<string, string> = {\n \"deepseek-pro\": \"deepseek-v4-pro\",\n kimi: \"kimi-k3\",\n \"kimi-pro\": \"kimi-k3\",\n \"kimi-code\": \"kimi-k2.7-code\",\n};\nconst VALID_MODELS = new Set([...Object.keys(ALIAS_MAP), ...Object.values(ALIAS_MAP)]);\nconst VALID_EFFORTS = new Set(MAESTRO_EFFORT_VALUES);\n\nexport const maestroRegistry: AgentRegistry = {\n kind: \"maestro\",\n defaultModel: \"deepseek-pro\",\n defaultEffort: \"medium\",\n expandModelAlias(model) {\n return ALIAS_MAP[model] ?? model;\n },\n validateModel(model) {\n return VALID_MODELS.has(model);\n },\n validEfforts: MAESTRO_EFFORT_VALUES,\n validateEffort(effort) {\n return VALID_EFFORTS.has(effort);\n },\n footerLabel(model, effort) {\n return effort ? `${model} · ${effort}` : model;\n },\n};\n\nfunction maestroSessionsDir(): string {\n return join(\n process.env.MAESTRO_DATA_DIR\n ? resolve(process.env.MAESTRO_DATA_DIR)\n : join(homedir(), \".maestro\"),\n \"sessions\",\n );\n}\n\nfunction maestroSessionPath(sessionId: string): string {\n return join(maestroSessionsDir(), `${sessionId}.jsonl`);\n}\n\nfunction maestroActiveSessionPath(sessionId: string): string {\n return join(maestroSessionsDir(), `${sessionId}.active.jsonl`);\n}\n\nfunction existingCreatedAt(path: string): string | undefined {\n if (!existsSync(path)) return undefined;\n try {\n const firstLine = readFileSync(path, \"utf8\").split(\"\\n\", 1)[0];\n const parsed = JSON.parse(firstLine) as { _meta?: { createdAt?: unknown } };\n return typeof parsed._meta?.createdAt === \"string\" ? parsed._meta.createdAt : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction writeRollout(options: Parameters<AgentRegistryOperations[\"writeRollout\"]>[0]) {\n const sessionId = options.reuseSessionId ?? randomUUID();\n assertUuidLike(\"sessionId\", sessionId);\n ensureCwdExists(options.cwd);\n const path = maestroSessionPath(sessionId);\n mkdirSync(maestroSessionsDir(), { recursive: true });\n const messages = extractChatPairs(options.entries).flatMap((pair) => [\n { role: \"user\", content: pair.userText },\n { role: \"assistant\", content: pair.assistantText },\n ]);\n const lines = [\n {\n _meta: {\n version: 1,\n cwd: options.cwd,\n createdAt: existingCreatedAt(path) ?? new Date().toISOString(),\n sdkVersion: \"0.1.54\",\n },\n },\n ...messages,\n ];\n writeFileSync(path, `${lines.map((line) => JSON.stringify(line)).join(\"\\n\")}\\n`, {\n mode: 0o600,\n });\n return { sessionId, rolloutPath: path };\n}\n\nexport const maestroRegistryOperations: AgentRegistryOperations = {\n writeRollout(options) {\n return writeRollout(options);\n },\n async forkSession(options) {\n const rollout = writeRollout({\n cwd: options.cwd,\n entries: readConversation(options.userId, options.topicName),\n ...(options.model ? { model: options.model } : {}),\n ...(options.effort ? { effort: options.effort } : {}),\n });\n return { forkId: rollout.sessionId, rolloutPath: rollout.rolloutPath };\n },\n async cleanupRollouts(options) {\n // Keep the SDK off the daemon startup path, but use its canonical cleanup\n // once a Maestro session is actually being removed. It also clears memory,\n // task, todo, and in-process file-state sidecars.\n const { deleteMaestroSession } = await import(\"maestro-agent-sdk\");\n const failures: unknown[] = [];\n for (const sessionId of options.sessionIds) {\n try {\n deleteMaestroSession(sessionId);\n const remaining = [\n maestroSessionPath(sessionId),\n maestroActiveSessionPath(sessionId),\n ].filter(existsSync);\n if (remaining.length > 0) {\n throw new Error(`Maestro session files remain after cleanup: ${remaining.join(\", \")}`);\n }\n } catch (error) {\n logger.warn({ err: error, sessionId }, \"maestro cleanupRollouts: cleanup failed\");\n failures.push(error);\n }\n }\n if (failures.length > 0) {\n throw new AggregateError(failures, \"Maestro rollout cleanup failed\");\n }\n },\n};\n",
12
12
  "import { claudeRegistry, claudeRegistryOperations } from \"#agents/claude-registry\";\nimport { codexRegistry, codexRegistryOperations } from \"#agents/codex-registry\";\nimport type { AgentRegistry, AgentRegistryOperations } from \"#agents/contracts\";\nimport { maestroRegistry, maestroRegistryOperations } from \"#agents/maestro-registry\";\nimport type { AgentKind } from \"#types\";\n\nexport type {\n AgentRegistry,\n AgentRegistryOperations,\n CleanupRolloutsOptions,\n ForkRegistryOptions,\n ForkRegistryResult,\n WriteRolloutOptions,\n WriteRolloutResult,\n} from \"#agents/contracts\";\n\nconst REGISTRIES: Record<AgentKind, AgentRegistry> = {\n claude: claudeRegistry,\n codex: codexRegistry,\n maestro: maestroRegistry,\n};\n\nexport function getRegistry(agent: AgentKind): AgentRegistry {\n return REGISTRIES[agent];\n}\n\nconst OPERATIONS: Record<AgentKind, AgentRegistryOperations> = {\n claude: claudeRegistryOperations,\n codex: codexRegistryOperations,\n maestro: maestroRegistryOperations,\n};\n\nexport function getRegistryOperations(agent: AgentKind): AgentRegistryOperations {\n return OPERATIONS[agent];\n}\n",
13
13
  "import {\n type AgentRegistry,\n type AgentRegistryOperations,\n type CleanupRolloutsOptions,\n type ForkRegistryOptions,\n type ForkRegistryResult,\n getRegistry as resolveCoreRegistry,\n getRegistryOperations as resolveCoreRegistryOperations,\n type WriteRolloutOptions,\n type WriteRolloutResult,\n} from \"@negotium/core/registry\";\n\nexport type {\n AgentRegistry,\n AgentRegistryOperations,\n CleanupRolloutsOptions,\n ForkRegistryOptions,\n ForkRegistryResult,\n WriteRolloutOptions,\n WriteRolloutResult,\n};\n\nexport const getRegistry: typeof resolveCoreRegistry = (agent) => resolveCoreRegistry(agent);\n\nexport const getRegistryOperations: typeof resolveCoreRegistryOperations = (agent) =>\n resolveCoreRegistryOperations(agent);\n"
14
14
  ],
15
- "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AAOA,IAAM,YAAoC;AAAA,EACxC,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,SAAS,CAAC;AACpD,IAAM,gBAAgB,IAAI,IAAiB,oBAAoB;AAExD,IAAM,iBAAgC;AAAA,EAC3C,MAAM;AAAA,EACN,cAAc;AAAA,EACd,eAAe;AAAA,EAEf,gBAAgB,CAAC,GAAG;AAAA,IAClB,OAAO,UAAU,MAAM;AAAA;AAAA,EAGzB,aAAa,CAAC,GAAG;AAAA,IACf,OAAO,cAAc,IAAI,CAAC;AAAA;AAAA,EAG5B,cAAc;AAAA,EACd,cAAc,CAAC,GAAG;AAAA,IAChB,OAAO,cAAc,IAAI,CAAC;AAAA;AAAA,EAG5B,WAAW,CAAC,OAAO,QAAQ;AAAA,IACzB,OAAO,SAAS,GAAG,cAAU,WAAW;AAAA;AAE5C;AAEO,IAAM,2BAAoD;AAAA,EAC/D,YAAY,CAAC,MAAM;AAAA,IAIjB,QAAQ,WAAW,gBAAgB,mBAAmB;AAAA,MACpD,KAAK,KAAK;AAAA,MACV,SAAS,KAAK;AAAA,MACd,OAAO,eAAe,iBAAiB,KAAK,SAAS,eAAe,YAAY;AAAA,SAC5E,KAAK,iBAAiB,EAAE,WAAW,KAAK,eAAe,IAAI,CAAC;AAAA,IAClE,CAAC;AAAA,IACD,OAAO,EAAE,WAAW,YAAY;AAAA;AAAA,OAY5B,YAAW,GAAG,iBAAiB,KAAK,SAAS;AAAA,IACjD,QAAQ,gBAAgB,MAAa;AAAA,IAGrC,MAAM,SAAS,MAAM,YAAY,iBAAiB;AAAA,SAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC3B,CAAC;AAAA,IACD,MAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,UAAU;AAAA,IAC1D,MAAM,UAAU,KAAK,cAAc,gBAAgB,GAAG,CAAC;AAAA,IACvD,MAAM,WAAW,KAAK,SAAS,GAAG,OAAO,iBAAiB;AAAA,IAC1D,IAAI,CAAC,WAAW,QAAQ,GAAG;AAAA,MACzB,MAAM,aAAa,YAAY,YAAY,EACxC,IAAI,CAAC,MAAM,KAAK,cAAc,GAAG,GAAG,OAAO,iBAAiB,CAAC,EAC7D,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC;AAAA,MAC5B,IAAI,CAAC,YAAY;AAAA,QACf,MAAM,IAAI,MACR,oCAAoC,OAAO,mCAAmC,cAChF;AAAA,MACF;AAAA,MACA,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,MACtC,WAAW,YAAY,QAAQ;AAAA,IACjC;AAAA,IACA,OAAO;AAAA,MACL,QAAQ,OAAO;AAAA,MAIf,aAAa;AAAA,IACf;AAAA;AAAA,OAMI,gBAAe,GAAG,KAAK,cAAc;AAAA,IACzC,MAAM,cAAc,KAAK,QAAQ,GAAG,WAAW,YAAY,gBAAgB,GAAG,CAAC;AAAA,IAC/E,MAAM,WAAsB,CAAC;AAAA,IAC7B,WAAW,OAAO,YAAY;AAAA,MAC5B,MAAM,OAAO,KAAK,aAAa,GAAG,WAAW;AAAA,MAC7C,IAAI;AAAA,QACF,WAAW,IAAI;AAAA,QACf,OAAO,GAAG;AAAA,QACV,IAAK,GAA6B,SAAS,UAAU;AAAA,UACnD,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,uCAAuC;AAAA,UACrE,SAAS,KAAK,CAAC;AAAA,QACjB;AAAA;AAAA,IAEJ;AAAA,IACA,IAAI,SAAS,SAAS,GAAG;AAAA,MACvB,MAAM,IAAI,eAAe,UAAU,+BAA+B;AAAA,IACpE;AAAA;AAEJ;;;ACpHA,uBAAS,2BAAY;AACrB,oBAAS;AACT,iBAAS;;;ACFT;AAAA,gBACE;AAAA,eACA;AAAA;AAAA,gBAEA;AAAA,gBACA;AAAA;AAAA;AAGF,oBAAS,kBAAS;;;ACAX,SAAS,iBAAiB,CAAC,MAAc,YAAY,OAAe;AAAA,EACzE,MAAM,OAAO,KAAK,QAAQ,gCAAqB,GAAG,KAAK;AAAA,EACvD,OAAO,YAAY,KAAK,YAAY,IAAI;AAAA;;;ACV1C,sBAAS;AACT,oBAAS;AACT,0BAAkB;;;AC6BlB,IAAM,QAAQ,OAAO,QAAQ,SAAS,QAAQ;AAI9C,IAAI;AAEJ,IAAI,OAAO;AAAA,GACR,EAAE,SAAS,IAAI,MAAa;AAC/B,EAAO;AAAA,EAmBL,MAAM,sBAAsB,CAAC,QAAQ,QAAQ,EAAE,KAAK,GAAG;AAAA,EACvD,QAAQ,iBAAkB,MAAa;AAAA;AAAA,EAIvC,MAAM,aAAa;AAAA,IACjB;AAAA,IAEA,WAAW,CAAC,MAAc,UAAqB,CAAC,GAAG;AAAA,MAGjD,KAAK,MAAM,QAAQ,WACf,IAAI,aAAa,MAAM,EAAE,UAAU,KAAK,CAAC,IACzC,IAAI,aAAa,IAAI;AAAA;AAAA,IAG3B,KAAK,CAAC,KAAa;AAAA,MACjB,OAAO,KAAK,IAAI,QAAQ,GAAG;AAAA;AAAA,IAG7B,OAAO,CAAC,KAAa;AAAA,MACnB,OAAO,KAAK,IAAI,QAAQ,GAAG;AAAA;AAAA,IAG7B,IAAI,CAAC,KAAmB;AAAA,MACtB,KAAK,IAAI,KAAK,GAAG;AAAA;AAAA,IAGnB,GAAG,CAAC,QAAgB,QAAmB;AAAA,MACrC,OAAO,KAAK,IAAI,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAAA;AAAA,IAG5C,WAAsC,CAAC,IAAgD;AAAA,MAMrF,MAAM,MACJ,CAAC,UACD,IAAI,SAAkB;AAAA,QACpB,KAAK,IAAI,KAAK,KAAK;AAAA,QACnB,IAAI;AAAA,UACF,MAAM,SAAS,GAAG,GAAG,IAAI;AAAA,UACzB,KAAK,IAAI,KAAK,QAAQ;AAAA,UACtB,OAAO;AAAA,UACP,OAAO,KAAK;AAAA,UACZ,KAAK,IAAI,KAAK,UAAU;AAAA,UACxB,MAAM;AAAA;AAAA;AAAA,MAGZ,MAAM,KAAK,IAAI,OAAO;AAAA,MAKtB,GAAG,WAAW,IAAI,gBAAgB;AAAA,MAClC,GAAG,YAAY,IAAI,iBAAiB;AAAA,MACpC,GAAG,YAAY,IAAI,iBAAiB;AAAA,MACpC,OAAO;AAAA;AAAA,IAGT,KAAK,GAAS;AAAA,MACZ,KAAK,IAAI,MAAM;AAAA;AAAA,EAEnB;AAAA,EAEA,WAAW;AAAA;;;AD7Gb,IAAI,iBAA8C,CAAC;AAGnD,IAAI,mBAAgD;AACpD,IAAI,uBAAsC;AAe1C,IAAM,qBAAoD,CAAC;AAC3D,IAAM,qBAAqB,IAAI;AAC/B,IAAM,wBAAwB,IAAI;AAElC,SAAS,OAAO,CAAC,MAAc,UAA0B;AAAA,EACvD,MAAM,QAAQ,QAAQ,IAAI,OAAO,KAAK;AAAA,EACtC,OAAO,QAAQ,SAAS,QAAQ;AAAA;AAGlC,SAAS,eAAe,GAAW;AAAA,EACjC,OAAO,QAAQ,sBAAsB,MAAK,SAAQ,GAAG,WAAW,CAAC;AAAA;AAGnE,SAAS,cAAc,GAAW;AAAA,EAChC,OAAO,QAAQ,qBAAqB,MAAK,gBAAgB,GAAG,MAAM,CAAC;AAAA;AAgBrE,SAAS,2BAA2B,GAAW;AAAA,EAC7C,OAAO,QAAQ,oBAAoB,MAAK,sBAAsB,GAAG,aAAa,CAAC;AAAA;AAGjF,SAAS,kBAAkB,CAAC,UAAyC;AAAA,EAQnE,SAAS,KAAK,4BAA4B;AAAA,EAC1C,SAAS,KAAK,2BAA2B;AAAA,EACzC,SAAS,KAAK,0BAA0B;AAAA,EACxC,SAAS,KAAK,kCAAkC;AAAA,EAChD,IAAI;AAAA,IACF,SAAS,KAAK,iCAAiC;AAAA,IAC/C,MAAM;AAAA;AAKV,SAAS,eAAe,GAA4B;AAAA,EAClD,MAAM,OAAO,4BAA4B;AAAA,EACzC,IAAI,oBAAoB,yBAAyB;AAAA,IAAM,OAAO;AAAA,EAC9D,IAAI;AAAA,IAAkB,iBAAiB,MAAM;AAAA,EAC7C,WAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EAC5C,mBAAmB,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,EACtD,uBAAuB;AAAA,EACvB,mBAAmB,gBAAgB;AAAA,EACnC,OAAO;AAAA;AAGF,SAAS,sBAAsB,GAA4B;AAAA,EAChE,OAAQ,eAAe,YAAY,gBAAgB;AAAA;AAG9C,SAAS,qBAAqB,GAAW;AAAA,EAC9C,OAAO,eAAe,WAAW,eAAe;AAAA;AAmG3C,SAAS,oBAAoB,CAClC,WAAoC,uBAAuB,GACrD;AAAA,EACN,IAAI,sBAAsB,IAAI,QAAQ;AAAA,IAAG;AAAA,EACzC,IAAI,cAAc,mBAAmB,IAAI,QAAQ;AAAA,EACjD,IAAI,CAAC,aAAa;AAAA,IAChB,cAAc,IAAI;AAAA,IAClB,mBAAmB,IAAI,UAAU,WAAW;AAAA,EAC9C;AAAA,EACA,sBAAsB,IAAI,QAAQ;AAAA,EAClC,IAAI;AAAA,IACF,WAAW,SAAS,oBAAoB;AAAA,MACtC,IAAI,YAAY,IAAI,MAAM,UAAU;AAAA,QAAG;AAAA,MAGvC,YAAY,IAAI,MAAM,UAAU;AAAA,MAChC,IAAI;AAAA,QACF,MAAM,WAAW,QAAQ;AAAA,QACzB,OAAO,OAAO;AAAA,QACd,YAAY,OAAO,MAAM,UAAU;AAAA,QACnC,MAAM;AAAA;AAAA,IAEV;AAAA,YACA;AAAA,IACA,sBAAsB,OAAO,QAAQ;AAAA;AAAA;AAKlC,IAAM,0BAA0B,IAAI,MAAM,CAAC,GAA8B;AAAA,EAC9E,GAAG,CAAC,SAAS,UAAU;AAAA,IACrB,MAAM,WAAW,uBAAuB;AAAA,IACxC,qBAAqB,QAAQ;AAAA,IAC7B,MAAM,QAAQ,QAAQ,IAAI,UAAoB,UAAU,QAAQ;AAAA,IAChE,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,IAAI;AAAA;AAAA,EAE9D,GAAG,CAAC,SAAS,UAAU,OAAO;AAAA,IAC5B,MAAM,WAAW,uBAAuB;AAAA,IACxC,qBAAqB,QAAQ;AAAA,IAC7B,OAAO,QAAQ,IAAI,UAAoB,UAAU,OAAO,QAAQ;AAAA;AAEpE,CAAC;;;AFhMD,SAAS,mBAAmB,CAAC,QAAiC;AAAA,EAC5D,MAAM,MAAM,OAAO,MAAM;AAAA,EACzB,IAAI,CAAC,OAAO,aAAa,KAAK,GAAG,GAAG;AAAA,IAClC,MAAM,IAAI,MAAM,yDAAyD,KAAK;AAAA,EAChF;AAAA,EACA,OAAO;AAAA;AAGT,SAAS,eAAe,CAAC,QAAiC;AAAA,EACxD,OAAO,MAAK,sBAAsB,GAAG,iBAAiB,oBAAoB,MAAM,CAAC;AAAA;AAGnF,SAAS,aAAa,CAAC,WAA2B;AAAA,EAChD,MAAM,IAAI,kBAAkB,WAAW,IAAI;AAAA,EAC3C,OAAO,GAAG;AAAA;AAIL,SAAS,mBAAmB,CAAC,QAAyB,WAA2B;AAAA,EACtF,OAAO,MAAK,gBAAgB,MAAM,GAAG,cAAc,SAAS,CAAC;AAAA;AAIxD,SAAS,yBAAyB,CAAC,QAAyB,WAA2B;AAAA,EAC5F,MAAM,UAAU,oBAAoB,QAAQ,SAAS;AAAA,EACrD,OAAO,QAAQ,SAAS,QAAQ,IAC5B,GAAG,QAAQ,MAAM,GAAG,CAAC,SAAS,MAAM,mBACpC,GAAG;AAAA;AA4HT,SAAS,oBAAoB,CAAC,MAAmC;AAAA,EAC/D,MAAM,MAA2B,CAAC;AAAA,EAClC,IAAI,CAAC,YAAW,IAAI;AAAA,IAAG,OAAO;AAAA,EAC9B,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,MAAM,aAAa,MAAM,MAAM;AAAA,IAC/B,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK,EAAE,KAAK,KAAK,GAAG,+BAA+B;AAAA,IAC1D,OAAO;AAAA;AAAA,EAET,WAAW,QAAQ,IAAI,MAAM;AAAA,CAAI,GAAG;AAAA,IAClC,IAAI,CAAC,KAAK,KAAK;AAAA,MAAG;AAAA,IAClB,IAAI;AAAA,MACF,IAAI,KAAK,KAAK,MAAM,IAAI,CAAsB;AAAA,MAC9C,OAAO,KAAK;AAAA,MACZ,OAAO,KACL,EAAE,KAAK,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE,GAChC,gDACF;AAAA;AAAA,EAEJ;AAAA,EACA,OAAO;AAAA;AAaF,SAAS,gBAAgB,CAAC,QAAyB,WAAwC;AAAA,EAChG,MAAM,aAAa,0BAA0B,QAAQ,SAAS;AAAA,EAC9D,OAAO,qBACL,YAAW,UAAU,IAAI,aAAa,oBAAoB,QAAQ,SAAS,CAC7E;AAAA;;;ADxOF,IAAM,iBAAgB,IAAI,IAAiB,mBAAmB;AAQvD,IAAM,gBAA+B;AAAA,EAC1C,MAAM;AAAA,EACN,cAAc;AAAA,EAId,gBAAgB,CAAC,GAAG;AAAA,IAClB,OAAO;AAAA;AAAA,EAGT,aAAa,CAAC,GAAG;AAAA,IAIf,OAAO,OAAO,MAAM,YAAY,EAAE,SAAS;AAAA;AAAA,EAG7C,cAAc;AAAA,EACd,cAAc,CAAC,GAAG;AAAA,IAIhB,OAAO,eAAc,IAAI,CAAC;AAAA;AAAA,EAG5B,WAAW,CAAC,OAAO,QAAQ;AAAA,IAGzB,OAAO,GAAG,cAAU,UAAU;AAAA;AAElC;AAEO,IAAM,0BAAmD;AAAA,EAC9D,YAAY,CAAC,MAAM;AAAA,IAMjB,QAAQ,UAAU,gBAAgB,kBAAkB;AAAA,MAClD,KAAK,KAAK;AAAA,MACV,SAAS,KAAK;AAAA,MACd,OAAO,KAAK,SAAS,cAAc;AAAA,SAC/B,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,SACzC,KAAK,iBAAiB,EAAE,UAAU,KAAK,eAAe,IAAI,CAAC;AAAA,IACjE,CAAC;AAAA,IACD,OAAO,EAAE,WAAW,UAAU,YAAY;AAAA;AAAA,OAQtC,YAAW,GAAG,KAAK,QAAQ,WAAW,OAAO,UAAU;AAAA,IAC3D,MAAM,UAAU,iBAAiB,QAAQ,SAAS;AAAA,IAClD,QAAQ,UAAU,gBAAgB,kBAAkB;AAAA,MAClD;AAAA,MACA;AAAA,MACA,OAAO,SAAS,cAAc;AAAA,SAC1B,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7B,CAAC;AAAA,IACD,OAAO,EAAE,QAAQ,UAAU,YAAY;AAAA;AAAA,OAQnC,gBAAe,GAAG,cAAc;AAAA,IACpC,IAAI,WAAW,WAAW;AAAA,MAAG;AAAA,IAC7B,MAAM,cAAc,MAAK,QAAQ,IAAI,cAAc,MAAK,SAAQ,GAAG,QAAQ,GAAG,UAAU;AAAA,IAExF,IAAI,CAAC,YAAW,WAAW;AAAA,MAAG;AAAA,IAC9B,MAAM,WAAsB,CAAC;AAAA,IAG7B,WAAW,OAAO,YAAY;AAAA,MAC5B,IAAI;AAAA,QACF,MAAM,OAAO,IAAI,IAAI,KAAK,gBAAgB,WAAW;AAAA,QACrD,iBAAiB,OAAO,KAAK,KAAK,EAAE,KAAK,aAAa,WAAW,KAAK,CAAC,GAAG;AAAA,UACxE,MAAM,OAAO,MAAK,aAAa,GAAG;AAAA,UAClC,IAAI;AAAA,YACF,YAAW,IAAI;AAAA,YACf,OAAO,GAAG;AAAA,YACV,IAAK,GAA6B,SAAS,UAAU;AAAA,cACnD,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,sCAAsC;AAAA,cACpE,SAAS,KAAK,CAAC;AAAA,YACjB;AAAA;AAAA,QAEJ;AAAA,QACA,OAAO,GAAG;AAAA,QACV,OAAO,KAAK,EAAE,KAAK,GAAG,UAAU,IAAI,GAAG,oCAAoC;AAAA,QAC3E,SAAS,KAAK,CAAC;AAAA;AAAA,IAEnB;AAAA,IACA,IAAI,SAAS,SAAS,GAAG;AAAA,MACvB,MAAM,IAAI,eAAe,UAAU,8BAA8B;AAAA,IACnE;AAAA;AAEJ;;;AKtHA;AACA,uBAAS,0BAAY,4BAAW,gCAAc;AAC9C,oBAAS;AACT,iBAAS,kBAAM;AAOf,IAAM,aAAoC;AAAA,EACxC,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,aAAa;AACf;AACA,IAAM,eAAe,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,UAAS,GAAG,GAAG,OAAO,OAAO,UAAS,CAAC,CAAC;AACrF,IAAM,iBAAgB,IAAI,IAAI,qBAAqB;AAE5C,IAAM,kBAAiC;AAAA,EAC5C,MAAM;AAAA,EACN,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB,CAAC,OAAO;AAAA,IACtB,OAAO,WAAU,UAAU;AAAA;AAAA,EAE7B,aAAa,CAAC,OAAO;AAAA,IACnB,OAAO,aAAa,IAAI,KAAK;AAAA;AAAA,EAE/B,cAAc;AAAA,EACd,cAAc,CAAC,QAAQ;AAAA,IACrB,OAAO,eAAc,IAAI,MAAM;AAAA;AAAA,EAEjC,WAAW,CAAC,OAAO,QAAQ;AAAA,IACzB,OAAO,SAAS,GAAG,cAAU,WAAW;AAAA;AAE5C;AAEA,SAAS,kBAAkB,GAAW;AAAA,EACpC,OAAO,MACL,QAAQ,IAAI,mBACR,SAAQ,QAAQ,IAAI,gBAAgB,IACpC,MAAK,SAAQ,GAAG,UAAU,GAC9B,UACF;AAAA;AAGF,SAAS,kBAAkB,CAAC,WAA2B;AAAA,EACrD,OAAO,MAAK,mBAAmB,GAAG,GAAG,iBAAiB;AAAA;AAGxD,SAAS,wBAAwB,CAAC,WAA2B;AAAA,EAC3D,OAAO,MAAK,mBAAmB,GAAG,GAAG,wBAAwB;AAAA;AAG/D,SAAS,iBAAiB,CAAC,MAAkC;AAAA,EAC3D,IAAI,CAAC,YAAW,IAAI;AAAA,IAAG;AAAA,EACvB,IAAI;AAAA,IACF,MAAM,YAAY,cAAa,MAAM,MAAM,EAAE,MAAM;AAAA,GAAM,CAAC,EAAE;AAAA,IAC5D,MAAM,SAAS,KAAK,MAAM,SAAS;AAAA,IACnC,OAAO,OAAO,OAAO,OAAO,cAAc,WAAW,OAAO,MAAM,YAAY;AAAA,IAC9E,MAAM;AAAA,IACN;AAAA;AAAA;AAIJ,SAAS,YAAY,CAAC,SAAiE;AAAA,EACrF,MAAM,YAAY,QAAQ,kBAAkB,WAAW;AAAA,EACvD,eAAe,aAAa,SAAS;AAAA,EACrC,gBAAgB,QAAQ,GAAG;AAAA,EAC3B,MAAM,OAAO,mBAAmB,SAAS;AAAA,EACzC,WAAU,mBAAmB,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACnD,MAAM,WAAW,iBAAiB,QAAQ,OAAO,EAAE,QAAQ,CAAC,SAAS;AAAA,IACnE,EAAE,MAAM,QAAQ,SAAS,KAAK,SAAS;AAAA,IACvC,EAAE,MAAM,aAAa,SAAS,KAAK,cAAc;AAAA,EACnD,CAAC;AAAA,EACD,MAAM,QAAQ;AAAA,IACZ;AAAA,MACE,OAAO;AAAA,QACL,SAAS;AAAA,QACT,KAAK,QAAQ;AAAA,QACb,WAAW,kBAAkB,IAAI,KAAK,IAAI,KAAK,EAAE,YAAY;AAAA,QAC7D,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,GAAG;AAAA,EACL;AAAA,EACA,eAAc,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK;AAAA,CAAI;AAAA,GAAO;AAAA,IAC/E,MAAM;AAAA,EACR,CAAC;AAAA,EACD,OAAO,EAAE,WAAW,aAAa,KAAK;AAAA;AAGjC,IAAM,4BAAqD;AAAA,EAChE,YAAY,CAAC,SAAS;AAAA,IACpB,OAAO,aAAa,OAAO;AAAA;AAAA,OAEvB,YAAW,CAAC,SAAS;AAAA,IACzB,MAAM,UAAU,aAAa;AAAA,MAC3B,KAAK,QAAQ;AAAA,MACb,SAAS,iBAAiB,QAAQ,QAAQ,QAAQ,SAAS;AAAA,SACvD,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,SAC5C,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACrD,CAAC;AAAA,IACD,OAAO,EAAE,QAAQ,QAAQ,WAAW,aAAa,QAAQ,YAAY;AAAA;AAAA,OAEjE,gBAAe,CAAC,SAAS;AAAA,IAI7B,QAAQ,yBAAyB,MAAa;AAAA,IAC9C,MAAM,WAAsB,CAAC;AAAA,IAC7B,WAAW,aAAa,QAAQ,YAAY;AAAA,MAC1C,IAAI;AAAA,QACF,qBAAqB,SAAS;AAAA,QAC9B,MAAM,YAAY;AAAA,UAChB,mBAAmB,SAAS;AAAA,UAC5B,yBAAyB,SAAS;AAAA,QACpC,EAAE,OAAO,WAAU;AAAA,QACnB,IAAI,UAAU,SAAS,GAAG;AAAA,UACxB,MAAM,IAAI,MAAM,+CAA+C,UAAU,KAAK,IAAI,GAAG;AAAA,QACvF;AAAA,QACA,OAAO,OAAO;AAAA,QACd,OAAO,KAAK,EAAE,KAAK,OAAO,UAAU,GAAG,yCAAyC;AAAA,QAChF,SAAS,KAAK,KAAK;AAAA;AAAA,IAEvB;AAAA,IACA,IAAI,SAAS,SAAS,GAAG;AAAA,MACvB,MAAM,IAAI,eAAe,UAAU,gCAAgC;AAAA,IACrE;AAAA;AAEJ;;;ACnHA,IAAM,aAA+C;AAAA,EACnD,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AACX;AAEO,SAAS,WAAW,CAAC,OAAiC;AAAA,EAC3D,OAAO,WAAW;AAAA;AAGpB,IAAM,aAAyD;AAAA,EAC7D,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AACX;AAEO,SAAS,qBAAqB,CAAC,OAA2C;AAAA,EAC/E,OAAO,WAAW;AAAA;;;ACXb,IAAM,eAA0C,CAAC,UAAU,YAAoB,KAAK;AAEpF,IAAM,yBAA8D,CAAC,UAC1E,sBAA8B,KAAK;",
16
- "debugId": "795560EC3C7C45C764756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "6F48338E5A857EC064756E2164756E21",
17
17
  "names": []
18
18
  }
package/dist/rollout.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  repairPoisonedRollout,
15
15
  writeClaudeRollout,
16
16
  writeCodexRollout
17
- } from "./chunk-5rpw9d1f.js";
17
+ } from "./chunk-kc5kmr95.js";
18
18
 
19
19
  // src/rollout.ts
20
20
  import { fileURLToPath } from "url";
@@ -80,7 +80,7 @@ function writeRollout(options: Parameters<AgentRegistryOperations["writeRollout"
80
80
  version: 1,
81
81
  cwd: options.cwd,
82
82
  createdAt: existingCreatedAt(path) ?? new Date().toISOString(),
83
- sdkVersion: "0.1.53",
83
+ sdkVersion: "0.1.54",
84
84
  },
85
85
  },
86
86
  ...messages,
@@ -10,7 +10,12 @@
10
10
  import { mkdirSync } from "node:fs";
11
11
  import { dirname, join, resolve } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
- import { DM_WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, WORKSPACE_DIR } from "#platform/config";
13
+ import {
14
+ DM_WORKSPACE_DIR,
15
+ SESSION_WORKSPACE_DIR,
16
+ TOPIC_WORKSPACE_DIR,
17
+ WORKSPACE_DIR,
18
+ } from "#platform/config";
14
19
  import { logger } from "#platform/logger";
15
20
  import { renderUserPromptBatch } from "#runtime/user-turn-envelope";
16
21
  import type { ConversationEntry } from "#storage/conversations";
@@ -24,9 +29,12 @@ export interface RolloutHostOptions {
24
29
  fixturesDir?: string;
25
30
  }
26
31
 
27
- let trustedWorkspaceRoots = [WORKSPACE_DIR, DM_WORKSPACE_DIR, TOPIC_WORKSPACE_DIR].map((root) =>
28
- resolve(root),
29
- );
32
+ let trustedWorkspaceRoots = [
33
+ WORKSPACE_DIR,
34
+ DM_WORKSPACE_DIR,
35
+ SESSION_WORKSPACE_DIR,
36
+ TOPIC_WORKSPACE_DIR,
37
+ ].map((root) => resolve(root));
30
38
 
31
39
  /** Configure the embedding host's trusted workspace roots. */
32
40
  export function configureRolloutHost(options: RolloutHostOptions): () => void {
@@ -24,7 +24,7 @@ export async function connectStdio(server: McpServer): Promise<void> {
24
24
  /**
25
25
  * Parse the `--user-id=<N>` CLI flag from argv and return the raw decimal
26
26
  * string. Returns empty string on missing/invalid values so callers can
27
- * retain their existing "userId || default" guards.
27
+ * retain their existing "userId || local" guards.
28
28
  *
29
29
  * Rejects unsafe values so the id stays usable as a storage key / process arg
30
30
  * if spawn args ever come from an untrusted source.
@@ -114,7 +114,7 @@ function parseArgv(): {
114
114
  topicId?: string;
115
115
  surface: WikiSurface;
116
116
  } {
117
- let userId = "default";
117
+ let userId = "local";
118
118
  let topicId: string | undefined;
119
119
  let surface: WikiSurface = "all";
120
120