@3030-labs/wotw 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +312 -0
- package/LICENSE +36 -0
- package/LICENSE-NOTICES.md +199 -0
- package/README.md +147 -0
- package/SECURITY.md +181 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +14993 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/daemon/entry.d.ts +2 -0
- package/dist/daemon/entry.js +11544 -0
- package/dist/daemon/entry.js.map +1 -0
- package/dist/index.d.ts +617 -0
- package/dist/index.js +1290 -0
- package/dist/index.js.map +1 -0
- package/dist/wiki/templates/CLAUDE.md +87 -0
- package/dist/wiki/templates/getting-started.md +89 -0
- package/dist/wiki/templates/index.md +32 -0
- package/dist/wiki/templates/log.md +9 -0
- package/package.json +127 -0
- package/src/wiki/templates/CLAUDE.md +87 -0
- package/src/wiki/templates/getting-started.md +89 -0
- package/src/wiki/templates/index.md +32 -0
- package/src/wiki/templates/log.md +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js","../../src/utils/actionable-error.ts","../../src/utils/errors.ts","../../src/utils/logger.ts","../../src/utils/version.ts","../../src/ingestion/execution-mode.ts","../../src/utils/fs.ts","../../src/daemon/config.ts","../../src/utils/git.ts","../../src/cli/output.ts","../../src/cli/lib/vault-detect.ts","../../src/daemon/lifecycle.ts","../../src/ingestion/cost-tracker.ts","../../src/ingestion/dead-letter.ts","../../src/wiki/page.ts","../../src/wiki/store.ts","../../src/wiki/search.ts","../../src/wiki/provenance-footer.ts","../../src/ingestion/wiki-writer.ts","../../src/wiki/cross-reference.ts","../../src/wiki/health.ts","../../src/server/query-metrics.ts","../../src/provenance/hash.ts","../../src/provenance/chain.ts","../../src/ingestion/model-router.ts","../../src/utils/retry.ts","../../src/ingestion/git-committer.ts","../../src/ingestion/cli-invoker.ts","../../src/ingestion/llm-invoker.ts","../../src/llm/providers/anthropic.ts","../../src/llm/providers/openai.ts","../../src/llm/providers/gemini.ts","../../src/llm/providers/ollama.ts","../../src/llm/runtime-aware.ts","../../src/llm/edits.ts","../../src/wiki/heal-handlers.ts","../../src/cli/commands/lint.ts","../../src/multi-user/token-store.ts","../../src/wiki/index-manager.ts","../../src/facts/store.ts","../../src/facts/index-manager.ts","../../src/facts/extractor.ts","../../src/keys/envelope.ts","../../src/keys/store.ts","../../src/daemon/index.ts","../../src/daemon/lint-scheduler.ts","../../src/daemon/dek-archive-scheduler.ts","../../src/utils/sanitize.ts","../../src/ingestion/prompt-builder.ts","../../src/ingestion/tenant-scheduler.ts","../../src/ingestion/queue.ts","../../src/watcher/debounce.ts","../../src/watcher/event-classifier.ts","../../src/watcher/ignore-patterns.ts","../../src/watcher/index.ts","../../src/server/safe-fetch.ts","../../src/server/query-expansion.ts","../../src/server/query-engine.ts","../../src/server/truncate.ts","../../src/server/token-estimator.ts","../../src/server/progressive-query.ts","../../src/server/cost-estimator.ts","../../src/server/narrow-query.ts","../../src/server/fact-query.ts","../../src/server/tools.ts","../../src/server/resources.ts","../../src/server/middleware.ts","../../src/server/progressive-cache.ts","../../src/server/index.ts","../../src/compounding/engine.ts","../../src/provenance/cloud-sink.ts","../../src/daemon/entry.ts","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/telemetry/index.ts","../../src/telemetry/sink.ts","../../src/telemetry/categorize.ts","../../src/cli/commands/start.ts","../../src/daemon/process-manager.ts","../../src/cli/commands/stop.ts","../../src/cli/commands/status.ts","../../src/cli/commands/query.ts","../../src/cli/commands/lib/mcp-client.ts","../../src/cli/commands/audit.ts","../../src/cli/commands/logs.ts","../../src/cli/commands/install-hook.ts","../../src/cli/commands/uninstall-hook.ts","../../src/cli/commands/serve.ts","../../src/cli/commands/synthesize.ts","../../src/cli/commands/search.ts","../../src/cli/commands/stale.ts","../../src/cli/commands/user.ts","../../src/cli/commands/approve.ts","../../src/cli/commands/reject.ts","../../src/cli/commands/candidates.ts","../../src/cli/commands/facts.ts","../../src/cli/commands/keys.ts","../../src/cli/commands/workspace.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * Actionable error class for the 10 user-facing unhappy paths surfaced\n * by PASS-023 (public-launch readiness). Each instance carries:\n *\n * - a stable `code` for programmatic handling + log indexing\n * - a one-line `summary` for the top of the rendered error\n * - a `cause` chain for diagnostics\n * - a `suggestions` array of concrete next steps the user can take\n *\n * The CLI's top-level error handler renders these as a structured block\n * to stderr; the daemon's internal logger captures the same fields as\n * structured JSON. Stack traces are suppressed unless `WOTW_DEBUG=1`.\n */\nexport type ActionableErrorCode =\n | \"MISSING_VAULT_PATH\"\n | \"CONFIG_PARSE_ERROR\"\n | \"NATIVE_BINDING_LOAD_FAILURE\"\n | \"INVALID_API_KEY\"\n | \"RATE_LIMITED\"\n | \"WIKI_DIR_PERMISSION_DENIED\"\n | \"VAULT_FILE_LOCKED\"\n | \"PORT_IN_USE\"\n | \"DAEMON_ALREADY_RUNNING\"\n | \"INIT_TARGET_NOT_EMPTY\";\n\nexport interface ActionableErrorOptions {\n code: ActionableErrorCode;\n summary: string;\n suggestions: readonly string[];\n cause?: unknown;\n docs?: string;\n}\n\nexport class ActionableError extends Error {\n readonly code: ActionableErrorCode;\n readonly summary: string;\n readonly suggestions: readonly string[];\n readonly docs?: string;\n\n constructor(opts: ActionableErrorOptions) {\n const lines: string[] = [opts.summary];\n if (opts.suggestions.length > 0) {\n lines.push(\"\", \"What to try:\");\n for (const suggestion of opts.suggestions) {\n lines.push(` - ${suggestion}`);\n }\n }\n if (opts.docs) {\n lines.push(\"\", `Docs: ${opts.docs}`);\n }\n super(lines.join(\"\\n\"));\n this.name = \"ActionableError\";\n this.code = opts.code;\n this.summary = opts.summary;\n this.suggestions = opts.suggestions;\n this.docs = opts.docs;\n if (opts.cause !== undefined) {\n (this as { cause?: unknown }).cause = opts.cause;\n }\n }\n}\n\nexport function isActionableError(e: unknown): e is ActionableError {\n return e instanceof ActionableError;\n}\n\n/**\n * Heuristic detection of native-binding load failures. better-sqlite3\n * (and other N-API addons) surface very platform-specific error\n * messages; this matcher captures the common shapes.\n */\nexport function looksLikeNativeBindingFailure(e: unknown): boolean {\n const msg = e instanceof Error ? e.message : String(e);\n return (\n /Cannot find module .*\\.node/i.test(msg) ||\n /could not load the bindings file/i.test(msg) ||\n /better.sqlite3.*\\.node.*was compiled against/i.test(msg) ||\n /NODE_MODULE_VERSION/i.test(msg) ||\n /ERR_DLOPEN_FAILED/i.test(msg) ||\n /libstdc\\+\\+.*GLIBCXX/i.test(msg) ||\n /Symbol not found.*sqlite3/i.test(msg) ||\n /image not found/i.test(msg)\n );\n}\n\n/**\n * Heuristic detection of EACCES / EPERM errors from filesystem APIs.\n */\nexport function looksLikePermissionDenied(e: unknown): boolean {\n const msg = e instanceof Error ? e.message : String(e);\n const code = e instanceof Error ? (e as { code?: string }).code : undefined;\n return code === \"EACCES\" || code === \"EPERM\" || /EACCES|EPERM/i.test(msg);\n}\n\n/**\n * Heuristic detection of file-lock contention errors. Obsidian holds\n * EBUSY/ETXTBSY on macOS, an undocumented lock state on Windows, and\n * EAGAIN on Linux when another process holds an exclusive lock.\n */\nexport function looksLikeFileLock(e: unknown): boolean {\n const code = e instanceof Error ? (e as { code?: string }).code : undefined;\n const msg = e instanceof Error ? e.message : String(e);\n return (\n code === \"EBUSY\" ||\n code === \"ETXTBSY\" ||\n code === \"EAGAIN\" ||\n /EBUSY|ETXTBSY|EAGAIN|file is locked/i.test(msg)\n );\n}\n\n/**\n * Heuristic detection of port-in-use binding failures. listen() rejects\n * with EADDRINUSE on every supported platform.\n */\nexport function looksLikePortInUse(e: unknown): boolean {\n const code = e instanceof Error ? (e as { code?: string }).code : undefined;\n const msg = e instanceof Error ? e.message : String(e);\n return code === \"EADDRINUSE\" || /EADDRINUSE/i.test(msg);\n}\n\n/**\n * Build an ActionableError for the OBSIDIAN_VAULT_PATH-missing path\n * (item 1 of the PASS-023 error audit).\n */\nexport function missingVaultPathError(): ActionableError {\n return new ActionableError({\n code: \"MISSING_VAULT_PATH\",\n summary: \"No Obsidian vault path was provided and none could be auto-detected.\",\n suggestions: [\n \"Run `wotw init` and pick a vault interactively.\",\n \"Pass `--path /path/to/vault` to point at a specific directory.\",\n \"Set OBSIDIAN_VAULT_PATH in your shell environment to a vault root.\",\n \"Install Obsidian and open at least one vault to register a default.\",\n ],\n docs: \"docs/init-walkthrough.md\",\n });\n}\n\n/**\n * Build an ActionableError for the malformed-config path (item 2).\n */\nexport function configParseError(configPath: string, cause: unknown): ActionableError {\n return new ActionableError({\n code: \"CONFIG_PARSE_ERROR\",\n summary: `Could not parse wotw config at ${configPath}: ${cause instanceof Error ? cause.message : String(cause)}`,\n suggestions: [\n 'Validate the file with `node -e \\'console.log(JSON.parse(require(\"fs\").readFileSync(process.argv[1], \"utf8\")))\\' ' +\n configPath +\n \"` (for JSON) or `yamllint \" +\n configPath +\n \"` (for YAML).\",\n \"Check for unmatched braces, missing colons, or trailing commas.\",\n \"Compare against the example at docs/configuration.md.\",\n \"Delete the file and re-run `wotw init` to regenerate a fresh config.\",\n ],\n docs: \"docs/configuration.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the better-sqlite3 native-binding-load\n * path (item 3).\n */\nexport function nativeBindingLoadError(module: string, cause: unknown): ActionableError {\n return new ActionableError({\n code: \"NATIVE_BINDING_LOAD_FAILURE\",\n summary: `Could not load the native binding for ${module} on this platform.`,\n suggestions: [\n \"Run `pnpm rebuild \" +\n module +\n \"` (or `npm rebuild \" +\n module +\n \"`) in the install directory.\",\n \"Verify Node.js >= 20 is installed (`node --version`).\",\n \"Confirm your platform matches one of: macOS arm64, macOS amd64, Linux amd64, Windows amd64.\",\n \"If running under Docker, ensure the image was built for the runtime arch (not host arch).\",\n \"Reinstall: `npm uninstall -g @3030-labs/wotw && npm install -g @3030-labs/wotw`.\",\n ],\n docs: \"docs/install-evidence/\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the invalid-API-key path (item 4).\n */\nexport function invalidApiKeyError(\n provider: \"anthropic\" | \"openai\" | \"gemini\" | \"other\",\n envVar: string,\n cause?: unknown,\n): ActionableError {\n return new ActionableError({\n code: \"INVALID_API_KEY\",\n summary: `Provider ${provider} returned 401 Unauthorized — the API key in ${envVar} is invalid or revoked.`,\n suggestions: [\n `Verify the key works: \\`curl -H \"x-api-key: $${envVar}\" https://api.${provider === \"anthropic\" ? \"anthropic.com\" : provider + \".com\"}/v1/models\\` (provider URL may vary).`,\n \"Regenerate the key at the provider's console if it's revoked.\",\n `Re-export the new key (\\`export ${envVar}=\"...\"\\`) and restart the daemon (\\`wotw stop && wotw start\\`).`,\n \"Daemon does NOT pick up env-var changes while running — restart is required.\",\n ],\n docs: \"docs/self-hosted-byok.md\",\n cause,\n });\n}\n\n/**\n * Detect a Claude Code CLI authentication failure in the CLI's output.\n * In CLI (subscription) runtime, a 401 surfaces in the agent's stdout as\n * an \"API Error: 401 ... authentication_error ... Please run /login\"\n * string rather than as an HTTP status the daemon can branch on\n * (PASS-023 dogfood finding #21).\n */\nexport function looksLikeCliAuthFailure(text: string): boolean {\n return (\n /API Error:\\s*401/i.test(text) ||\n /authentication_error/i.test(text) ||\n /invalid authentication credentials/i.test(text) ||\n /please run \\/login/i.test(text) ||\n /not (logged in|authenticated)/i.test(text)\n );\n}\n\n/**\n * Build an ActionableError for a Claude Code CLI auth failure (item 4,\n * CLI-runtime variant). Distinct remediation from the env-var API-key\n * path: the fix is `claude /login`, not rotating an env var.\n */\nexport function cliAuthError(cause?: unknown): ActionableError {\n return new ActionableError({\n code: \"INVALID_API_KEY\",\n summary:\n \"The Claude Code CLI is not authenticated (got 401 from the model API). \" +\n \"wotw is in CLI runtime mode and the `claude` binary has no valid session.\",\n suggestions: [\n \"Run `claude` to open the interactive shell, then type `/login` and complete the browser auth flow.\",\n 'Verify auth worked: `echo \"hi\" | claude -p` should print a reply, not a 401.',\n \"Then restart the daemon: `wotw stop && wotw start`.\",\n \"If you intended to use an API key instead of the subscription CLI, set `execution.mode: api` + `ANTHROPIC_API_KEY` in your config — see docs/self-hosted-byok.md.\",\n ],\n docs: \"docs/self-hosted-byok.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the rate-limited path (item 5).\n */\nexport function rateLimitedError(\n provider: \"anthropic\" | \"openai\" | \"gemini\" | \"other\",\n retryAfterSec?: number,\n cause?: unknown,\n): ActionableError {\n return new ActionableError({\n code: \"RATE_LIMITED\",\n summary: `Provider ${provider} returned 429 — rate limit exceeded${\n retryAfterSec !== undefined ? ` (retry after ${retryAfterSec}s)` : \"\"\n }.`,\n suggestions: [\n \"Lower `ingestion.concurrency` in wotw.config.yaml to spread requests further.\",\n `Wait ${retryAfterSec ?? \"the indicated\"} seconds and retry.`,\n \"Upgrade your provider tier if 429s are sustained over multiple ingest cycles.\",\n \"Check `wotw status` for dead-letter queue growth; clear with `wotw dlq retry` once the window recovers.\",\n ],\n docs: \"docs/self-hosted-byok.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the wiki-dir EACCES path (item 6).\n */\nexport function wikiDirPermissionError(path: string, cause: unknown): ActionableError {\n return new ActionableError({\n code: \"WIKI_DIR_PERMISSION_DENIED\",\n summary: `Cannot create or write to wiki directory at ${path}: permission denied.`,\n suggestions: [\n `Check the directory's owner and permissions: \\`ls -la \"${path}\"\\`.`,\n `Ensure the daemon's user can write: \\`chmod u+w \"${path}\"\\` (or relocate to a writable parent).`,\n \"If running under Docker, confirm the volume mount has the right ownership.\",\n \"Choose a different `wiki_root` in wotw.config.yaml.\",\n ],\n docs: \"docs/configuration.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the locked-vault-file path (item 7).\n */\nexport function vaultFileLockedError(path: string, cause: unknown): ActionableError {\n return new ActionableError({\n code: \"VAULT_FILE_LOCKED\",\n summary: `Cannot write to ${path}: another process is holding an exclusive lock (usually Obsidian).`,\n suggestions: [\n \"Close the file in Obsidian (or close Obsidian entirely) and retry the operation.\",\n \"On Windows, check for indexing services or backup tools that may briefly lock files.\",\n \"Run `wotw status` to confirm the lock cleared after closing the holder.\",\n \"If the lock persists with no holder visible, restart your machine.\",\n ],\n docs: \"docs/obsidian-setup.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the port-in-use path (item 8).\n */\nexport function portInUseError(port: number, cause?: unknown): ActionableError {\n return new ActionableError({\n code: \"PORT_IN_USE\",\n summary: `Port ${port} is already in use; the MCP server cannot bind.`,\n suggestions: [\n `Find the process holding the port: \\`lsof -iTCP:${port} -sTCP:LISTEN\\` (macOS/Linux) or \\`netstat -ano | findstr :${port}\\` (Windows).`,\n \"Stop that process, or change `server.port` in wotw.config.yaml to a free port.\",\n `If it's an orphaned wotw daemon, \\`wotw stop\\` (or kill the PID) and retry.`,\n ],\n docs: \"docs/configuration.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the daemon-already-running path (item 9).\n */\nexport function daemonAlreadyRunningError(lockPath: string, cause?: unknown): ActionableError {\n return new ActionableError({\n code: \"DAEMON_ALREADY_RUNNING\",\n summary: `Another wotw daemon already holds the lock at ${lockPath}.`,\n suggestions: [\n \"Run `wotw status` to see the live daemon's PID and health.\",\n \"If you intend to replace it, `wotw stop` first, then `wotw start`.\",\n \"If the lock is stale (e.g. after a crash), `wotw stop --force` clears it.\",\n ],\n docs: \"docs/cli-reference.md\",\n cause,\n });\n}\n\n/**\n * Build an ActionableError for the non-empty-init-target path (item 10).\n */\nexport function initTargetNotEmptyError(\n path: string,\n conflictingEntries: readonly string[],\n): ActionableError {\n return new ActionableError({\n code: \"INIT_TARGET_NOT_EMPTY\",\n summary: `Cannot scaffold a new vault at ${path}: target directory is not empty.`,\n suggestions: [\n `Conflicting entries: ${conflictingEntries.slice(0, 5).join(\", \")}${conflictingEntries.length > 5 ? ` … (+${conflictingEntries.length - 5} more)` : \"\"}.`,\n \"Pick a different `--path` (or an empty subdirectory of this one).\",\n \"If you meant to overlay onto an existing vault, this is the wrong code path — the wizard would have offered an overlay prompt. Confirm the target has `.obsidian/` and re-run.\",\n \"If you're sure the existing files are safe to overwrite, re-run with `--force`.\",\n ],\n docs: \"docs/init-walkthrough.md\",\n });\n}\n","/**\n * Safe error message extraction. Replaces unsafe `(err as Error).message` casts.\n */\nexport function errMsg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","/**\n * Centralized pino logger factory. All modules should import `getLogger()` rather\n * than constructing their own logger so we have one configuration surface.\n */\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport pino, { type Logger, type LoggerOptions } from \"pino\";\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\";\n\nlet rootLogger: Logger | null = null;\n\n/**\n * Initialize the root logger. Idempotent — calling again replaces the existing logger.\n *\n * @param level minimum log level to emit\n * @param logFile optional path to a log file. If provided, JSON lines are written there.\n * If omitted, logs go to stdout in pretty format for humans.\n */\n/**\n * Pino redact paths — review item 1 closure.\n *\n * Pino's `redact.paths` walks the structured log object before serialization\n * and replaces matching values with `[Redacted]`. We list the common shapes\n * the daemon's log sites produce when error / response objects flow\n * through: headers.authorization, ANTHROPIC_API_KEY-shaped env values,\n * SDK error payloads with embedded keys, etc.\n *\n * This is BELT to the SUSPENDERS of src/utils/sanitize.ts's string-level\n * regex redaction. Sanitize covers free-form text in prompts; redact\n * covers structured fields the call site might have forgotten to pass\n * through sanitize.\n */\nconst REDACT_PATHS = [\n \"headers.authorization\",\n \"headers.Authorization\",\n \"*.headers.authorization\",\n \"*.headers.Authorization\",\n \"req.headers.authorization\",\n \"req.headers.Authorization\",\n \"request.headers.authorization\",\n \"request.headers.Authorization\",\n \"response.headers.authorization\",\n \"response.headers.Authorization\",\n \"headers['x-admin-key']\",\n \"headers['x-api-key']\",\n \"headers.cookie\",\n \"*.headers.cookie\",\n \"config.api_key\",\n \"config.headers.authorization\",\n \"err.config.headers.authorization\",\n \"err.request.headers.authorization\",\n \"err.response.headers.authorization\",\n // Provider SDK error shapes — observed leaking via err.headers.* on Axios-based clients.\n \"err.headers.authorization\",\n \"err.headers.Authorization\",\n // Env-bag dumps (occasionally added by ad-hoc debug logs).\n \"env.ANTHROPIC_API_KEY\",\n \"env.OPENAI_API_KEY\",\n \"env.GOOGLE_API_KEY\",\n \"env.ADMIN_SERVICE_KEY\",\n \"env.WOTW_MCP_BEARER\",\n \"env.WOTW_INTERNAL_ADMIN_KEY\",\n \"env.WOTW_CLOUD_SINK_SECRET\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"GOOGLE_API_KEY\",\n \"ADMIN_SERVICE_KEY\",\n \"WOTW_MCP_BEARER\",\n \"WOTW_INTERNAL_ADMIN_KEY\",\n \"WOTW_CLOUD_SINK_SECRET\",\n \"apiKey\",\n \"api_key\",\n \"*.apiKey\",\n \"*.api_key\",\n \"secret\",\n \"*.secret\",\n];\n\n/**\n * Review item 6: Pino's default err serializer dumps arbitrary error\n * properties via `pino.stdSerializers.err`. Empirically verified that\n * SDK errors carry `err.headers.authorization` / `err.response.headers.*`\n * verbatim. The redact-paths layer (item 1) catches the common shapes,\n * but a custom serializer makes the safety guarantee load-bearing:\n * only the small allowlist of fields below is ever emitted; everything\n * else is dropped, regardless of where it sits on the error object.\n */\nfunction safeErrSerializer(err: unknown): Record<string, unknown> {\n if (!(err instanceof Error)) {\n return { message: String(err) };\n }\n const out: Record<string, unknown> = {\n type: err.constructor.name,\n message: err.message,\n };\n if (err.stack) out.stack = err.stack;\n // Some custom errors (SafeFetchError, OrchestratorError) carry a\n // `code` field that's safe to surface — it's an enum, never user\n // content.\n const maybeCode = (err as unknown as { code?: unknown }).code;\n if (typeof maybeCode === \"string\") out.code = maybeCode;\n // `cause` is usually another Error — recurse one level (cap depth\n // to avoid stack overflow on circular chains).\n const maybeCause = (err as unknown as { cause?: unknown }).cause;\n if (maybeCause && typeof maybeCause === \"object\" && \"message\" in maybeCause) {\n out.cause = {\n type: (maybeCause as { constructor?: { name: string } }).constructor?.name ?? \"Error\",\n message: (maybeCause as { message: unknown }).message,\n };\n }\n // INTENTIONALLY DROPPED (item 6 fix surface):\n // err.headers — Axios-shape; carries authorization\n // err.config — Axios-shape; carries authorization header\n // err.request — Axios-shape; carries headers\n // err.response — Axios-shape; carries headers + body\n // any other property — unknown SDK shapes default to \"drop\"\n return out;\n}\n\nexport function initLogger(level: LogLevel = \"info\", logFile?: string): Logger {\n const options: LoggerOptions = {\n level,\n base: { pid: process.pid, hostname: undefined },\n timestamp: pino.stdTimeFunctions.isoTime,\n redact: {\n paths: REDACT_PATHS,\n censor: \"[Redacted]\",\n remove: false,\n },\n serializers: {\n err: safeErrSerializer,\n error: safeErrSerializer,\n },\n };\n\n if (logFile) {\n mkdirSync(dirname(logFile), { recursive: true });\n rootLogger = pino(options, pino.destination({ dest: logFile, sync: false, mkdir: true }));\n } else {\n rootLogger = pino({\n ...options,\n transport: {\n target: \"pino-pretty\",\n options: { colorize: true, translateTime: \"HH:MM:ss.l\", ignore: \"pid,hostname\" },\n },\n });\n }\n return rootLogger;\n}\n\n/** Default context fields merged into every child logger (e.g. tenantId in hosted mode). */\nlet defaultContext: Record<string, unknown> = {};\n\n/**\n * Set default context fields that are merged into every child logger.\n * Call once at startup when hosted.enabled is true.\n */\nexport function setLoggerContext(ctx: Record<string, unknown>): void {\n defaultContext = ctx;\n}\n\n/**\n * Return the root logger, initializing with defaults if none exists.\n * When defaultContext has been set (hosted mode), every child logger\n * automatically includes those fields.\n */\nexport function getLogger(module?: string, extra?: Record<string, unknown>): Logger {\n if (!rootLogger) {\n rootLogger = initLogger(\"info\");\n }\n const ctx = { ...defaultContext, ...extra, ...(module ? { module } : {}) };\n return Object.keys(ctx).length > 0 ? rootLogger.child(ctx) : rootLogger;\n}\n","/**\n * Single source of truth for the package version.\n *\n * Uses `createRequire` to read `package.json` at runtime so the version\n * can never drift from the declared value. This is safe in ESM because\n * `createRequire` resolves relative to the compiled output directory and\n * `package.json` sits at the repo root (two levels up from `dist/utils/`).\n */\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../../package.json\") as { version: string };\n\nexport const VERSION: string = pkg.version;\n","/**\n * Execution mode detection. Resolves the configured {@link ExecutionMode}\n * (\"auto\" | \"cli\" | \"api\") into a concrete {@link RuntimeMode} (\"cli\" | \"api\")\n * at daemon startup.\n *\n * Rules:\n * - mode=auto: prefer the `claude` CLI binary on PATH. If absent, fall back\n * to the Agent SDK iff `ANTHROPIC_API_KEY` (or the configured env var) is\n * set. If neither is available, refuse to start.\n * - mode=cli: require the CLI binary on PATH; refuse to start if missing.\n * - mode=api: require the API key env var to be set; refuse to start if\n * missing.\n *\n * The detection is synchronous and cheap: one `which`/`where` invocation and\n * one env lookup. It must be called exactly once, during daemon init, so the\n * resolved mode can be logged prominently.\n */\nimport { spawnSync } from \"node:child_process\";\nimport { platform } from \"node:os\";\nimport type { ExecutionMode, RuntimeMode, WotwConfig } from \"../utils/types.js\";\n\n/** Summary of a successful resolution. */\nexport interface ResolvedExecutionMode {\n /** Concrete runtime to use. */\n mode: RuntimeMode;\n /** Configured mode that produced this result. */\n configuredMode: ExecutionMode;\n /** Absolute path to the `claude` binary, if CLI mode was resolved. */\n cliPath: string | null;\n /** Name of the env var that supplied the API key, if API mode was resolved. */\n apiKeyEnv: string | null;\n /** Model identifier that will be used (cli_model in CLI mode, router in API mode). */\n effectiveModelHint: string;\n /** One-line human-readable summary, suitable for logging or CLI output. */\n description: string;\n}\n\n/**\n * Error thrown when the daemon cannot resolve a usable runtime mode.\n * Intentionally a subclass of Error with a `.code` field so callers can\n * surface a clean exit.\n */\nexport class ExecutionModeError extends Error {\n readonly code: string;\n constructor(message: string, code: string) {\n super(message);\n this.name = \"ExecutionModeError\";\n this.code = code;\n }\n}\n\n/**\n * Look up the absolute path of a binary on PATH. Returns null if not found.\n * Uses `which` on Unix and `where` on Windows. Safe to call on WSL — the\n * Linux `which` is used there since WSL reports `platform() === 'linux'`.\n */\nexport function findOnPath(binary: string): string | null {\n // `command -v` would be slightly more portable on Unix, but it is a shell\n // builtin and spawnSync won't invoke a shell. `which` is universally\n // available on macOS, Linux, and WSL; `where` is the Windows equivalent.\n const prog = platform() === \"win32\" ? \"where\" : \"which\";\n try {\n const result = spawnSync(prog, [binary], { encoding: \"utf8\" });\n if (result.status !== 0) return null;\n const stdout = result.stdout.trim();\n if (!stdout) return null;\n // `where` may list multiple matches; take the first line.\n const first = stdout.split(/\\r?\\n/)[0]?.trim();\n return first && first.length > 0 ? first : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Inspect the environment for an API key under the configured env var name.\n * Returns the var name if set (non-empty), null otherwise.\n */\nexport function findApiKey(envVarName: string): string | null {\n const value = process.env[envVarName];\n if (value && value.trim().length > 0) return envVarName;\n return null;\n}\n\n/**\n * Resolve {@link WotwConfig.execution} into a concrete {@link RuntimeMode}.\n * Throws {@link ExecutionModeError} with a precise reason if neither runtime\n * is available.\n */\nexport function resolveExecutionMode(config: WotwConfig): ResolvedExecutionMode {\n const { mode, cli_path: cliPath, cli_model: cliModel, api_key_env: apiKeyEnv } = config.execution;\n\n const detectCli = (): string | null => findOnPath(cliPath);\n const detectKey = (): string | null => findApiKey(apiKeyEnv);\n\n if (mode === \"cli\") {\n const path = detectCli();\n if (!path) {\n throw new ExecutionModeError(\n `execution.mode is 'cli' but the '${cliPath}' binary was not found on PATH. ` +\n \"Install Claude Code CLI (https://docs.claude.com/claude-code) or set execution.mode to 'api'.\",\n \"CLI_BINARY_NOT_FOUND\",\n );\n }\n return {\n mode: \"cli\",\n configuredMode: \"cli\",\n cliPath: path,\n apiKeyEnv: null,\n effectiveModelHint: cliModel,\n description: `CLI mode: using claude binary at ${path}, model ${cliModel}, zero marginal cost (subscription-covered)`,\n };\n }\n\n if (mode === \"api\") {\n const keyEnv = detectKey();\n if (!keyEnv) {\n throw new ExecutionModeError(\n `execution.mode is 'api' but ${apiKeyEnv} is not set. ` +\n \"Set the env var or change execution.mode to 'cli'/'auto'.\",\n \"API_KEY_NOT_SET\",\n );\n }\n return {\n mode: \"api\",\n configuredMode: \"api\",\n cliPath: null,\n apiKeyEnv: keyEnv,\n effectiveModelHint: `model-router (ingest=${config.models.ingest}, query=${config.models.query})`,\n description: `API mode: using Agent SDK with ${keyEnv}, model routing enabled (per-token billing)`,\n };\n }\n\n // mode === \"auto\"\n // Review item 62: when WOTW_LLM_PROVIDER (config.llm.provider) is\n // set to anything other than \"anthropic\", we MUST NOT silently auto-\n // detect into CLI mode. Pre-fix, an OpenAI / Gemini / Ollama tenant\n // with the claude binary on PATH (always true in the container)\n // would silently dispatch through CLI mode — billing the wrong key\n // for the wrong provider. Honor the explicit llm.provider.\n if (config.llm.provider !== \"anthropic\") {\n const keyEnv2 = detectKey();\n if (!keyEnv2 && config.llm.provider !== \"ollama\") {\n throw new ExecutionModeError(\n `auto-detect: llm.provider='${config.llm.provider}' but ${apiKeyEnv} is not set. ` +\n \"Refusing to fall back to CLI mode for non-anthropic provider.\",\n \"API_KEY_NOT_SET\",\n );\n }\n return {\n mode: \"api\",\n configuredMode: \"auto\",\n cliPath: null,\n apiKeyEnv: keyEnv2,\n effectiveModelHint: `model-router (ingest=${config.models.ingest}, query=${config.models.query})`,\n description: `API mode (auto-detected, provider=${config.llm.provider}): using ${keyEnv2 ?? \"no-key\"}, model routing enabled`,\n };\n }\n const cli = detectCli();\n if (cli) {\n return {\n mode: \"cli\",\n configuredMode: \"auto\",\n cliPath: cli,\n apiKeyEnv: null,\n effectiveModelHint: cliModel,\n description: `CLI mode (auto-detected): using claude binary at ${cli}, model ${cliModel}, zero marginal cost (subscription-covered)`,\n };\n }\n const keyEnv = detectKey();\n if (keyEnv) {\n return {\n mode: \"api\",\n configuredMode: \"auto\",\n cliPath: null,\n apiKeyEnv: keyEnv,\n effectiveModelHint: `model-router (ingest=${config.models.ingest}, query=${config.models.query})`,\n description: `API mode (auto-detected): using Agent SDK with ${keyEnv}, model routing enabled (per-token billing)`,\n };\n }\n throw new ExecutionModeError(\n `No '${cliPath}' binary on PATH and no ${apiKeyEnv} env var set. ` +\n \"Install Claude Code CLI (https://docs.claude.com/claude-code) or set an API key to run watcher-on-the-wall.\",\n \"NO_RUNTIME_AVAILABLE\",\n );\n}\n","/**\n * File system utilities: atomic writes, directory scaffolding, path resolution.\n */\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\n/**\n * Expand a leading `~` to the current user's home directory.\n */\nexport function expandHome(path: string): string {\n if (path.startsWith(\"~\")) {\n return resolve(homedir(), path.slice(path.startsWith(\"~/\") ? 2 : 1));\n }\n return path;\n}\n\n/**\n * Resolve a path against an optional base directory, expanding `~`.\n */\nexport function resolvePath(path: string, base?: string): string {\n const expanded = expandHome(path);\n if (isAbsolute(expanded)) return expanded;\n return resolve(base ?? process.cwd(), expanded);\n}\n\n/**\n * Ensure the directory at `dir` exists, creating it and all parents if needed.\n */\nexport function ensureDirSync(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\n/**\n * Async version of {@link ensureDirSync}.\n */\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\n/**\n * Atomic write: write to a temp file in the same directory and rename.\n * This guarantees readers never see a partial file on POSIX systems.\n */\nexport function atomicWriteSync(filePath: string, contents: string | Buffer): void {\n ensureDirSync(dirname(filePath));\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n let renamed = false;\n try {\n writeFileSync(tmp, contents);\n renameSync(tmp, filePath);\n renamed = true;\n } finally {\n if (!renamed) {\n try {\n rmSync(tmp, { force: true });\n } catch {\n /* best effort */\n }\n }\n }\n}\n\n/**\n * Async version of {@link atomicWriteSync}.\n */\nexport async function atomicWrite(filePath: string, contents: string | Buffer): Promise<void> {\n await ensureDir(dirname(filePath));\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n let renamed = false;\n try {\n await writeFile(tmp, contents);\n await rename(tmp, filePath);\n renamed = true;\n } finally {\n if (!renamed) {\n try {\n rmSync(tmp, { force: true });\n } catch {\n /* best effort */\n }\n }\n }\n}\n\n/**\n * Read a UTF-8 file, returning null if it does not exist.\n */\nexport function readTextOrNull(filePath: string): string | null {\n try {\n return readFileSync(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n\n/**\n * Async version of {@link readTextOrNull}.\n */\nexport async function readTextOrNullAsync(filePath: string): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n\n/**\n * Delete a file if it exists, otherwise no-op.\n */\nexport function removeIfExistsSync(filePath: string): void {\n if (existsSync(filePath)) {\n rmSync(filePath, { force: true });\n }\n}\n\n/**\n * Check if a path points to an existing file.\n */\nexport function fileExists(filePath: string): boolean {\n try {\n return statSync(filePath).isFile();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n\n/**\n * Check if a path points to an existing directory.\n */\nexport function dirExists(dirPath: string): boolean {\n try {\n return statSync(dirPath).isDirectory();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n","/**\n * Configuration loader. Uses cosmiconfig to discover a user config file, validates\n * it, and merges it with sensible defaults into a {@link WotwConfig}.\n */\nimport { cosmiconfig, type CosmiconfigResult } from \"cosmiconfig\";\nimport { parse as parseYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { configParseError } from \"../utils/actionable-error.js\";\nimport { resolvePath } from \"../utils/fs.js\";\nimport type { WotwConfig } from \"../utils/types.js\";\n\nconst MODULE_NAME = \"wotw\";\n\n/** Default plan limits. Active only when `hosted.enabled: true`. */\nexport const PLAN_DEFAULTS = {\n founding: {\n storage_bytes: 2 * 1024 ** 3, // 2 GB\n max_files_per_day: 50,\n max_file_size_bytes: 25 * 1024 ** 2, // 25 MB\n max_ingest_bytes_per_day: 250 * 1024 ** 2, // 250 MB\n heal_cooldown_seconds: 3600, // 1 hour\n query_rate_limit_per_hour: 60,\n },\n pro: {\n storage_bytes: 10 * 1024 ** 3, // 10 GB\n max_files_per_day: 200,\n max_file_size_bytes: 100 * 1024 ** 2, // 100 MB\n max_ingest_bytes_per_day: 1024 ** 3, // 1 GB\n heal_cooldown_seconds: 900, // 15 min\n query_rate_limit_per_hour: 300,\n },\n} as const;\n\n/**\n * Default configuration applied when no file is found (or when fields are missing).\n */\nexport function defaultConfig(): WotwConfig {\n return {\n wiki_root: \"./wiki-store\",\n raw_path: \"./wiki-store/raw\",\n llm: {\n provider: \"anthropic\",\n model: \"claude-sonnet-4-5\",\n },\n execution: {\n mode: \"auto\",\n cli_path: \"claude\",\n cli_model: \"claude-sonnet-4-5\",\n api_key_env: \"ANTHROPIC_API_KEY\",\n },\n models: {\n ingest: \"claude-haiku-4-5\",\n query: \"claude-sonnet-4-5\",\n lint: \"claude-sonnet-4-5\",\n compound_eval: \"claude-haiku-4-5\",\n },\n watcher: {\n debounce_initial_ms: 5000,\n debounce_max_ms: 60000,\n debounce_growth_factor: 1.5,\n burst_threshold: 5,\n max_batch_size: 20,\n ignore_patterns: [\"**/.git/**\", \"**/node_modules/**\", \"**/.DS_Store\", \"**/Thumbs.db\"],\n },\n ingestion: {\n max_turns: 50,\n max_budget_per_batch_usd: 1.0,\n resume_session: true,\n dead_letter_file: \".wotw/failed-batches.jsonl\",\n staging: true,\n },\n cost: {\n max_daily_usd: 10.0,\n max_per_query_usd: 0.5,\n max_per_ingest_usd: 2.0,\n track_file: \"~/.wotw/cost-log.jsonl\",\n },\n server: {\n port: 8787,\n host: \"127.0.0.1\",\n auth_token: null,\n rate_limit_rpm: 60,\n trust_proxy: false,\n },\n daemon: {\n pid_file: \"~/.wotw/daemon.pid\",\n lock_file: \"~/.wotw/daemon.lock\",\n log_file: \"~/.wotw/daemon.log\",\n log_level: \"info\",\n },\n compounding: {\n enabled: true,\n min_source_pages: 3,\n confidence_threshold: 70,\n },\n provenance: {\n enabled: true,\n chain_file: \"provenance-chain.jsonl\",\n // Review item 37: default ON so partial-corruption is detected at\n // boot. Pre-fix default false let a chain with one bad line boot\n // silently and advance on top of a verifiably-broken tail forever.\n verify_on_startup: true,\n },\n multi_user: {\n enabled: false,\n workspaces_dir: \"~/.wotw/workspaces\",\n },\n query: {\n expand: true,\n },\n fact_extraction: {\n enabled: \"auto\",\n force_enabled: false,\n questions_per_fact: 3,\n },\n lint: {\n schedule_enabled: false,\n interval_hours: 24,\n auto_fix: false,\n },\n health: {\n staleness_thresholds: [7, 30, 90, 180, 365],\n staleness_scores: [100, 80, 60, 40, 20, 0],\n weights: {\n staleness: 0.25,\n source_availability: 0.25,\n link_health: 0.2,\n duplicate_risk: 0.15,\n contradiction_risk: 0.15,\n },\n duplicate_threshold: 60,\n auto_fix_staleness_below: 40,\n max_fixes_per_run: 10,\n detect_contradictions: false,\n consolidation_threshold: 5,\n consolidation_enabled: true,\n zero_hit_threshold: 0.2,\n enrichment_enabled: true,\n query_log_file: \".wotw/query-log.jsonl\",\n },\n hosted: {\n enabled: false,\n tenant_id: null,\n concurrency_cap: 1,\n paused: false,\n plan: \"pro\",\n limits: {\n storage_bytes: PLAN_DEFAULTS.pro.storage_bytes,\n max_files_per_day: PLAN_DEFAULTS.pro.max_files_per_day,\n max_file_size_bytes: PLAN_DEFAULTS.pro.max_file_size_bytes,\n max_ingest_bytes_per_day: PLAN_DEFAULTS.pro.max_ingest_bytes_per_day,\n heal_cooldown_seconds: PLAN_DEFAULTS.pro.heal_cooldown_seconds,\n query_rate_limit_per_hour: PLAN_DEFAULTS.pro.query_rate_limit_per_hour,\n onboarding_burst_multiplier: 3,\n onboarding_burst_hours: 48,\n },\n timezone: \"America/New_York\",\n created_at: null,\n },\n };\n}\n\n/** Result of loading config: resolved value and origin path (if any). */\nexport interface LoadConfigResult {\n config: WotwConfig;\n path: string | null;\n}\n\n/**\n * Load configuration from cosmiconfig's discovery of `wotw.config.*`, `.wotwrc`, or\n * a `wotw` key in package.json. If no file is found, the default config is returned.\n *\n * Resolution order (highest to lowest priority):\n * 1. Environment variables (see {@link applyEnvOverrides})\n * 2. User config file\n * 3. Defaults\n *\n * In hosted mode (`WOTW_HOSTED=true` or `hosted.enabled` in the file), the\n * resulting config is additionally checked by {@link validateHostedConfig}\n * which throws when `tenant_id` is missing, malformed, or `wiki_root` is\n * unset.\n *\n * @param searchFrom optional directory to search from (defaults to process.cwd())\n */\nexport async function loadConfig(searchFrom?: string): Promise<LoadConfigResult> {\n const explorer = cosmiconfig(MODULE_NAME, {\n searchPlaces: [\n \"package.json\",\n `.${MODULE_NAME}rc`,\n `.${MODULE_NAME}rc.json`,\n `.${MODULE_NAME}rc.yaml`,\n `.${MODULE_NAME}rc.yml`,\n // `wotw.yaml` / `wotw.yml` are what the `wotw init` wizard writes\n // (see src/cli/commands/init.ts:CONFIG_CANDIDATES). They must be in\n // searchPlaces or every fresh-init vault runs with all-defaults.\n // Finding #12 from PASS-023 dogfood pass.\n `${MODULE_NAME}.yaml`,\n `${MODULE_NAME}.yml`,\n `${MODULE_NAME}.config.json`,\n `${MODULE_NAME}.config.yaml`,\n `${MODULE_NAME}.config.yml`,\n ],\n loaders: {\n \".yaml\": (_filepath, content) => parseYaml(content) as unknown,\n \".yml\": (_filepath, content) => parseYaml(content) as unknown,\n },\n });\n\n let result: CosmiconfigResult;\n try {\n result = await explorer.search(searchFrom ?? process.cwd());\n } catch (err) {\n // cosmiconfig parser errors (malformed YAML/JSON in a discovered config\n // file) surface here. Wrap as ActionableError so the CLI handler\n // renders the path + next-step suggestions instead of a stack trace.\n const hintPath =\n err instanceof Error && (err as { filepath?: string }).filepath\n ? ((err as { filepath?: string }).filepath as string)\n : (searchFrom ?? process.cwd());\n throw configParseError(hintPath, err);\n }\n const defaults = defaultConfig();\n let merged: WotwConfig;\n let path: string | null = null;\n if (!result || !result.config) {\n // eslint-disable-next-line no-console -- runs before pino logger is initialized\n console.warn(\n \"[wotw] no wotw.yaml found — using all defaults (auth disabled, max_daily_usd: 10.0)\",\n );\n merged = defaults;\n } else {\n merged = mergeConfig(defaults, result.config as Partial<WotwConfig>);\n path = result.filepath;\n }\n // Env overrides take precedence over the file. Applied here so the\n // hosted-mode validation below sees the final, fully-resolved values.\n const withEnv = applyEnvOverrides(merged);\n const validated = validateConfig(withEnv);\n validateHostedConfig(validated);\n return { config: validated, path };\n}\n\n/**\n * Apply runtime environment-variable overrides to a parsed config. Returns\n * a new object; the input is not mutated. Each override is read from the\n * corresponding env var and only set if the value is non-empty.\n *\n * Variables consumed (each optional):\n * WOTW_HOSTED \"true\" / \"false\" — `hosted.enabled`\n * TENANT_ID UUID — `hosted.tenant_id` (validated downstream)\n * WIKI_ROOT absolute path — `wiki_root`\n * WOTW_PLAN \"founding\" | \"pro\" — `hosted.plan`\n * WOTW_TIMEZONE IANA tz — `hosted.timezone`\n * WOTW_PORT integer — `server.port`\n * WOTW_HOST host string — `server.host`\n * WOTW_LOG_LEVEL pino level — `daemon.log_level`\n * WOTW_RUNTIME_MODE \"auto\" | \"cli\" | \"api\" — `execution.mode`\n * ADMIN_SERVICE_KEY bearer token — `server.auth_token`\n */\nexport function applyEnvOverrides(config: WotwConfig): WotwConfig {\n const out = structuredClone(config);\n const env = process.env;\n\n if (env.WOTW_HOSTED !== undefined) {\n // Review item 61: accept the conventional truthy spellings rather\n // than only \"true\". Pre-fix, \"1\" / \"True\" / \"yes\" / \"on\" all\n // silently set hosted.enabled = false; container operators expect\n // any of these to enable hosted-mode defaults.\n const v = env.WOTW_HOSTED.trim().toLowerCase();\n out.hosted.enabled = v === \"true\" || v === \"1\" || v === \"yes\" || v === \"on\";\n }\n if (env.TENANT_ID && env.TENANT_ID.length > 0) {\n out.hosted.tenant_id = env.TENANT_ID;\n }\n if (env.WIKI_ROOT && env.WIKI_ROOT.length > 0) {\n out.wiki_root = env.WIKI_ROOT;\n // When WIKI_ROOT is explicitly set, the wiki layout is flat — the env\n // value IS the wiki root, no wiki-store/ wrapper. Flatten default paths\n // that assume the wrapper. (User-overridden values in wotw.yaml stay\n // intact; we only touch defaults.)\n if (out.raw_path === \"./wiki-store/raw\") {\n out.raw_path = \"raw\";\n }\n }\n if (env.WOTW_PLAN === \"founding\" || env.WOTW_PLAN === \"pro\") {\n out.hosted.plan = env.WOTW_PLAN;\n }\n if (env.WOTW_TIMEZONE && env.WOTW_TIMEZONE.length > 0) {\n out.hosted.timezone = env.WOTW_TIMEZONE;\n }\n if (env.WOTW_PORT) {\n const n = Number.parseInt(env.WOTW_PORT, 10);\n if (Number.isFinite(n) && n > 0 && n < 65536) {\n out.server.port = n;\n }\n }\n if (env.WOTW_HOST && env.WOTW_HOST.length > 0) {\n out.server.host = env.WOTW_HOST;\n }\n if (env.WOTW_LOG_LEVEL) {\n const lvl = env.WOTW_LOG_LEVEL;\n if (\n lvl === \"trace\" ||\n lvl === \"debug\" ||\n lvl === \"info\" ||\n lvl === \"warn\" ||\n lvl === \"error\" ||\n lvl === \"fatal\"\n ) {\n out.daemon.log_level = lvl;\n }\n }\n if (\n env.WOTW_RUNTIME_MODE === \"auto\" ||\n env.WOTW_RUNTIME_MODE === \"cli\" ||\n env.WOTW_RUNTIME_MODE === \"api\"\n ) {\n out.execution.mode = env.WOTW_RUNTIME_MODE;\n }\n // Multi-LLM Phase 10: per-tenant provider selection via hosted-mode\n // env vars. The wotw-cloud orchestrator sets these at spawn time\n // based on `wiki.llm_provider`. Defaults to anthropic when unset.\n if (\n env.WOTW_LLM_PROVIDER === \"anthropic\" ||\n env.WOTW_LLM_PROVIDER === \"openai\" ||\n env.WOTW_LLM_PROVIDER === \"gemini\" ||\n env.WOTW_LLM_PROVIDER === \"ollama\"\n ) {\n out.llm.provider = env.WOTW_LLM_PROVIDER;\n // Update execution.api_key_env to match the chosen provider's\n // canonical key var. Each provider's SDK reads from its own env var;\n // the daemon's runtime-aware dispatch consults this field when\n // selecting which key to inject.\n switch (env.WOTW_LLM_PROVIDER) {\n case \"anthropic\":\n out.execution.api_key_env = \"ANTHROPIC_API_KEY\";\n break;\n case \"openai\":\n out.execution.api_key_env = \"OPENAI_API_KEY\";\n break;\n case \"gemini\":\n out.execution.api_key_env = \"GOOGLE_API_KEY\";\n break;\n case \"ollama\":\n // Ollama uses no API key. Leave api_key_env at whatever default\n // the config has; the dispatcher ignores it for Ollama.\n break;\n }\n }\n if (env.WOTW_LLM_MODEL && env.WOTW_LLM_MODEL.length > 0) {\n out.llm.model = env.WOTW_LLM_MODEL;\n }\n if (env.WOTW_OLLAMA_URL && env.WOTW_OLLAMA_URL.length > 0) {\n out.llm.ollama_url = env.WOTW_OLLAMA_URL;\n }\n // Review item 50: prefer the dedicated MCP bearer secret; fall back to\n // ADMIN_SERVICE_KEY only while wotw-cloud is being migrated to the\n // split-secret scheme (G4 — viable rip-and-replace with tenant_count=0).\n if (env.WOTW_MCP_BEARER && env.WOTW_MCP_BEARER.length > 0) {\n out.server.auth_token = env.WOTW_MCP_BEARER;\n } else if (env.ADMIN_SERVICE_KEY && env.ADMIN_SERVICE_KEY.length > 0) {\n out.server.auth_token = env.ADMIN_SERVICE_KEY;\n }\n // In hosted mode, default to stdout logging so container log streams\n // (Fly, Kubernetes, Docker) capture daemon output without ssh+cat. The\n // file-default ~/.wotw/daemon.log is invisible to Fly's log stream which\n // is a critical observability blind spot for production support. Users\n // who explicitly want file logging in hosted mode can set WOTW_LOG_FILE.\n if (env.WOTW_LOG_FILE !== undefined) {\n out.daemon.log_file = env.WOTW_LOG_FILE;\n } else if (out.hosted.enabled) {\n out.daemon.log_file = \"\";\n }\n\n // In hosted mode, disable candidates staging. The interactive `wotw start\n // --auto-approve` flag sets `ingestion.staging = false` to bypass the\n // human-review queue; the hosted entry point (dist/daemon/entry.js)\n // bypasses the CLI entirely, so the flag never fires and the\n // interactive-mode default `staging: true` leaks into hosted operation.\n // Without this override, every ingestion writes to candidates/ and waits\n // forever for a human reviewer who doesn't exist in the hosted topology\n // (per-tenant Fly Machine, BYOK, autonomous operation). BM25 indexes\n // wiki/{plural-category}/ and never sees candidates/, so MCP query returns\n // \"no relevant wiki pages found\" despite successful ingestion. Validation-\n // gap instance #12 closure, 2026-05-12.\n if (out.hosted.enabled) {\n out.ingestion.staging = false;\n // Pass 012-completion (2026-05-19): two additional hosted-mode\n // defaults ratified from Pass 012's audit. See\n // feedback_hosted_mode_default_audit.md for the inversion-shape framing.\n //\n // A third candidate (health.detect_contradictions) was originally in\n // Pass 012's audit but surfaced during Pass 012-completion verification\n // as having no runtime consumer at v0.2.12 — the LLM contradiction\n // detection pass that would check this flag is not yet implemented.\n // Hosted-mode override deferred to a future consumer-implementation\n // pass. See TODO at the field's declaration in src/utils/types.ts.\n out.lint.schedule_enabled = true;\n out.lint.auto_fix = true;\n }\n return out;\n}\n\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Hosted-mode runtime invariants. Throws with a precise message when:\n * - `hosted.enabled` is true but `tenant_id` is missing\n * - `tenant_id` is set but is not a valid UUID\n * - `wiki_root` is an empty/relative path that won't resolve to a stable\n * mount point under `/data`\n *\n * Called from {@link loadConfig}. Community-mode configs (where\n * `hosted.enabled` is false) are unaffected.\n */\nexport function validateHostedConfig(config: WotwConfig): void {\n if (!config.hosted.enabled) return;\n if (!config.hosted.tenant_id || config.hosted.tenant_id.length === 0) {\n throw new Error(\n \"Config error: hosted.enabled is true but TENANT_ID / hosted.tenant_id is unset.\",\n );\n }\n if (!UUID_REGEX.test(config.hosted.tenant_id)) {\n throw new Error(\n `Config error: hosted.tenant_id \"${config.hosted.tenant_id}\" is not a valid UUID.`,\n );\n }\n if (!config.wiki_root || config.wiki_root.length === 0) {\n throw new Error(\"Config error: hosted.enabled is true but wiki_root / WIKI_ROOT is unset.\");\n }\n // Review item 60: a relative wiki_root in hosted mode resolves against\n // process.cwd(), which is the ephemeral container filesystem on Fly\n // Machines. Tenant data would be wiped on every restart. Require\n // absolute path in hosted mode.\n if (!config.wiki_root.startsWith(\"/\")) {\n throw new Error(\n `Config error: hosted.enabled is true but wiki_root \"${config.wiki_root}\" is not absolute. ` +\n `In hosted mode the daemon must persist tenant data on a mounted volume; ` +\n `a relative path resolves against process.cwd() which is the ephemeral container fs.`,\n );\n }\n}\n\n/**\n * Deep-merge user config on top of defaults. Unknown keys in user config are dropped\n * to prevent typos from leaking into runtime behavior.\n */\nexport function mergeConfig(base: WotwConfig, override: Partial<WotwConfig>): WotwConfig {\n const out: WotwConfig = structuredClone(base);\n const assign = <K extends keyof WotwConfig>(key: K, value: Partial<WotwConfig[K]>): void => {\n out[key] = { ...(out[key] as object), ...(value as object) } as WotwConfig[K];\n };\n\n if (override.wiki_root !== undefined) out.wiki_root = override.wiki_root;\n if (override.raw_path !== undefined) out.raw_path = override.raw_path;\n if (override.llm) assign(\"llm\", override.llm);\n if (override.execution) assign(\"execution\", override.execution);\n if (override.models) assign(\"models\", override.models);\n if (override.watcher) assign(\"watcher\", override.watcher);\n if (override.ingestion) assign(\"ingestion\", override.ingestion);\n if (override.cost) assign(\"cost\", override.cost);\n if (override.server) assign(\"server\", override.server);\n if (override.daemon) assign(\"daemon\", override.daemon);\n if (override.compounding) assign(\"compounding\", override.compounding);\n if (override.provenance) assign(\"provenance\", override.provenance);\n if (override.multi_user) assign(\"multi_user\", override.multi_user);\n if (override.query) assign(\"query\", override.query);\n if (override.fact_extraction) assign(\"fact_extraction\", override.fact_extraction);\n if (override.lint) assign(\"lint\", override.lint);\n if (override.health) {\n // Deep-merge the weights sub-object separately.\n const healthBase = out.health;\n const healthOverride = override.health as Partial<WotwConfig[\"health\"]>;\n out.health = { ...healthBase, ...healthOverride };\n if (healthOverride.weights) {\n out.health.weights = { ...healthBase.weights, ...healthOverride.weights };\n }\n }\n if (override.hosted) assign(\"hosted\", override.hosted);\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Zod validation schema\n// ---------------------------------------------------------------------------\n\nconst positiveNumber = z.number().positive();\nconst nonNegativeNumber = z.number().min(0);\nconst logLevelSchema = z.enum([\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"]);\nconst llmProviderSchema = z.enum([\"anthropic\", \"openai\", \"gemini\", \"ollama\"]);\n\n/**\n * Zod schema that validates a fully-merged WotwConfig object. Every field\n * has a default matching {@link defaultConfig} so a bare `{}` passes.\n */\nconst WotwConfigSchema = z.object({\n wiki_root: z.string().min(1),\n raw_path: z.string().min(1),\n llm: z.object({\n provider: llmProviderSchema,\n model: z.string().min(1),\n ollama_url: z.string().min(1).optional(),\n }),\n execution: z.object({\n mode: z.enum([\"auto\", \"cli\", \"api\"]),\n cli_path: z.string().min(1),\n cli_model: z.string().min(1),\n api_key_env: z.string().min(1),\n }),\n models: z.object({\n ingest: z.string().min(1),\n query: z.string().min(1),\n lint: z.string().min(1),\n compound_eval: z.string().min(1),\n }),\n watcher: z.object({\n debounce_initial_ms: positiveNumber,\n debounce_max_ms: positiveNumber,\n debounce_growth_factor: positiveNumber,\n burst_threshold: z.number().int().positive(),\n max_batch_size: z.number().int().positive(),\n ignore_patterns: z.array(z.string()),\n }),\n ingestion: z.object({\n max_turns: z.number().int().positive(),\n max_budget_per_batch_usd: positiveNumber,\n resume_session: z.boolean(),\n dead_letter_file: z.string(),\n staging: z.boolean(),\n }),\n cost: z.object({\n max_daily_usd: positiveNumber,\n max_per_query_usd: positiveNumber,\n max_per_ingest_usd: positiveNumber,\n track_file: z.string().min(1),\n }),\n server: z.object({\n port: z.number().int().min(1).max(65535),\n host: z.string().min(1),\n auth_token: z.string().nullable(),\n rate_limit_rpm: z.number().int().positive(),\n trust_proxy: z.boolean(),\n }),\n daemon: z.object({\n pid_file: z.string().min(1),\n lock_file: z.string().min(1),\n // Empty string is meaningful: \"log to stdout\" (used by hosted mode for\n // container log capture). initLogger treats empty as no destination\n // and defaults to pino's stdout output.\n log_file: z.string(),\n log_level: logLevelSchema,\n }),\n compounding: z.object({\n enabled: z.boolean(),\n min_source_pages: z.number().int().min(0),\n confidence_threshold: z.number().min(0).max(100),\n }),\n provenance: z.object({\n enabled: z.boolean(),\n chain_file: z.string().min(1),\n verify_on_startup: z.boolean(),\n }),\n multi_user: z.object({\n enabled: z.boolean(),\n workspaces_dir: z.string().min(1),\n }),\n query: z.object({\n expand: z.boolean(),\n }),\n fact_extraction: z.object({\n enabled: z.union([z.boolean(), z.literal(\"auto\")]),\n force_enabled: z.boolean(),\n questions_per_fact: z.number().int().min(1).max(5),\n model: z.string().min(1).optional(),\n }),\n lint: z.object({\n schedule_enabled: z.boolean(),\n interval_hours: positiveNumber,\n auto_fix: z.boolean(),\n }),\n hosted: z.object({\n enabled: z.boolean(),\n tenant_id: z.string().nullable(),\n concurrency_cap: z.number().int().positive(),\n paused: z.boolean(),\n plan: z.enum([\"founding\", \"pro\"]),\n limits: z.object({\n storage_bytes: positiveNumber,\n max_files_per_day: z.number().int().positive(),\n max_file_size_bytes: positiveNumber,\n max_ingest_bytes_per_day: positiveNumber,\n heal_cooldown_seconds: nonNegativeNumber,\n query_rate_limit_per_hour: z.number().int().positive(),\n onboarding_burst_multiplier: positiveNumber,\n onboarding_burst_hours: positiveNumber,\n }),\n timezone: z.string().min(1),\n created_at: z.string().nullable(),\n }),\n health: z.object({\n staleness_thresholds: z.array(z.number().int().min(0)),\n staleness_scores: z.array(z.number().min(0).max(100)),\n weights: z.object({\n staleness: nonNegativeNumber,\n source_availability: nonNegativeNumber,\n link_health: nonNegativeNumber,\n duplicate_risk: nonNegativeNumber,\n contradiction_risk: nonNegativeNumber,\n }),\n duplicate_threshold: z.number().min(0).max(100),\n auto_fix_staleness_below: z.number().min(0).max(100),\n max_fixes_per_run: z.number().int().min(0),\n detect_contradictions: z.boolean(),\n consolidation_threshold: z.number().int().min(2),\n consolidation_enabled: z.boolean(),\n zero_hit_threshold: z.number().min(0).max(1),\n enrichment_enabled: z.boolean(),\n query_log_file: z.string(),\n }),\n});\n\n/**\n * Validate a merged config against the Zod schema. Throws a descriptive\n * error on failure, naming the invalid field, expected type, and value.\n */\nexport function validateConfig(config: WotwConfig): WotwConfig {\n const result = WotwConfigSchema.safeParse(config);\n if (!result.success) {\n const issue = result.error.issues[0]!;\n const path = issue.path.join(\".\");\n throw new Error(`Config error: \"${path}\" ${issue.message}`);\n }\n return result.data as WotwConfig;\n}\n\n/**\n * Expand all path-like fields in a config using {@link resolvePath}.\n * Returns a new config; the input is not mutated.\n */\nexport function resolveConfigPaths(config: WotwConfig, baseDir?: string): WotwConfig {\n const out = structuredClone(config);\n out.wiki_root = resolvePath(out.wiki_root, baseDir);\n out.raw_path = resolvePath(out.raw_path, baseDir);\n out.cost.track_file = resolvePath(out.cost.track_file, baseDir);\n out.daemon.pid_file = resolvePath(out.daemon.pid_file, baseDir);\n out.daemon.lock_file = resolvePath(out.daemon.lock_file, baseDir);\n // Empty log_file is meaningful (means \"log to stdout\" in hosted mode);\n // resolvePath would prepend baseDir to it, producing a path that points\n // at the baseDir itself. Skip resolution when empty.\n if (out.daemon.log_file.length > 0) {\n out.daemon.log_file = resolvePath(out.daemon.log_file, baseDir);\n }\n out.multi_user.workspaces_dir = resolvePath(out.multi_user.workspaces_dir, baseDir);\n // Provenance chain lives inside the wiki root by default — resolve it\n // against the (already resolved) wiki_root so a relative default like\n // `provenance-chain.jsonl` lands in the right place.\n out.provenance.chain_file = resolvePath(out.provenance.chain_file, out.wiki_root);\n // Dead-letter file is likewise wiki-relative (so each wiki has its own\n // failure ledger). Empty string disables; leave it alone in that case.\n if (out.ingestion.dead_letter_file.length > 0) {\n out.ingestion.dead_letter_file = resolvePath(out.ingestion.dead_letter_file, out.wiki_root);\n }\n // Query log file is wiki-relative, like the dead-letter file.\n if (out.health.query_log_file.length > 0) {\n out.health.query_log_file = resolvePath(out.health.query_log_file, out.wiki_root);\n }\n return out;\n}\n","/**\n * Git utilities wrapping simple-git for initialization, commits, and log inspection.\n */\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { simpleGit, type SimpleGit } from \"simple-git\";\n\n/**\n * Check whether a directory already contains a `.git` folder.\n */\nexport function isGitRepo(dir: string): boolean {\n return existsSync(join(dir, \".git\"));\n}\n\n/**\n * Return a SimpleGit handle bound to a given directory.\n */\nexport function git(dir: string): SimpleGit {\n return simpleGit({ baseDir: dir });\n}\n\n/**\n * Initialize a git repo in `dir` if one does not already exist.\n * Creates an initial empty commit so subsequent commits have a parent.\n */\nexport async function ensureGitRepo(\n dir: string,\n initialMessage = \"chore: wotw init\",\n): Promise<void> {\n if (isGitRepo(dir)) return;\n const g = git(dir);\n await g.init();\n // Configure identity locally if none is set globally, so init commits don't fail.\n try {\n const name = (await g.getConfig(\"user.name\")).value;\n const email = (await g.getConfig(\"user.email\")).value;\n if (!name) await g.addConfig(\"user.name\", \"watcher-on-the-wall\", false, \"local\");\n if (!email) await g.addConfig(\"user.email\", \"wotw@localhost\", false, \"local\");\n } catch {\n await g.addConfig(\"user.name\", \"watcher-on-the-wall\", false, \"local\");\n await g.addConfig(\"user.email\", \"wotw@localhost\", false, \"local\");\n }\n await g.add(\".\");\n await g.commit(initialMessage, { \"--allow-empty\": null });\n}\n\n/**\n * Stage all and commit with a message. Silently no-op if nothing to commit.\n * When `paths` is provided, only those paths are staged (relative to `dir`);\n * otherwise the full working tree is staged.\n */\nexport async function commitAll(\n dir: string,\n message: string,\n paths?: string[],\n): Promise<string | null> {\n if (!isGitRepo(dir)) return null;\n const g = git(dir);\n if (paths && paths.length > 0) {\n await g.add(paths);\n } else {\n await g.add(\".\");\n }\n const status = await g.status();\n if (status.files.length === 0) return null;\n const result = await g.commit(message);\n return result.commit || null;\n}\n","/**\n * Pretty terminal output helpers wrapping chalk, boxen, and ora. All CLI commands\n * should emit user-visible output through this module; debug output goes through\n * pino via {@link getLogger}.\n */\nimport boxen, { type Options as BoxenOptions } from \"boxen\";\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\nconst stdout = process.stdout;\nconst stderr = process.stderr;\n\n/** Write a raw line to stdout. */\nexport function line(text = \"\"): void {\n stdout.write(`${text}\\n`);\n}\n\n/** Write an error line to stderr. */\nexport function errorLine(text: string): void {\n stderr.write(`${text}\\n`);\n}\n\n/** Success message in green. */\nexport function success(text: string): void {\n line(`${chalk.green(\"✔\")} ${text}`);\n}\n\n/** Info message in blue. */\nexport function info(text: string): void {\n line(`${chalk.cyan(\"ℹ\")} ${text}`);\n}\n\n/** Warning message in yellow. */\nexport function warn(text: string): void {\n line(`${chalk.yellow(\"⚠\")} ${text}`);\n}\n\n/** Failure message in red. */\nexport function fail(text: string): void {\n errorLine(`${chalk.red(\"✖\")} ${text}`);\n}\n\n/** Start a spinner with a given title. */\nexport function spinner(title: string): Ora {\n return ora({ text: title, color: \"cyan\" }).start();\n}\n\n/** Render a framed box around content. */\nexport function box(content: string, title?: string): void {\n const opts: BoxenOptions = {\n padding: 1,\n borderStyle: \"round\",\n borderColor: \"cyan\",\n ...(title ? { title, titleAlignment: \"center\" } : {}),\n };\n stdout.write(`${boxen(content, opts)}\\n`);\n}\n\n/** Render a two-column key/value table. */\nexport function keyValueTable(rows: Array<[string, string]>): string {\n const widest = rows.reduce((m, [k]) => Math.max(m, k.length), 0);\n return rows.map(([k, v]) => `${chalk.dim(k.padEnd(widest))} ${v}`).join(\"\\n\");\n}\n\n/** Export chalk for call sites that need ad-hoc coloring. */\nexport { chalk };\n","/**\n * Obsidian vault detection and launch helpers.\n *\n * Obsidian stores its vault registry in a platform-specific `obsidian.json`\n * file that maps hex IDs → `{ path, ts, open }`. We parse it best-effort;\n * any error (missing file, bad JSON, permission denied) just yields an empty\n * list so the init wizard falls back to manual entry.\n *\n * The launch helper uses the `obsidian://` URI scheme, which is registered\n * by the Obsidian desktop app on all three platforms. We dispatch through\n * `open` / `xdg-open` / `start` and return `false` on any failure — the\n * wizard reports a friendly \"couldn't open automatically\" note and moves on.\n */\nimport { exec } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { basename, dirname, join, parse, resolve } from \"node:path\";\n\n/** A single Obsidian vault as surfaced by the registry. */\nexport interface ObsidianVault {\n /** Basename of the vault root, used as the display name. */\n name: string;\n /** Absolute path to the vault root. */\n path: string;\n /** Last-opened timestamp from the registry (ms since epoch). */\n ts: number;\n /** Whether the vault is currently marked open in the registry. */\n open: boolean;\n}\n\n/** Shape of a single entry in the `vaults` object of `obsidian.json`. */\ninterface ObsidianJsonVaultEntry {\n path?: unknown;\n ts?: unknown;\n open?: unknown;\n}\n\n/** Shape of the top-level `obsidian.json` file. */\ninterface ObsidianJson {\n vaults?: Record<string, ObsidianJsonVaultEntry>;\n}\n\n/**\n * Return the absolute path of Obsidian's per-user registry file. Exported\n * for tests so they can stub the location via the `override` parameter.\n */\nexport function obsidianRegistryPath(override?: string): string {\n if (override && override.length > 0) return override;\n const home = homedir();\n switch (platform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"obsidian\", \"obsidian.json\");\n case \"win32\": {\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n return join(appData, \"obsidian\", \"obsidian.json\");\n }\n default:\n // Linux / WSL / BSDs — XDG_CONFIG_HOME if set, else ~/.config.\n return join(\n process.env.XDG_CONFIG_HOME ?? join(home, \".config\"),\n \"obsidian\",\n \"obsidian.json\",\n );\n }\n}\n\n/**\n * Read the Obsidian registry and return every vault that still exists on\n * disk, sorted by last-opened timestamp descending. Any failure — missing\n * file, parse error, unreadable — returns an empty array.\n *\n * The `registryPath` argument is exposed for tests; production callers\n * should use the default.\n */\nexport function findObsidianVaults(registryPath?: string): ObsidianVault[] {\n const path = obsidianRegistryPath(registryPath);\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return [];\n }\n let parsed: ObsidianJson;\n try {\n parsed = JSON.parse(raw) as ObsidianJson;\n } catch {\n return [];\n }\n const vaults = parsed.vaults ?? {};\n const out: ObsidianVault[] = [];\n for (const entry of Object.values(vaults)) {\n if (!entry || typeof entry !== \"object\") continue;\n const p = entry.path;\n if (typeof p !== \"string\" || p.length === 0) continue;\n if (!existsSync(p)) continue;\n try {\n if (!statSync(p).isDirectory()) continue;\n } catch {\n continue;\n }\n out.push({\n name: basename(p),\n path: p,\n ts: typeof entry.ts === \"number\" ? entry.ts : 0,\n open: entry.open === true,\n });\n }\n out.sort((a, b) => b.ts - a.ts);\n return out;\n}\n\n/**\n * Walk up from `dir` looking for a directory that contains `.obsidian/`.\n * Returns the vault root (the directory that holds `.obsidian/`) or null\n * if none is found before reaching the filesystem root.\n */\nexport function findEnclosingVault(dir: string): string | null {\n let current = resolve(dir);\n // Guard against symlink loops / pathological input by bounding the walk.\n for (let i = 0; i < 64; i += 1) {\n const candidate = join(current, \".obsidian\");\n try {\n if (statSync(candidate).isDirectory()) return current;\n } catch {\n // not a directory at this level, keep walking\n }\n const parent = dirname(current);\n if (parent === current) return null;\n const parsed = parse(current);\n if (current === parsed.root) return null;\n current = parent;\n }\n return null;\n}\n\n/**\n * Platform-specific command used to hand an `obsidian://` URI off to the\n * registered handler. Exported so tests can snapshot the command without\n * actually spawning anything.\n */\nexport function obsidianOpenCommand(vaultPath: string): { command: string; args: string[] } {\n const uri = `obsidian://open?path=${encodeURIComponent(vaultPath)}`;\n switch (platform()) {\n case \"darwin\":\n return { command: \"open\", args: [uri] };\n case \"win32\":\n // `start` is a cmd.exe builtin — invoke via cmd /c. The empty title\n // argument is required because `start` treats the first quoted arg\n // as a window title.\n return { command: \"cmd\", args: [\"/c\", \"start\", \"\", uri] };\n default:\n return { command: \"xdg-open\", args: [uri] };\n }\n}\n\n/**\n * Launch Obsidian on the given vault path via the `obsidian://open` URI.\n * Returns true if the launcher command exited 0, false on any failure\n * (command not found, non-zero exit, timeout). Never throws.\n */\nexport async function openInObsidian(vaultPath: string): Promise<boolean> {\n const { command, args } = obsidianOpenCommand(vaultPath);\n // Quote each arg defensively. `exec` runs through /bin/sh (or cmd.exe on\n // Windows) so we must escape. We only pass constants + an encoded URI\n // so there is no user-controlled injection surface, but quoting keeps\n // shells that interpret `?` or `&` happy.\n const quoted = [command, ...args.map(shellQuote)].join(\" \");\n return await new Promise<boolean>((resolvePromise) => {\n const child = exec(quoted, { timeout: 5000 }, (err) => {\n resolvePromise(err === null || err === undefined);\n });\n child.on(\"error\", () => resolvePromise(false));\n });\n}\n\n/**\n * Tiny shell-quoting helper. On Windows we fall back to double quotes;\n * everywhere else we use single quotes with the standard `'\\''` escape.\n */\nfunction shellQuote(arg: string): string {\n if (arg.length === 0) return `''`;\n if (platform() === \"win32\") {\n // cmd.exe double-quote: escape embedded quotes.\n return `\"${arg.replace(/\"/g, '\\\\\"')}\"`;\n }\n // POSIX shells: wrap in single quotes, escape embedded single quotes.\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n","/**\n * Daemon lifecycle: PID file management, file locking, graceful shutdown.\n *\n * A running daemon has three facts associated with it:\n * 1. A PID file containing its numeric PID and a timestamp.\n * 2. A lock file that prevents two daemons from starting simultaneously.\n * 3. A log file where pino writes structured events.\n */\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { atomicWriteSync, ensureDirSync, removeIfExistsSync } from \"../utils/fs.js\";\n\nexport interface PidFileContents {\n pid: number;\n started_at: string;\n version: string;\n}\n\n/**\n * Write the PID file atomically.\n */\nexport function writePidFile(pidFilePath: string, contents: PidFileContents): void {\n ensureDirSync(dirname(pidFilePath));\n atomicWriteSync(pidFilePath, JSON.stringify(contents));\n}\n\n/**\n * Read a PID file, returning null if missing or malformed.\n */\nexport function readPidFile(pidFilePath: string): PidFileContents | null {\n if (!existsSync(pidFilePath)) return null;\n try {\n const raw = readFileSync(pidFilePath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"pid\" in parsed &&\n typeof (parsed as { pid: unknown }).pid === \"number\"\n ) {\n return parsed as PidFileContents;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Remove the PID file, ignoring missing files.\n */\nexport function removePidFile(pidFilePath: string): void {\n removeIfExistsSync(pidFilePath);\n}\n\n/**\n * Check whether a process is alive by sending signal 0. Returns true if it is\n * still alive, false otherwise. Handles EPERM (process owned by another user)\n * by returning true, since the process exists even if we cannot signal it.\n */\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") return false;\n if (code === \"EPERM\") return true;\n return false;\n }\n}\n\n/**\n * Check liveness based on the PID file: returns {alive, pid, contents}.\n * If the PID file references a dead process, the file is considered stale.\n */\nexport function checkDaemonAlive(pidFilePath: string): {\n alive: boolean;\n pid: number | null;\n stale: boolean;\n contents: PidFileContents | null;\n} {\n const contents = readPidFile(pidFilePath);\n if (!contents) return { alive: false, pid: null, stale: false, contents: null };\n const alive = isProcessAlive(contents.pid);\n return { alive, pid: contents.pid, stale: !alive, contents };\n}\n\n/**\n * Acquire the daemon start-lock. Returns a release function on success.\n * Throws if another process holds the lock.\n */\nexport async function acquireStartLock(lockPath: string): Promise<() => Promise<void>> {\n ensureDirSync(dirname(lockPath));\n // proper-lockfile requires the target file to exist; create a zero-byte stub if missing.\n if (!existsSync(lockPath)) writeFileSync(lockPath, \"\");\n const release = await lockfile.lock(lockPath, {\n stale: 10_000,\n retries: { retries: 0 },\n });\n return release;\n}\n\n/**\n * Send SIGTERM to a PID and wait up to `timeoutMs` for it to exit. Returns true\n * if the process exited, false if it is still alive after the timeout.\n */\nexport async function terminateAndWait(pid: number, timeoutMs = 10_000): Promise<boolean> {\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") return true;\n throw err;\n }\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!isProcessAlive(pid)) return true;\n await new Promise((r) => setTimeout(r, 100));\n }\n return !isProcessAlive(pid);\n}\n","/**\n * Append-only cost log. One JSON object per line, persisted under the\n * configured `cost.track_file` path. Designed for fast append + cheap\n * tail reads (status command reads the last ~1000 lines).\n */\nimport { appendFileSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { ensureDirSync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { CostLogEntry, ModelId, OperationType } from \"../utils/types.js\";\n\n/** Return today's UTC date as `YYYY-MM-DD`. */\nfunction utcToday(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/**\n * Shared JSONL cost-file parser. Sums `cost_usd` across every record whose\n * timestamp falls on the given UTC day. Used by both {@link CostTracker}\n * (for in-daemon budget checks) and by `wotw status` (for the \"cost today\"\n * readout) so there is a single source of truth for cost accounting\n * (L-CODE-3).\n *\n * Missing files return 0 gracefully. Non-ENOENT read failures throw so\n * that budget checks fail closed rather than silently returning $0.\n * Malformed lines are skipped individually.\n */\nexport function sumCostsForDay(trackFile: string, day: string = utcToday()): number {\n let text: string;\n try {\n text = readFileSync(trackFile, \"utf8\");\n } catch (err: unknown) {\n if (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return 0;\n }\n throw new Error(`cost log unreadable: ${err instanceof Error ? err.message : String(err)}`);\n }\n let total = 0;\n for (const rawLine of text.split(\"\\n\")) {\n if (!rawLine.trim()) continue;\n try {\n const entry = JSON.parse(rawLine) as { timestamp?: string; cost_usd?: number };\n if (\n typeof entry.timestamp === \"string\" &&\n entry.timestamp.slice(0, 10) === day &&\n typeof entry.cost_usd === \"number\"\n ) {\n total += entry.cost_usd;\n }\n } catch {\n // malformed line — skip\n }\n }\n return total;\n}\n\nexport class CostTracker {\n private readonly trackFile: string;\n private readonly maxDailyUsd: number;\n private readonly maxPerIngestUsd: number;\n private readonly maxPerQueryUsd: number;\n /**\n * Cache of today's total spend (L-PERF-1). Before this cache, every\n * `wouldExceedDaily()` call re-scanned the entire JSONL cost log —\n * O(n) on every pre-flight budget check, which a long-running daemon\n * eventually feels. We lazily hydrate the cache on first read and\n * keep it in sync by incrementing on each `record()`. When the UTC\n * day rolls over we simply re-scan once (bounded by \"lines written\n * today\" which is small).\n */\n private cachedDay: string | null = null;\n private cachedTotal = 0;\n\n constructor(opts: {\n trackFile: string;\n maxDailyUsd: number;\n maxPerIngestUsd: number;\n maxPerQueryUsd: number;\n }) {\n this.trackFile = opts.trackFile;\n this.maxDailyUsd = opts.maxDailyUsd;\n this.maxPerIngestUsd = opts.maxPerIngestUsd;\n this.maxPerQueryUsd = opts.maxPerQueryUsd;\n ensureDirSync(dirname(this.trackFile));\n }\n\n /** Append a cost entry to the log. */\n record(entry: CostLogEntry): void {\n const line = `${JSON.stringify(entry)}\\n`;\n // Update in-memory cache FIRST so budget checks reflect this entry\n // even if the file write fails.\n const entryDay = entry.timestamp.slice(0, 10);\n const today = utcToday();\n if (entryDay === today) {\n if (this.cachedDay === today) {\n this.cachedTotal += entry.cost_usd;\n } else {\n // Not yet hydrated; let the next spentToday() re-scan.\n this.cachedDay = null;\n }\n } else if (entryDay !== this.cachedDay) {\n // Entry is for an older day (unusual — timestamps are always \"now\")\n // or for the future. Invalidate so we re-scan next read.\n this.cachedDay = null;\n }\n try {\n appendFileSync(this.trackFile, line, \"utf8\");\n } catch (err) {\n getLogger(\"cost\").error(\n { err, file: this.trackFile, costUsd: entry.cost_usd },\n \"failed to persist cost entry — in-memory total updated but file may be stale\",\n );\n }\n }\n\n /**\n * Sum cost entries whose timestamp falls within today (UTC). Backed by\n * the in-memory cache populated on the first call and kept warm by\n * {@link record}. On a UTC day rollover the cache is re-hydrated once.\n */\n spentToday(): number {\n const today = utcToday();\n if (this.cachedDay === today) return this.cachedTotal;\n const total = sumCostsForDay(this.trackFile, today);\n this.cachedDay = today;\n this.cachedTotal = total;\n return total;\n }\n\n /** Return true if the given cost would exceed today's daily budget. */\n wouldExceedDaily(next: number): boolean {\n return this.spentToday() + next > this.maxDailyUsd;\n }\n\n /** Check per-operation caps. Returns null if allowed, error string if not. */\n checkOperationBudget(op: OperationType, cost: number): string | null {\n if (op === \"ingest\" && cost > this.maxPerIngestUsd) {\n return `ingest cost $${cost.toFixed(4)} exceeds per-ingest cap $${this.maxPerIngestUsd}`;\n }\n if (op === \"query\" && cost > this.maxPerQueryUsd) {\n return `query cost $${cost.toFixed(4)} exceeds per-query cap $${this.maxPerQueryUsd}`;\n }\n if (this.wouldExceedDaily(cost)) {\n return `operation would exceed daily cap $${this.maxDailyUsd}`;\n }\n return null;\n }\n\n /** Log entry with `operation: type` convenience. */\n logUsage(params: {\n operation: OperationType;\n model: ModelId;\n costUsd: number;\n inputTokens?: number;\n outputTokens?: number;\n batchId?: string;\n }): void {\n this.record({\n timestamp: new Date().toISOString(),\n operation: params.operation,\n model_id: params.model,\n cost_usd: params.costUsd,\n input_tokens: params.inputTokens,\n output_tokens: params.outputTokens,\n batch_id: params.batchId,\n });\n }\n}\n","/**\n * Dead-letter queue. Records permanently-failed ingestion batches to a\n * JSONL file so operators can inspect and replay them. One record per\n * line; append-only. An empty-string config path disables the queue\n * entirely (every call becomes a no-op) — useful for tests.\n *\n * Wired by {@link src/ingestion/queue.ts}: on catch, the queue calls\n * `deadLetter.record(batch, err)`, logs ERROR, and continues. Surfaced\n * to the user via `wotw status` (count) and the `get_stats` MCP tool.\n */\nimport { appendFile, readFile, rm } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { ensureDir, fileExists } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode } from \"../utils/types.js\";\nimport type { WatcherBatch } from \"../watcher/index.js\";\n\n/**\n * A single failed-batch record as persisted to the JSONL file. The\n * `retry` field is always `false` today — we don't auto-retry — but the\n * field is reserved so a future replay command can flip it when the\n * operator marks a batch for re-ingestion.\n */\nexport interface DeadLetterRecord {\n timestamp: string;\n batch_id: string;\n files: string[];\n reason: \"add\" | \"delete\";\n mode: RuntimeMode;\n error: string;\n stack?: string;\n retry: boolean;\n}\n\nexport interface DeadLetterOptions {\n /** Absolute path to the JSONL file. Empty string disables the queue. */\n path: string;\n /**\n * Runtime mode this batch ran under. Persisted with each record so the\n * operator can tell which execution mode produced the failure — useful\n * when the same wiki is used from a CLI machine and an API machine.\n */\n runtimeMode?: RuntimeMode;\n}\n\n/**\n * Append-only dead-letter sink. Every method is safe against a missing\n * path (empty string) and against I/O errors on the sink file — the\n * queue must never prevent the daemon from continuing.\n */\nexport class DeadLetterQueue {\n readonly path: string;\n private readonly runtimeMode: RuntimeMode;\n\n constructor(opts: DeadLetterOptions) {\n this.path = opts.path;\n this.runtimeMode = opts.runtimeMode ?? \"api\";\n }\n\n /** True if this queue actually persists anything. */\n get enabled(): boolean {\n return this.path.length > 0;\n }\n\n /**\n * Append a failure record. Never throws — a failing dead-letter sink\n * must not take the daemon down. Errors are logged at WARN so the\n * operator can still tell something went wrong with the ledger.\n */\n async record(\n batch: Pick<WatcherBatch, \"id\" | \"paths\">,\n error: unknown,\n reason: \"add\" | \"delete\" = \"add\",\n ): Promise<void> {\n if (!this.enabled) return;\n const log = getLogger(\"dead-letter\");\n const err = toError(error);\n const record: DeadLetterRecord = {\n timestamp: new Date().toISOString(),\n batch_id: batch.id,\n files: [...batch.paths],\n reason,\n mode: this.runtimeMode,\n error: err.message,\n ...(err.stack ? { stack: err.stack } : {}),\n retry: false,\n };\n try {\n await ensureDir(dirname(this.path));\n await appendFile(this.path, `${JSON.stringify(record)}\\n`, \"utf8\");\n log.error(\n {\n batchId: batch.id,\n files: record.files.length,\n reason,\n error: err.message,\n },\n \"batch permanently failed — recorded to dead-letter queue. \" +\n `Inspect with 'wotw status' or via get_stats; raw log: ${this.path}`,\n );\n } catch (writeErr) {\n log.warn({ err: writeErr, path: this.path }, \"failed to append dead-letter record\");\n }\n }\n\n /** Return the number of persisted failed batches (0 if disabled). */\n async count(): Promise<number> {\n if (!this.enabled || !fileExists(this.path)) return 0;\n try {\n const text = await readFile(this.path, \"utf8\");\n return text.split(\"\\n\").filter((l) => l.trim().length > 0).length;\n } catch {\n return 0;\n }\n }\n\n /** Return the last N records (most recent last). 0 = all. */\n async list(limit = 0): Promise<DeadLetterRecord[]> {\n if (!this.enabled || !fileExists(this.path)) return [];\n let text: string;\n try {\n text = await readFile(this.path, \"utf8\");\n } catch {\n return [];\n }\n const records: DeadLetterRecord[] = [];\n for (const rawLine of text.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line) continue;\n try {\n records.push(JSON.parse(line) as DeadLetterRecord);\n } catch {\n // Skip malformed lines silently — best-effort ledger.\n }\n }\n if (limit > 0 && records.length > limit) {\n return records.slice(records.length - limit);\n }\n return records;\n }\n\n /** Delete the ledger file. Idempotent. */\n async clear(): Promise<void> {\n if (!this.enabled) return;\n if (!fileExists(this.path)) return;\n await rm(this.path, { force: true });\n }\n}\n\n/** Coerce any thrown value to an Error so we can safely read message/stack. */\nfunction toError(err: unknown): Error {\n if (err instanceof Error) return err;\n if (typeof err === \"string\") return new Error(err);\n try {\n return new Error(JSON.stringify(err));\n } catch {\n return new Error(String(err));\n }\n}\n","/**\n * Wiki page model. A wiki page is a markdown file with YAML frontmatter.\n * This module parses, validates, and serializes them.\n */\nimport matter from \"gray-matter\";\nimport type {\n ConfidenceLevel,\n WikiCategory,\n WikiFrontmatter,\n WikiPage,\n WikiPageStatus,\n} from \"../utils/types.js\";\nimport { getLogger } from \"../utils/logger.js\";\n\nconst VALID_CATEGORIES: readonly WikiCategory[] = [\n \"concept\",\n \"entity\",\n \"source\",\n \"comparison\",\n \"synthesis\",\n \"query\",\n];\n\nconst VALID_CONFIDENCE: readonly ConfidenceLevel[] = [\"high\", \"medium\", \"low\"];\n\n/**\n * Parse a raw markdown file with frontmatter into a {@link WikiPage}.\n * Fills in missing fields with sensible defaults rather than throwing.\n *\n * Review item 45: a single malformed YAML page must not crash the whole\n * health / lint / heal / candidate pass. When `matter()` throws (invalid\n * YAML, BOM-prefix junk, indentation collision) we fall back to a body-\n * only parse: treat the entire raw content as body, derive a title from\n * the filename, and let the caller continue. The page is recoverable\n * via heal/manual edit.\n */\nexport function parsePage(filePath: string, raw: string): WikiPage {\n let parsed: ReturnType<typeof matter>;\n try {\n parsed = matter(raw);\n } catch {\n // Body-only fallback. Treat the entire raw content as body.\n parsed = { data: {}, content: raw } as ReturnType<typeof matter>;\n }\n const data = parsed.data as Record<string, unknown>;\n\n const now = new Date().toISOString().slice(0, 10);\n const category = normalizeCategory(data.category);\n const confidence = normalizeConfidence(data.confidence);\n const status = normalizeStatus(data.status);\n const frontmatter: WikiFrontmatter = {\n title: typeof data.title === \"string\" ? data.title : deriveTitle(filePath),\n category,\n created: typeof data.created === \"string\" ? data.created : now,\n updated: typeof data.updated === \"string\" ? data.updated : now,\n sources: normalizeStringArray(data.sources),\n related: normalizeStringArray(data.related),\n tags: normalizeStringArray(data.tags),\n confidence,\n };\n if (status) {\n frontmatter.status = status;\n if (typeof data.orphaned_at === \"string\") {\n frontmatter.orphaned_at = data.orphaned_at;\n }\n const orphanedSource = normalizeStringArray(data.orphaned_source);\n if (orphanedSource.length > 0) {\n frontmatter.orphaned_source = orphanedSource;\n }\n if (typeof data.merged_into === \"string\") {\n frontmatter.merged_into = data.merged_into;\n }\n }\n const contradictions = normalizeStringArray(data.contradictions);\n if (contradictions.length > 0) {\n frontmatter.contradictions = contradictions;\n }\n if (typeof data.last_compiled === \"string\") {\n frontmatter.last_compiled = data.last_compiled;\n }\n if (typeof data.source_count === \"number\") {\n frontmatter.source_count = data.source_count;\n }\n if (typeof data.last_confirmed === \"string\") {\n frontmatter.last_confirmed = data.last_confirmed;\n }\n if (data.superseded_by === null || typeof data.superseded_by === \"string\") {\n frontmatter.superseded_by = data.superseded_by;\n }\n if (typeof data.rejected_at === \"string\") {\n frontmatter.rejected_at = data.rejected_at;\n }\n if (typeof data.rejection_note === \"string\") {\n frontmatter.rejection_note = data.rejection_note;\n }\n if (typeof data.domain === \"string\") {\n frontmatter.domain = data.domain;\n }\n if (typeof data.scope === \"string\") {\n frontmatter.scope = data.scope;\n }\n const keyTerms = normalizeStringArray(data.key_terms);\n if (keyTerms.length > 0) {\n frontmatter.key_terms = keyTerms;\n }\n if (typeof data.consolidated_into === \"string\") {\n frontmatter.consolidated_into = data.consolidated_into;\n }\n\n return {\n path: filePath,\n frontmatter,\n body: parsed.content.trim(),\n raw,\n };\n}\n\n/**\n * Serialize a {@link WikiPage} back to a markdown string with frontmatter.\n */\nexport function serializePage(page: Pick<WikiPage, \"frontmatter\" | \"body\">): string {\n const fm = page.frontmatter;\n // matter.stringify automatically handles YAML escaping. Orphan-lifecycle\n // fields are only emitted when present so active pages stay clean.\n const data: Record<string, unknown> = {\n title: fm.title,\n category: fm.category,\n created: fm.created,\n updated: fm.updated,\n sources: fm.sources,\n related: fm.related,\n tags: fm.tags,\n confidence: fm.confidence,\n };\n if (fm.status) data.status = fm.status;\n if (fm.orphaned_at) data.orphaned_at = fm.orphaned_at;\n if (fm.orphaned_source && fm.orphaned_source.length > 0) {\n data.orphaned_source = fm.orphaned_source;\n }\n if (fm.merged_into) data.merged_into = fm.merged_into;\n if (fm.contradictions && fm.contradictions.length > 0) {\n data.contradictions = fm.contradictions;\n }\n if (fm.last_compiled) data.last_compiled = fm.last_compiled;\n if (fm.source_count !== undefined) data.source_count = fm.source_count;\n if (fm.last_confirmed) data.last_confirmed = fm.last_confirmed;\n if (fm.superseded_by !== undefined) data.superseded_by = fm.superseded_by;\n if (fm.rejected_at) data.rejected_at = fm.rejected_at;\n if (fm.rejection_note) data.rejection_note = fm.rejection_note;\n if (fm.domain) data.domain = fm.domain;\n if (fm.scope) data.scope = fm.scope;\n if (fm.key_terms && fm.key_terms.length > 0) data.key_terms = fm.key_terms;\n if (fm.consolidated_into) data.consolidated_into = fm.consolidated_into;\n return matter.stringify(page.body, data);\n}\n\n/**\n * Create a new blank page with defaults filled in.\n */\nexport function newPage(\n path: string,\n title: string,\n category: WikiCategory,\n body: string,\n opts: Partial<WikiFrontmatter> = {},\n): WikiPage {\n const now = new Date().toISOString().slice(0, 10);\n const frontmatter: WikiFrontmatter = {\n title,\n category,\n created: opts.created ?? now,\n updated: opts.updated ?? now,\n sources: opts.sources ?? [],\n related: opts.related ?? [],\n tags: opts.tags ?? [],\n confidence: opts.confidence ?? \"medium\",\n };\n return {\n path,\n frontmatter,\n body: body.trim(),\n raw: \"\",\n };\n}\n\nfunction normalizeCategory(v: unknown): WikiCategory {\n if (typeof v === \"string\" && (VALID_CATEGORIES as readonly string[]).includes(v)) {\n return v as WikiCategory;\n }\n if (v !== undefined && v !== null) {\n getLogger(\"page\").debug({ field: \"category\", value: v }, \"coerced invalid frontmatter value\");\n }\n return \"concept\";\n}\n\nfunction normalizeConfidence(v: unknown): ConfidenceLevel {\n if (typeof v === \"string\" && (VALID_CONFIDENCE as readonly string[]).includes(v)) {\n return v as ConfidenceLevel;\n }\n if (v !== undefined && v !== null) {\n getLogger(\"page\").debug({ field: \"confidence\", value: v }, \"coerced invalid frontmatter value\");\n }\n return \"medium\";\n}\n\nfunction normalizeStatus(v: unknown): WikiPageStatus | null {\n if (v === \"orphaned\") return \"orphaned\";\n if (v === \"merged\") return \"merged\";\n if (v === \"stale\") return \"stale\";\n if (v === \"consolidated\") return \"consolidated\";\n // Review item 46: prior implementation silently dropped unknown\n // values. Lifecycle gates (orphaned/merged/consolidated/stale)\n // depend on this field; an unknown value being coerced to null\n // means the gate fails open. Log so operators see schema drift.\n if (v !== undefined && v !== null) {\n getLogger(\"page\").warn(\n { field: \"status\", value: v },\n \"page frontmatter has unknown status value — coerced to null\",\n );\n }\n return null;\n}\n\nfunction normalizeStringArray(v: unknown): string[] {\n if (!Array.isArray(v)) return [];\n return v.filter((x): x is string => typeof x === \"string\");\n}\n\nfunction deriveTitle(filePath: string): string {\n const base = filePath.split(\"/\").pop() ?? filePath;\n return base.replace(/\\.md$/i, \"\").replace(/[-_]+/g, \" \");\n}\n","/**\n * Wiki store: three-layer abstraction over the wiki_root directory tree.\n *\n * raw/ — immutable source files dropped by users (we only read them)\n * wiki/ — generated markdown pages grouped by category\n * sources/\n * concepts/\n * entities/\n * comparisons/\n * syntheses/\n * queries/\n *\n * The store knows how to list pages, read them, write them atomically,\n * and resolve slugs to category directories. It owns no mutation policy\n * beyond \"one file per page, atomic writes, directories created on demand\".\n */\nimport { createHash } from \"node:crypto\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport { basename, join, relative, resolve, sep } from \"node:path\";\nimport { atomicWrite, dirExists, ensureDir, readTextOrNullAsync } from \"../utils/fs.js\";\nimport type { WikiCategory, WikiPage } from \"../utils/types.js\";\nimport { parsePage, serializePage } from \"./page.js\";\n\n/** Map category → wiki/<subdir>. */\nexport const CATEGORY_DIRS: Readonly<Record<WikiCategory, string>> = {\n concept: \"concepts\",\n entity: \"entities\",\n source: \"sources\",\n comparison: \"comparisons\",\n synthesis: \"syntheses\",\n query: \"queries\",\n};\n\nexport interface StoreOptions {\n wikiRoot: string;\n}\n\n/**\n * WikiStore resolves paths inside the wiki root and performs atomic IO.\n * It does NOT emit events — the caller is responsible for higher-level effects.\n */\nexport class WikiStore {\n readonly wikiRoot: string;\n readonly wikiDir: string;\n /** Candidates staging directory (sibling of wiki/). */\n readonly candidatesDir: string;\n /** Rejected candidates subdirectory. */\n readonly rejectedDir: string;\n\n constructor(opts: StoreOptions) {\n this.wikiRoot = resolve(opts.wikiRoot);\n this.wikiDir = join(this.wikiRoot, \"wiki\");\n this.candidatesDir = join(this.wikiRoot, \"candidates\");\n this.rejectedDir = join(this.wikiRoot, \"candidates\", \"rejected\");\n }\n\n /** Ensure every category subdirectory exists. */\n async ensureLayout(): Promise<void> {\n await ensureDir(this.wikiDir);\n for (const dir of Object.values(CATEGORY_DIRS)) {\n await ensureDir(join(this.wikiDir, dir));\n }\n await ensureDir(this.candidatesDir);\n await ensureDir(this.rejectedDir);\n }\n\n /** Absolute path to the category directory. */\n categoryDir(category: WikiCategory): string {\n return join(this.wikiDir, CATEGORY_DIRS[category]);\n }\n\n /** Build an absolute path for a page slug inside a category. */\n pathFor(category: WikiCategory, slug: string): string {\n const safe = sanitizeSlug(slug);\n return join(this.categoryDir(category), `${safe}.md`);\n }\n\n /** Relative-to-wiki-root path for human-readable references. */\n relativePath(absPath: string): string {\n return relative(this.wikiRoot, absPath);\n }\n\n /** List all markdown pages across every category. */\n listAll(): string[] {\n const out: string[] = [];\n if (!dirExists(this.wikiDir)) return out;\n for (const dir of Object.values(CATEGORY_DIRS)) {\n const full = join(this.wikiDir, dir);\n if (!dirExists(full)) continue;\n for (const entry of readdirSync(full)) {\n if (entry.endsWith(\".md\")) out.push(join(full, entry));\n }\n }\n return out.sort();\n }\n\n /** Read and parse a page by absolute path. Returns null if missing. */\n async readPage(absPath: string): Promise<WikiPage | null> {\n const raw = await readTextOrNullAsync(absPath);\n if (raw === null) return null;\n return parsePage(absPath, raw);\n }\n\n /**\n * Write a page atomically. Creates parent dirs if needed.\n *\n * Review item 48: rejects writes that resolve outside the wiki root.\n * Pre-fix, the only guard was each caller getting the path right; one\n * forgotten check would land arbitrary content via a crafted\n * `page.path`. Now the store enforces containment itself.\n */\n async writePage(page: WikiPage): Promise<void> {\n const abs = resolve(page.path);\n const root = resolve(this.wikiRoot);\n if (abs !== root && !abs.startsWith(`${root}${sep}`)) {\n throw new Error(\n `WikiStore.writePage: page.path ${page.path} resolves outside wikiRoot ${this.wikiRoot}`,\n );\n }\n const serialized = serializePage(page);\n await atomicWrite(abs, serialized);\n }\n\n /**\n * Find an existing page by title within a category, matching on\n * frontmatter.title case-insensitively. Used for idempotent upserts.\n */\n async findByTitle(category: WikiCategory, title: string): Promise<WikiPage | null> {\n const dir = this.categoryDir(category);\n if (!dirExists(dir)) return null;\n const needle = title.trim().toLowerCase();\n for (const entry of readdirSync(dir)) {\n if (!entry.endsWith(\".md\")) continue;\n const page = await this.readPage(join(dir, entry));\n if (page && page.frontmatter.title.trim().toLowerCase() === needle) {\n return page;\n }\n }\n return null;\n }\n\n /** List all markdown files in candidates/ (not rejected). */\n listCandidates(): string[] {\n const out: string[] = [];\n if (!dirExists(this.candidatesDir)) return out;\n for (const entry of readdirSync(this.candidatesDir)) {\n if (entry === \"rejected\") continue;\n const full = join(this.candidatesDir, entry);\n try {\n if (statSync(full).isFile() && entry.endsWith(\".md\")) {\n out.push(full);\n }\n } catch {\n // Skip entries we can't stat.\n }\n }\n return out.sort();\n }\n\n /** List rejected candidates. */\n listRejected(): string[] {\n const out: string[] = [];\n if (!dirExists(this.rejectedDir)) return out;\n for (const entry of readdirSync(this.rejectedDir)) {\n const full = join(this.rejectedDir, entry);\n try {\n if (statSync(full).isFile() && entry.endsWith(\".md\")) {\n out.push(full);\n }\n } catch {\n // Skip entries we can't stat.\n }\n }\n return out.sort();\n }\n\n /** Count markdown files, optionally filtered by category. */\n count(category?: WikiCategory): number {\n if (category) {\n const dir = this.categoryDir(category);\n if (!dirExists(dir)) return 0;\n return readdirSync(dir).filter((e) => e.endsWith(\".md\")).length;\n }\n return this.listAll().length;\n }\n}\n\n/**\n * Convert an arbitrary string into a safe slug: lowercase, hyphens,\n * strip anything that isn't alphanumeric/hyphen/underscore.\n */\nexport function sanitizeSlug(input: string): string {\n const trimmed = input.trim().toLowerCase();\n const base = trimmed.endsWith(\".md\") ? trimmed.slice(0, -3) : trimmed;\n const cleaned = base\n .replace(/[^a-z0-9\\-_\\s]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return cleaned || `untitled-${createHash(\"sha256\").update(input).digest(\"hex\").slice(0, 8)}`;\n}\n\n/** Derive a slug from an absolute file path (drops directory + extension). */\nexport function slugFromPath(absPath: string): string {\n return sanitizeSlug(basename(absPath).replace(/\\.md$/i, \"\"));\n}\n\n/** Collect file stats for a wiki page (size, mtime) without reading contents. */\nexport function pageStat(absPath: string): { size: number; mtimeMs: number } | null {\n try {\n const s = statSync(absPath);\n return { size: s.size, mtimeMs: s.mtimeMs };\n } catch {\n return null;\n }\n}\n","/**\n * In-memory full-text search over wiki pages using minisearch.\n * Re-indexes on every rebuild; no on-disk state.\n */\nimport MiniSearch from \"minisearch\";\nimport type { WikiPage } from \"../utils/types.js\";\n\nexport interface SearchHit {\n path: string;\n title: string;\n category: string;\n score: number;\n snippet: string;\n}\n\n/** Optional filters for narrowing search results. */\nexport interface SearchFilters {\n /** Only return pages matching this domain. */\n domain?: string;\n /** Only return pages matching this scope. */\n scope?: string;\n}\n\ninterface IndexDoc {\n id: string;\n path: string;\n title: string;\n category: string;\n tags: string;\n key_terms: string;\n domain: string;\n scope: string;\n body: string;\n}\n\n/**\n * Small wrapper around MiniSearch with sensible defaults for the wiki.\n */\nexport class WikiSearch {\n private readonly engine: MiniSearch<IndexDoc>;\n private byId = new Map<string, IndexDoc>();\n\n constructor() {\n this.engine = new MiniSearch<IndexDoc>({\n fields: [\"title\", \"tags\", \"key_terms\", \"body\"],\n storeFields: [\"path\", \"title\", \"category\", \"domain\", \"scope\"],\n searchOptions: {\n boost: { title: 3, tags: 2, key_terms: 2 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n }\n\n /** Replace the entire index with the given pages. */\n rebuild(pages: WikiPage[]): void {\n // Build new data structures before mutating existing ones.\n const docs = pages.map((p) => toDoc(p));\n const newById = new Map<string, IndexDoc>();\n for (const d of docs) newById.set(d.id, d);\n // Snapshot old state for rollback.\n const oldDocs = Array.from(this.byId.values());\n try {\n this.engine.removeAll();\n this.engine.addAll(docs);\n this.byId = newById;\n } catch (err) {\n // Rollback: restore old index.\n try {\n this.engine.removeAll();\n this.engine.addAll(oldDocs);\n } catch {\n /* rollback failed — index is now empty */\n }\n throw err;\n }\n }\n\n /** Upsert a single page into the index. */\n upsert(page: WikiPage): void {\n const doc = toDoc(page);\n if (this.byId.has(doc.id)) this.engine.replace(doc);\n else this.engine.add(doc);\n this.byId.set(doc.id, doc);\n }\n\n /** Drop a page from the index by its absolute path. */\n remove(absPath: string): void {\n const id = absPath;\n if (this.byId.has(id)) {\n this.engine.remove({ id } as IndexDoc);\n this.byId.delete(id);\n }\n }\n\n /** Query the index. Returns ranked hits with snippets. */\n search(query: string, limit = 20, filters?: SearchFilters): SearchHit[] {\n if (!query.trim()) return [];\n // OR-combination is the right default for natural-language questions —\n // AND would zero-out any query containing stop words or uncommon terms.\n const results = this.engine.search(query, { combineWith: \"OR\" });\n let hits = results.map((r) => {\n const doc = this.byId.get(r.id as string);\n return {\n path: (r as unknown as { path: string }).path,\n title: (r as unknown as { title: string }).title,\n category: (r as unknown as { category: string }).category,\n domain: (r as unknown as { domain: string }).domain,\n scope: (r as unknown as { scope: string }).scope,\n score: r.score,\n snippet: doc ? makeSnippet(doc.body, query) : \"\",\n };\n });\n\n // Apply optional metadata filters.\n if (filters?.domain) {\n const d = filters.domain.toLowerCase();\n hits = hits.filter((h) => h.domain.toLowerCase() === d);\n }\n if (filters?.scope) {\n const s = filters.scope.toLowerCase();\n hits = hits.filter((h) => h.scope.toLowerCase() === s);\n }\n\n // Strip internal fields before returning.\n return hits.slice(0, limit).map(({ domain: _d, scope: _s, ...rest }) => rest);\n }\n\n /** Number of indexed documents. */\n size(): number {\n return this.byId.size;\n }\n}\n\nfunction toDoc(page: WikiPage): IndexDoc {\n return {\n id: page.path,\n path: page.path,\n title: page.frontmatter.title,\n category: page.frontmatter.category,\n tags: page.frontmatter.tags.join(\" \"),\n key_terms: (page.frontmatter.key_terms ?? []).join(\" \"),\n domain: page.frontmatter.domain ?? \"\",\n scope: page.frontmatter.scope ?? \"\",\n body: page.body,\n };\n}\n\n/** Return ~240 characters around the first query term hit. */\nfunction makeSnippet(body: string, query: string): string {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n const lower = body.toLowerCase();\n let best = -1;\n for (const t of terms) {\n const idx = lower.indexOf(t);\n if (idx !== -1 && (best === -1 || idx < best)) best = idx;\n }\n if (best === -1) return body.slice(0, 240).trim();\n const start = Math.max(0, best - 80);\n const end = Math.min(body.length, best + 160);\n const prefix = start > 0 ? \"…\" : \"\";\n const suffix = end < body.length ? \"…\" : \"\";\n return `${prefix}${body.slice(start, end).trim()}${suffix}`.replace(/\\s+/g, \" \");\n}\n","/**\n * Provenance footer renderer. Appends a clickable provenance section to\n * wiki page bodies using Obsidian `[[wikilink]]` syntax, and strips it\n * during re-parse so the footer is always regenerated from frontmatter.\n */\nimport type { WikiFrontmatter } from \"../utils/types.js\";\n\n/** Sentinel comment used to detect the provenance footer block. */\nconst FOOTER_START = \"<!-- wotw:provenance:start -->\";\nconst FOOTER_END = \"<!-- wotw:provenance:end -->\";\n\n/**\n * Strip any existing provenance footer from a page body. Safe to call\n * on bodies that don't have one (returns the input unchanged).\n */\nexport function stripProvenanceFooter(body: string): string {\n const startIdx = body.indexOf(FOOTER_START);\n if (startIdx === -1) return body;\n // Remove everything from the separator before the sentinel to the end.\n // Walk backwards from startIdx to find a leading `---` separator.\n let cutStart = startIdx;\n const before = body.slice(0, startIdx);\n const lastSep = before.lastIndexOf(\"\\n---\\n\");\n if (lastSep !== -1 && before.slice(lastSep).trim() === \"---\") {\n cutStart = lastSep;\n }\n return body.slice(0, cutStart).trimEnd();\n}\n\n/**\n * Render a provenance footer from frontmatter data. Returns the footer\n * block including the `---` separator and sentinel comments.\n */\nexport function renderProvenanceFooter(fm: WikiFrontmatter): string {\n const sources = fm.sources ?? [];\n const links = sources.map((s) => `[[${s}]]`).join(\" | \");\n const compiled = fm.last_compiled ?? fm.updated ?? new Date().toISOString();\n const count = fm.source_count ?? sources.length;\n\n const lines = [\n \"\",\n \"---\",\n FOOTER_START,\n `**Sources:** ${links || \"_none_\"}`,\n `**Compiled:** ${compiled}`,\n `**Corroborating sources:** ${count}`,\n FOOTER_END,\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Ensure a page body has an up-to-date provenance footer. Strips any\n * existing footer and appends a fresh one from the frontmatter.\n */\nexport function ensureProvenanceFooter(body: string, fm: WikiFrontmatter): string {\n const clean = stripProvenanceFooter(body);\n return clean + renderProvenanceFooter(fm);\n}\n","/**\n * Wiki writer reconciler. The ingestion agent writes files directly using\n * the Agent SDK's Write/Edit tools, so the bulk of file IO is already done\n * by the time we receive the invoker result. The writer's job is to:\n *\n * 1. Walk the files the agent claims to have written.\n * 2. Parse each one and validate frontmatter.\n * 3. Rewrite pages through WikiStore.writePage so atomic writes +\n * frontmatter serialization go through a single choke point.\n * 4. Return the parsed WikiPage objects so the caller can update the\n * index, search, and cross-reference graph.\n *\n * If the agent wrote a page with missing or bad frontmatter, we normalize\n * it in place rather than failing the whole batch — the worst case is the\n * page gets a default category (\"concept\") and today's date.\n */\nimport { unlinkSync } from \"node:fs\";\nimport { basename, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { errMsg } from \"../utils/errors.js\";\nimport { readTextOrNullAsync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { WikiPage } from \"../utils/types.js\";\nimport { parsePage } from \"../wiki/page.js\";\nimport { ensureProvenanceFooter } from \"../wiki/provenance-footer.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\n\nexport interface ReconcileResult {\n /** Pages successfully parsed, validated, and atomically rewritten. */\n pages: WikiPage[];\n /** Paths the agent claimed to write but which we could not reconcile. */\n skipped: { path: string; reason: string }[];\n}\n\nexport interface ReconcileOptions {\n /** When true, redirect pages to candidates/ for human review. */\n staging?: boolean;\n}\n\n/**\n * Walk a list of absolute paths, keep only those inside the wiki directory,\n * parse them, normalize frontmatter, and rewrite them through the store.\n */\nexport async function reconcileWrittenPages(\n store: WikiStore,\n candidatePaths: string[],\n opts?: ReconcileOptions,\n): Promise<ReconcileResult> {\n const log = getLogger(\"wiki-writer\");\n const pages: WikiPage[] = [];\n const skipped: ReconcileResult[\"skipped\"] = [];\n const wikiDir = resolve(store.wikiDir);\n\n for (const p of candidatePaths) {\n const abs = resolve(p);\n const rel = relative(wikiDir, abs);\n // Reject paths that escape the wiki directory. We accept dotfiles\n // (`.gitkeep`, `.tools.yml`, etc.) — the earlier broad `rel.startsWith(\".\")`\n // check also rejected those legitimate files. The `..` check must only\n // catch \"parent\" components, not dotfile names like `..config.md`.\n if (\n rel === \"\" ||\n rel === \"..\" ||\n rel.startsWith(`..${sep}`) ||\n rel.startsWith(\"../\") ||\n isAbsolute(rel)\n ) {\n skipped.push({ path: abs, reason: \"outside wiki directory\" });\n continue;\n }\n const raw = await readTextOrNullAsync(abs);\n if (raw === null) {\n skipped.push({ path: abs, reason: \"file not found after agent run\" });\n continue;\n }\n try {\n const page = parsePage(abs, raw);\n // Populate lifecycle fields on write.\n const now = new Date().toISOString();\n page.frontmatter.last_compiled = now;\n page.frontmatter.source_count = page.frontmatter.sources.length;\n if (!page.frontmatter.last_confirmed) {\n page.frontmatter.last_confirmed = now;\n }\n if (page.frontmatter.superseded_by === undefined) {\n page.frontmatter.superseded_by = null;\n }\n // Append clickable provenance footer.\n page.body = ensureProvenanceFooter(page.body, page.frontmatter);\n\n if (opts?.staging === true) {\n // Redirect page to candidates/ for human review.\n const originalPath = page.path;\n const candidatePath = join(store.candidatesDir, basename(page.path));\n page.path = candidatePath;\n await store.writePage(page);\n // Remove the agent's original file (already written to category dir).\n try {\n if (resolve(originalPath) !== resolve(candidatePath)) {\n unlinkSync(originalPath);\n }\n } catch {\n // Best-effort cleanup.\n }\n } else {\n await store.writePage(page);\n }\n pages.push(page);\n } catch (err) {\n log.warn({ err, path: abs }, \"failed to parse written page\");\n skipped.push({ path: abs, reason: errMsg(err) });\n }\n }\n\n return { pages, skipped };\n}\n\n/**\n * Scan the entire wiki store, parse every page, and return them. Used on\n * daemon startup and whenever we need a full rebuild of the index/search.\n */\nexport async function loadAllPages(store: WikiStore): Promise<WikiPage[]> {\n const log = getLogger(\"wiki-writer\");\n const pages: WikiPage[] = [];\n for (const abs of store.listAll()) {\n const raw = await readTextOrNullAsync(abs);\n if (raw === null) continue;\n try {\n pages.push(parsePage(abs, raw));\n } catch (err) {\n log.warn({ err, path: abs }, \"failed to parse existing page during scan\");\n }\n }\n return pages;\n}\n","/**\n * Cross-reference manager. Keeps `related:` frontmatter bidirectional\n * across wiki pages. When page A lists page B as related, page B should\n * list page A in return.\n *\n * The manager works on slugs relative to the wiki directory (e.g.\n * `concepts/provenance-chain`). Bidirectional repair runs over a page set\n * and writes back only the pages that actually changed.\n */\nimport { relative } from \"node:path\";\nimport type { WikiPage } from \"../utils/types.js\";\nimport type { WikiStore } from \"./store.js\";\n\n/** Turn an absolute page path into a wiki-relative slug (no extension). */\nexport function toWikiSlug(store: WikiStore, absPath: string): string {\n const rel = relative(store.wikiDir, absPath).replace(/\\\\/g, \"/\");\n return rel.replace(/\\.md$/i, \"\");\n}\n\n/**\n * Ensure bidirectional related links across every provided page.\n * Returns the list of pages whose frontmatter was mutated in memory.\n * The caller is responsible for persisting them (via store.writePage).\n */\nexport function repairBidirectionalLinks(store: WikiStore, pages: WikiPage[]): WikiPage[] {\n const bySlug = new Map<string, WikiPage>();\n for (const p of pages) bySlug.set(toWikiSlug(store, p.path), p);\n\n const mutated = new Set<string>();\n\n for (const page of pages) {\n const mySlug = toWikiSlug(store, page.path);\n for (const related of page.frontmatter.related) {\n const target = bySlug.get(normalizeSlug(related));\n if (!target) continue; // unknown reference — ignore silently\n if (!target.frontmatter.related.map(normalizeSlug).includes(mySlug)) {\n target.frontmatter.related = [...target.frontmatter.related, mySlug];\n mutated.add(toWikiSlug(store, target.path));\n }\n }\n }\n\n return pages.filter((p) => mutated.has(toWikiSlug(store, p.path)));\n}\n\n/**\n * Strip leading slashes / .md suffix for comparison. Accepts both\n * `concepts/foo` and `concepts/foo.md`.\n */\nexport function normalizeSlug(s: string): string {\n return s.trim().replace(/^\\/+/, \"\").replace(/\\.md$/i, \"\");\n}\n\n/**\n * Given a markdown body, extract `[[wiki-link]]` style references\n * and return the set of unique slugs.\n */\nexport function extractWikiLinks(body: string): string[] {\n const pattern = /\\[\\[([^\\]]+)\\]\\]/g;\n const out = new Set<string>();\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(body)) !== null) {\n const raw = m[1];\n if (raw) out.add(normalizeSlug(raw));\n }\n return [...out];\n}\n","/**\n * Knowledge health scoring and report generation.\n *\n * Every wiki page gets a health score (0-100) based on objective factors:\n * staleness, source availability, link health, duplicate risk, and\n * contradiction risk. The report also includes findings (stale pages,\n * broken links, orphans, duplicates, missing backlinks).\n *\n * All detection passes in this module are pure computation + file I/O —\n * no LLM calls. Contradiction detection requires the LLM and is handled\n * separately via the heal handlers when `health.detect_contradictions`\n * is enabled.\n */\nimport { existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport type { ProvenanceRecord, WotwConfig } from \"../utils/types.js\";\nimport { extractWikiLinks, normalizeSlug, toWikiSlug } from \"./cross-reference.js\";\nimport type { WikiSearch } from \"./search.js\";\nimport type { WikiStore } from \"./store.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type FindingKind =\n | \"stale\"\n | \"duplicate\"\n | \"contradiction\"\n | \"orphan\"\n | \"broken-link\"\n | \"missing-backlink\"\n | \"consolidation\";\n\nexport interface HealthFinding {\n /** Stable identifier, e.g. \"stale:concepts/old-page\". */\n id: string;\n kind: FindingKind;\n severity: \"high\" | \"medium\" | \"low\";\n /** Wiki-relative paths involved. */\n pages: string[];\n description: string;\n /** Can the LLM fix this without human review? */\n autoFixable: boolean;\n}\n\nexport interface PageHealthScore {\n /** Wiki-relative path. */\n page: string;\n score: number;\n factors: {\n staleness: number;\n sourceAvailability: number;\n linkHealth: number;\n duplicateRisk: number;\n contradictionRisk: number;\n };\n}\n\nexport interface HealthReportSummary {\n total: number;\n high: number;\n medium: number;\n low: number;\n autoFixable: number;\n}\n\nexport interface HealthReport {\n timestamp: string;\n findings: HealthFinding[];\n scores: PageHealthScore[];\n summary: HealthReportSummary;\n}\n\nexport interface HealthReportOptions {\n config: WotwConfig;\n /** Pre-loaded provenance records (avoids re-reading the chain file). */\n provenanceRecords?: ProvenanceRecord[];\n}\n\n// ---------------------------------------------------------------------------\n// Staleness scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute staleness score for a single page based on the most recent ingest\n * record that wrote to it.\n */\nexport function computeStaleness(\n pageRelPath: string,\n records: ProvenanceRecord[],\n thresholds: number[],\n scores: number[],\n now: Date = new Date(),\n): number {\n // Find most recent \"ingest\" or \"heal\" record that wrote this page.\n let latestTs: number | null = null;\n for (const r of records) {\n if (r.type !== \"ingest\" && r.type !== \"heal\" && r.type !== \"compound\") continue;\n if (!r.wiki_files_written.includes(pageRelPath)) continue;\n const ts = Date.parse(r.timestamp);\n if (!isNaN(ts) && (latestTs === null || ts > latestTs)) {\n latestTs = ts;\n }\n }\n if (latestTs === null) return scores[scores.length - 1] ?? 0;\n const ageDays = (now.getTime() - latestTs) / (1000 * 60 * 60 * 24);\n for (let i = 0; i < thresholds.length; i++) {\n if (ageDays <= thresholds[i]!) return scores[i] ?? 100;\n }\n return scores[scores.length - 1] ?? 0;\n}\n\n// ---------------------------------------------------------------------------\n// Source availability scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute source availability for a page. Checks whether the raw source\n * files that produced it still exist on disk.\n */\nexport function computeSourceAvailability(\n pageRelPath: string,\n records: ProvenanceRecord[],\n wikiRoot: string,\n isOrphaned: boolean,\n): number {\n if (isOrphaned) return 0;\n // Collect all source files from ingest records that wrote this page.\n const sources = new Set<string>();\n for (const r of records) {\n if (r.type !== \"ingest\") continue;\n if (!r.wiki_files_written.includes(pageRelPath)) continue;\n for (const s of r.source_files) sources.add(s);\n }\n if (sources.size === 0) return 100; // no known provenance — don't penalize\n let exists = 0;\n for (const s of sources) {\n const abs = join(wikiRoot, s);\n if (existsSync(abs)) exists += 1;\n }\n return Math.round((exists / sources.size) * 100);\n}\n\n// ---------------------------------------------------------------------------\n// Link health scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute link health for a page based on the ratio of valid wikilinks.\n */\nexport function computeLinkHealth(\n body: string,\n allPageSlugs: Set<string>,\n): { score: number; broken: string[] } {\n const links = extractWikiLinks(body);\n if (links.length === 0) return { score: 100, broken: [] };\n const broken: string[] = [];\n for (const link of links) {\n if (!allPageSlugs.has(normalizeSlug(link))) {\n broken.push(link);\n }\n }\n const valid = links.length - broken.length;\n return {\n score: Math.round((valid / links.length) * 100),\n broken,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Duplicate risk scoring\n// ---------------------------------------------------------------------------\n\n/**\n * Compute duplicate risk for a page using the search index. Returns the\n * similarity score with the closest non-self match, mapped to a 0-100 scale.\n */\nexport function computeDuplicateRisk(\n _pageRelPath: string,\n title: string,\n tags: string[],\n _bodyPrefix: string,\n search: WikiSearch,\n selfAbsPath: string,\n): number {\n const query = [title, ...tags.slice(0, 3)].join(\" \");\n if (!query.trim()) return 0;\n const hits = search.search(query, 5);\n // Find the best non-self match.\n let bestScore = 0;\n for (const hit of hits) {\n if (hit.path === selfAbsPath) continue;\n if (hit.score > bestScore) bestScore = hit.score;\n }\n // Normalize minisearch score to 0-100 duplicate risk.\n // MiniSearch scores are unbounded; we use heuristic thresholds.\n // A minisearch score above 20 usually means very similar content.\n const normalized = Math.min(100, Math.round((bestScore / 25) * 100));\n if (normalized < 30) return 0;\n if (normalized <= 60) return Math.round(((normalized - 30) / 30) * 50);\n if (normalized <= 80) return Math.round(50 + ((normalized - 60) / 20) * 30);\n return Math.round(80 + ((normalized - 80) / 20) * 20);\n}\n\n// ---------------------------------------------------------------------------\n// Overall score\n// ---------------------------------------------------------------------------\n\nexport function computeWeightedScore(\n factors: PageHealthScore[\"factors\"],\n weights: WotwConfig[\"health\"][\"weights\"],\n): number {\n // Higher factor values are better (100 = perfect). The overall score\n // is the weighted average, EXCEPT for duplicate risk and contradiction\n // risk which are inverted (0 = no risk = healthy).\n const invDup = 100 - factors.duplicateRisk;\n const invContra = 100 - factors.contradictionRisk;\n const raw =\n factors.staleness * weights.staleness +\n factors.sourceAvailability * weights.source_availability +\n factors.linkHealth * weights.link_health +\n invDup * weights.duplicate_risk +\n invContra * weights.contradiction_risk;\n return Math.round(Math.max(0, Math.min(100, raw)));\n}\n\n// ---------------------------------------------------------------------------\n// Full page health score\n// ---------------------------------------------------------------------------\n\nexport function computePageHealthScore(\n pageAbsPath: string,\n _store: WikiStore,\n records: ProvenanceRecord[],\n search: WikiSearch,\n allPageSlugs: Set<string>,\n config: WotwConfig,\n body: string,\n title: string,\n tags: string[],\n isOrphaned: boolean,\n): PageHealthScore {\n const wikiRoot = config.wiki_root;\n const pageRelPath = relative(wikiRoot, pageAbsPath);\n\n const staleness = computeStaleness(\n pageRelPath,\n records,\n config.health.staleness_thresholds,\n config.health.staleness_scores,\n );\n const sourceAvailability = computeSourceAvailability(pageRelPath, records, wikiRoot, isOrphaned);\n const { score: linkHealth } = computeLinkHealth(body, allPageSlugs);\n const duplicateRisk = computeDuplicateRisk(\n pageRelPath,\n title,\n tags,\n body.slice(0, 200),\n search,\n pageAbsPath,\n );\n\n const factors: PageHealthScore[\"factors\"] = {\n staleness,\n sourceAvailability,\n linkHealth,\n duplicateRisk,\n contradictionRisk: 0, // populated by contradiction detection pass\n };\n\n return {\n page: relative(wikiRoot, pageAbsPath),\n score: computeWeightedScore(factors, config.health.weights),\n factors,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Deduplication detection\n// ---------------------------------------------------------------------------\n\nexport interface DuplicateGroup {\n pages: string[];\n}\n\n/**\n * Find transitive duplicate groups from pairwise duplicate findings.\n * Uses union-find for efficiency.\n */\nexport function groupDuplicates(pairs: Array<[string, string]>): DuplicateGroup[] {\n const parent = new Map<string, string>();\n const find = (x: string): string => {\n let p = parent.get(x) ?? x;\n if (p !== x) {\n p = find(p);\n parent.set(x, p);\n }\n return p;\n };\n const union = (a: string, b: string): void => {\n const ra = find(a);\n const rb = find(b);\n if (ra !== rb) parent.set(ra, rb);\n };\n\n for (const [a, b] of pairs) {\n parent.set(a, parent.get(a) ?? a);\n parent.set(b, parent.get(b) ?? b);\n union(a, b);\n }\n\n const groups = new Map<string, Set<string>>();\n for (const node of parent.keys()) {\n const root = find(node);\n if (!groups.has(root)) groups.set(root, new Set());\n groups.get(root)!.add(node);\n }\n\n return [...groups.values()].filter((s) => s.size > 1).map((s) => ({ pages: [...s].sort() }));\n}\n\n// ---------------------------------------------------------------------------\n// Knowledge consolidation detection\n// ---------------------------------------------------------------------------\n\nexport interface ConsolidationGroup {\n /** Wiki-relative paths of pages in this topical cluster. */\n pages: string[];\n /** Shared topic description derived from common tags. */\n topic: string;\n /** Suggested title for the consolidated page. */\n suggestedTitle: string;\n}\n\n/**\n * Detect topic clusters that have accumulated too many pages and would\n * benefit from consolidation into a single authoritative page.\n *\n * Uses two signals:\n * 1. Union-find grouping at a lower similarity threshold than dedup (40 vs 60).\n * 2. Tag clustering — groups sharing the same primary tag.\n *\n * Groups with more than `consolidation_threshold` pages are returned.\n */\nexport function detectConsolidationCandidates(\n store: WikiStore,\n search: WikiSearch,\n config: WotwConfig,\n): ConsolidationGroup[] {\n if (!config.health.consolidation_enabled) return [];\n const threshold = config.health.consolidation_threshold;\n const allPaths = store.listAll();\n const wikiRoot = config.wiki_root;\n\n // ---- Signal 1: similarity-based grouping at lower threshold ----\n const simPairs: Array<[string, string]> = [];\n for (const absPath of allPaths) {\n const doc = search.search(\n // Use a broad query by path-slug to find related pages.\n absPath.split(\"/\").pop()?.replace(/\\.md$/, \"\").replace(/[-_]/g, \" \") ?? \"\",\n 10,\n );\n const pageRel = relative(wikiRoot, absPath);\n for (const hit of doc) {\n if (hit.path === absPath) continue;\n // Normalize to 0-100 risk scale (same as computeDuplicateRisk).\n const normalized = Math.min(100, Math.round((hit.score / 25) * 100));\n if (normalized >= 40) {\n const hitRel = relative(wikiRoot, hit.path);\n const key = [pageRel, hitRel].sort().join(\"||\");\n // Deduplicate pairs.\n if (!simPairs.some(([a, b]) => [a, b].sort().join(\"||\") === key)) {\n simPairs.push([pageRel, hitRel]);\n }\n }\n }\n }\n\n const simGroups = groupDuplicates(simPairs)\n .filter((g) => g.pages.length > threshold)\n .map((g) => ({\n pages: g.pages,\n topic: g.pages.map((p) => p.split(\"/\").pop()?.replace(/\\.md$/, \"\")).join(\", \"),\n suggestedTitle: `Consolidated: ${g.pages[0]?.split(\"/\").pop()?.replace(/\\.md$/, \"\").replace(/[-_]/g, \" \") ?? \"topic\"}`,\n }));\n\n // ---- Signal 2: tag clustering ----\n const tagGroups = new Map<string, string[]>();\n for (const absPath of allPaths) {\n const pageRel = relative(wikiRoot, absPath);\n // Quick tag peek from the index doc — reconstruct from search.\n const hits = search.search(absPath.split(\"/\").pop()?.replace(/\\.md$/, \"\") ?? \"\", 1);\n if (hits.length > 0 && hits[0]!.path === absPath) {\n // We need the tags from the actual page. Use store for accuracy.\n // But we want to keep this lightweight. Use relative path.\n // For now just use path-based inference; actual tag grouping\n // would need page reads. Defer to the sim-groups signal.\n }\n // Group by category directory as a lightweight proxy.\n const parts = pageRel.split(\"/\");\n if (parts.length >= 2) {\n const catDir = parts.slice(0, -1).join(\"/\");\n if (!tagGroups.has(catDir)) tagGroups.set(catDir, []);\n tagGroups.get(catDir)!.push(pageRel);\n }\n }\n\n // Merge sim-groups with tag groups that exceed threshold.\n // Tag groups alone are too coarse (entire categories), so only use sim-groups.\n const seen = new Set<string>();\n const result: ConsolidationGroup[] = [];\n for (const g of simGroups) {\n const key = g.pages.sort().join(\"||\");\n if (!seen.has(key)) {\n seen.add(key);\n result.push(g);\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Missing backlink detection\n// ---------------------------------------------------------------------------\n\nfunction detectMissingBacklinks(\n store: WikiStore,\n allPages: Array<{ absPath: string; related: string[] }>,\n): HealthFinding[] {\n const findings: HealthFinding[] = [];\n const bySlug = new Map<string, string>(); // slug → abs path\n for (const p of allPages) {\n bySlug.set(toWikiSlug(store, p.absPath), p.absPath);\n }\n const relatedBySlug = new Map<string, Set<string>>();\n for (const p of allPages) {\n const slug = toWikiSlug(store, p.absPath);\n relatedBySlug.set(slug, new Set(p.related.map(normalizeSlug)));\n }\n\n for (const p of allPages) {\n const mySlug = toWikiSlug(store, p.absPath);\n for (const related of p.related) {\n const targetSlug = normalizeSlug(related);\n const targetRels = relatedBySlug.get(targetSlug);\n if (targetRels && !targetRels.has(mySlug)) {\n const pageRel = relative(store.wikiRoot, p.absPath);\n const targetAbs = bySlug.get(targetSlug);\n const targetRel = targetAbs ? relative(store.wikiRoot, targetAbs) : targetSlug;\n const fid = `missing-backlink:${pageRel}->${targetRel}`;\n findings.push({\n id: fid,\n kind: \"missing-backlink\",\n severity: \"low\",\n pages: [pageRel, targetRel],\n description: `${pageRel} references ${targetRel} but ${targetRel} does not link back.`,\n autoFixable: true,\n });\n }\n }\n }\n return findings;\n}\n\n// ---------------------------------------------------------------------------\n// Full health report\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a complete health report for the wiki. Runs all detection passes\n * (staleness, source availability, link health, duplicates, orphans, broken\n * links, missing backlinks). Does NOT run LLM — contradiction detection\n * requires a separate LLM pass.\n */\nexport async function computeHealthReport(\n store: WikiStore,\n chain: ProvenanceChain | null,\n search: WikiSearch,\n opts: HealthReportOptions,\n): Promise<HealthReport> {\n const config = opts.config;\n const records = opts.provenanceRecords ?? (chain ? await chain.readAll() : []);\n const allAbsPaths = store.listAll();\n const findings: HealthFinding[] = [];\n const scores: PageHealthScore[] = [];\n\n // Build a set of all valid wiki slugs for link health checking.\n const allPageSlugs = new Set<string>();\n for (const p of allAbsPaths) {\n allPageSlugs.add(toWikiSlug(store, p));\n }\n\n // Page data for backlink detection.\n const pageData: Array<{ absPath: string; related: string[] }> = [];\n // Track duplicate pairs.\n const dupPairs: Array<[string, string]> = [];\n const seenDupPairKeys = new Set<string>();\n\n for (const absPath of allAbsPaths) {\n const page = await store.readPage(absPath);\n if (!page) continue;\n\n const pageRel = relative(config.wiki_root, absPath);\n const isOrphaned = page.frontmatter.status === \"orphaned\";\n const isMerged = page.frontmatter.status === \"merged\";\n const isConsolidated = page.frontmatter.status === \"consolidated\";\n\n // Skip merged and consolidated pages from scoring.\n if (isMerged || isConsolidated) continue;\n\n pageData.push({ absPath, related: page.frontmatter.related });\n\n // Compute health score.\n const score = computePageHealthScore(\n absPath,\n store,\n records,\n search,\n allPageSlugs,\n config,\n page.body,\n page.frontmatter.title,\n page.frontmatter.tags,\n isOrphaned,\n );\n scores.push(score);\n\n // --- Findings ---\n\n // Orphan finding.\n if (isOrphaned) {\n findings.push({\n id: `orphan:${pageRel}`,\n kind: \"orphan\",\n severity: \"medium\",\n pages: [pageRel],\n description: `Source files deleted; page retained with status: orphaned.`,\n autoFixable: false,\n });\n }\n\n // Stale finding.\n if (score.factors.staleness < config.health.auto_fix_staleness_below && !isOrphaned) {\n findings.push({\n id: `stale:${pageRel}`,\n kind: \"stale\",\n severity: score.factors.staleness <= 20 ? \"high\" : \"medium\",\n pages: [pageRel],\n description: `Page staleness score is ${score.factors.staleness} (below threshold ${config.health.auto_fix_staleness_below}).`,\n autoFixable: true,\n });\n }\n\n // Broken link finding.\n const { broken } = computeLinkHealth(page.body, allPageSlugs);\n if (broken.length > 0) {\n findings.push({\n id: `broken-link:${pageRel}`,\n kind: \"broken-link\",\n severity: broken.length > 3 ? \"high\" : \"medium\",\n pages: [pageRel],\n description: `${broken.length} broken wikilink(s): ${broken.slice(0, 5).join(\", \")}${broken.length > 5 ? \"...\" : \"\"}.`,\n autoFixable: true,\n });\n }\n\n // Duplicate detection: check if this page's duplicate risk is above threshold.\n if (score.factors.duplicateRisk >= config.health.duplicate_threshold) {\n // Find the closest match to build the pair.\n const query = [page.frontmatter.title, ...page.frontmatter.tags.slice(0, 3)].join(\" \");\n const hits = search.search(query, 5);\n for (const hit of hits) {\n if (hit.path === absPath) continue;\n const hitRel = relative(config.wiki_root, hit.path);\n const pairKey = [pageRel, hitRel].sort().join(\"||\");\n if (!seenDupPairKeys.has(pairKey)) {\n seenDupPairKeys.add(pairKey);\n dupPairs.push([pageRel, hitRel]);\n }\n break; // only add closest match\n }\n }\n }\n\n // Group transitive duplicates and create findings.\n const dupGroups = groupDuplicates(dupPairs);\n for (const group of dupGroups) {\n findings.push({\n id: `duplicate:${group.pages.join(\"+\")}`,\n kind: \"duplicate\",\n severity: \"medium\",\n pages: group.pages,\n description: `${group.pages.length} pages appear to cover the same topic.`,\n autoFixable: true,\n });\n }\n\n // Missing backlinks.\n const backlinkFindings = detectMissingBacklinks(store, pageData);\n findings.push(...backlinkFindings);\n\n // Consolidation candidates.\n const consolidationGroups = detectConsolidationCandidates(store, search, config);\n for (const group of consolidationGroups) {\n findings.push({\n id: `consolidation:${group.pages.join(\"+\")}`,\n kind: \"consolidation\",\n severity: \"low\",\n pages: group.pages,\n description: `${group.pages.length} pages cover the topic area \"${group.topic}\" and could be consolidated.`,\n autoFixable: true,\n });\n }\n\n // Summary.\n const summary: HealthReportSummary = {\n total: findings.length,\n high: findings.filter((f) => f.severity === \"high\").length,\n medium: findings.filter((f) => f.severity === \"medium\").length,\n low: findings.filter((f) => f.severity === \"low\").length,\n autoFixable: findings.filter((f) => f.autoFixable).length,\n };\n\n return {\n timestamp: new Date().toISOString(),\n findings,\n scores,\n summary,\n };\n}\n","/**\n * Query metrics — compute zero-hit rate and track query outcomes.\n *\n * The query log is an append-only JSONL file recording every query and\n * whether it returned zero results. Used for vocabulary enrichment triggers.\n */\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { ensureDirSync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\n\nexport interface QueryLogEntry {\n timestamp: string;\n query: string;\n zero_hit: boolean;\n citations: number;\n}\n\nexport interface ZeroHitMetrics {\n total_queries: number;\n zero_hits: number;\n zero_hit_rate: number;\n recent_zero_hit_queries: string[];\n}\n\n/**\n * Append a query outcome to the query log. Never throws — sink failures\n * are logged and swallowed to avoid breaking the query path.\n */\nexport function recordQueryOutcome(logFile: string, query: string, citationCount: number): void {\n if (!logFile) return;\n const log = getLogger(\"query-metrics\");\n const entry: QueryLogEntry = {\n timestamp: new Date().toISOString(),\n query,\n zero_hit: citationCount === 0,\n citations: citationCount,\n };\n try {\n ensureDirSync(dirname(logFile));\n appendFileSync(logFile, JSON.stringify(entry) + \"\\n\", \"utf8\");\n } catch (err) {\n log.warn({ err }, \"failed to write query log entry\");\n }\n}\n\n/**\n * Compute zero-hit rate from the query log within a time window.\n */\nexport function computeZeroHitRate(logFile: string, windowDays = 7): ZeroHitMetrics {\n const empty: ZeroHitMetrics = {\n total_queries: 0,\n zero_hits: 0,\n zero_hit_rate: 0,\n recent_zero_hit_queries: [],\n };\n\n if (!logFile || !existsSync(logFile)) return empty;\n\n let raw: string;\n try {\n raw = readFileSync(logFile, \"utf8\");\n } catch {\n return empty;\n }\n\n const cutoff = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1000).toISOString();\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n\n let total = 0;\n let zeroHits = 0;\n const zeroHitQueries: string[] = [];\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line) as QueryLogEntry;\n if (entry.timestamp < cutoff) continue;\n total++;\n if (entry.zero_hit) {\n zeroHits++;\n zeroHitQueries.push(entry.query);\n }\n } catch {\n // Skip malformed lines.\n }\n }\n\n return {\n total_queries: total,\n zero_hits: zeroHits,\n zero_hit_rate: total > 0 ? zeroHits / total : 0,\n recent_zero_hit_queries: zeroHitQueries,\n };\n}\n","/**\n * Hashing utilities used by the provenance chain and other subsystems.\n * Hashes are SHA-256 over canonical JSON (recursively sorted keys, no\n * whitespace, UTF-8). The canonical form is deterministic across machines\n * and language runtimes so verification does not depend on JSON.stringify\n * key-order quirks.\n *\n * This module is the single source of truth for hashing in `wotw` —\n * `src/utils/hash.ts` was previously a second copy with overlapping and\n * subtly-different sync/async signatures for `sha256File`. That duplication\n * has been removed (L-DUP-1). Prefer named exports from this module\n * (`sha256`/`sha256Hex`, `sha256File`, `sha256FileSync`, `canonicalJson`,\n * `sha256Canonical`) throughout the codebase.\n */\nimport { createHash } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\n\n/** Special value used as `previous_chain_hash` for the first record in a chain. */\nexport const GENESIS_HASH = \"0\".repeat(64);\n\n/**\n * Produce a canonical JSON string for any JSON-serializable input.\n * Keys in every object are sorted lexicographically, recursively.\n */\nexport function canonicalJson(value: unknown): string {\n const normalize = (v: unknown): unknown => {\n if (v === null || typeof v !== \"object\") return v;\n if (Array.isArray(v)) return v.map(normalize);\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(v as Record<string, unknown>).sort()) {\n sorted[key] = normalize((v as Record<string, unknown>)[key]);\n }\n return sorted;\n };\n return JSON.stringify(normalize(value));\n}\n\n/** SHA-256 of a string or buffer, returned as hex. */\nexport function sha256Hex(input: string | Buffer): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\n/**\n * Alias for {@link sha256Hex}. Kept for ergonomic call sites\n * (`sha256(contents)` reads more naturally than `sha256Hex(contents)` in\n * non-provenance code) and for the stable public API in `src/index.ts`.\n */\nexport const sha256 = sha256Hex;\n\n/** SHA-256 of the canonical JSON form of a value. */\nexport function sha256Canonical(value: unknown): string {\n return sha256Hex(canonicalJson(value));\n}\n\n/** Alias for {@link sha256Canonical} — hashes the canonical JSON of a value. */\nexport const sha256Json = sha256Canonical;\n\n/** Alias for {@link canonicalJson} — kept for the stable public API. */\nexport const stableStringify = canonicalJson;\n\n/**\n * Synchronous SHA-256 of a file on disk. Reads the whole file into memory,\n * which is fine for wiki pages. Throws if the file does not exist — callers\n * that need ENOENT tolerance should use the async {@link sha256File} which\n * returns null. Used by the watcher's in-memory event classifier where\n * synchronous semantics simplify the seed flow.\n */\nexport function sha256FileSync(filePath: string): string {\n return sha256Hex(readFileSync(filePath));\n}\n\n/**\n * SHA-256 of a file on disk. Returns null if the file does not exist.\n * Reads the whole file into memory — fine for wiki pages which are small.\n */\nexport async function sha256File(path: string): Promise<string | null> {\n try {\n const buf = await readFile(path);\n return sha256Hex(buf);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n\n/**\n * Hash many files in parallel. Missing files map to null entries.\n * Used when recording the post-ingestion state of the wiki.\n */\nexport async function sha256Files(paths: string[]): Promise<Record<string, string>> {\n const out: Record<string, string> = {};\n await Promise.all(\n paths.map(async (p) => {\n const h = await sha256File(p);\n if (h !== null) out[p] = h;\n }),\n );\n return out;\n}\n","/**\n * Cryptographic provenance chain. Every operation that mutates (or answers\n * from) the wiki appends a single record to an append-only JSONL file.\n * Records form a hash chain: each entry's `previous_chain_hash` matches the\n * prior entry's `chain_hash`, so any tampering with past entries invalidates\n * the tail of the chain.\n *\n * File layout: one JSON object per line, newline-separated, no trailing\n * newline when empty. We never rewrite or truncate the file — history is\n * write-once. Rotation, if ever needed, is a separate concern outside this\n * module.\n *\n * Concurrency: the daemon is single-writer (IngestionQueue has concurrency 1,\n * QueryEngine is called from the MCP request handler which is naturally\n * serialized by Node's single-threaded event loop). We serialize all chain\n * operations behind an in-process mutex to guard against interleaved\n * appends from multiple subsystems.\n */\nimport { open, readFile, stat } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { ProvenanceRecord, OperationType, ModelId } from \"../utils/types.js\";\nimport { ensureDir, fileExists } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport { GENESIS_HASH, canonicalJson, sha256Canonical, sha256Hex } from \"./hash.js\";\nimport type { KeyStore } from \"../keys/store.js\";\n\n/**\n * Optional sink invoked fire-and-forget after a JSONL append succeeds.\n * Used by the daemon to mirror records to wotw-cloud's Supabase replica.\n * Sink failures MUST NOT throw — JSONL is canonical, the sink is a\n * sync-replica for UI consumption. See cloud-sink.ts.\n */\nexport interface ProvenanceSink {\n append(record: ProvenanceRecord): Promise<boolean>;\n}\n\n/** Fields a caller must supply when appending a new record. */\nexport interface ProvenanceAppendInput {\n type: OperationType;\n source_files: string[];\n source_hashes: string[];\n prompt_hash: string;\n model_id: ModelId;\n response_hash: string;\n wiki_files_written: string[];\n wiki_file_hashes_after: Record<string, string>;\n metadata?: Record<string, string | number | boolean>;\n /**\n * Review item 43: tenant_id when running in hosted mode. Folded into\n * the canonical payload so a record from tenant A cannot be replayed\n * as a record of tenant B without recomputing id + chain_hash + hmac.\n */\n tenant_id?: string;\n /**\n * Pass B (fact extraction): fact_hash strings added by this operation.\n * Stored on the record but NOT folded into the canonical payload — they\n * are best-effort metadata, not cryptographically attested, so new\n * daemons emitting these fields produce records that verify identically\n * under older daemons that don't know about them.\n */\n fact_hashes_added?: string[];\n /** Pass B: fact_hash strings superseded by this operation. */\n fact_hashes_superseded?: string[];\n}\n\nexport interface VerificationError {\n seq: number;\n id: string;\n reason: string;\n}\n\nexport interface VerificationResult {\n ok: boolean;\n totalRecords: number;\n verifiedRecords: number;\n errors: VerificationError[];\n}\n\n/**\n * Append-only hash-chain of provenance records.\n *\n * Usage:\n * ```\n * const chain = new ProvenanceChain({ path: \"/path/to/chain.jsonl\" });\n * await chain.init();\n * await chain.append({ ... });\n * const result = await chain.verify();\n * ```\n */\nexport class ProvenanceChain {\n readonly path: string;\n private nextSeq: number;\n private lastChainHash: string;\n private lastId: string | null;\n private totalRecords: number;\n private initialized: boolean;\n /** Promise that serializes all append operations. */\n private writeLock: Promise<void>;\n /** Optional sync-replica sink (e.g., wotw-cloud Supabase). Fire-and-forget. */\n private sink: ProvenanceSink | null;\n /**\n * Review item 43: tenant_id folded into the canonical payload when set.\n * In hosted mode the daemon passes config.hosted.tenant_id; in interactive\n * mode this stays undefined and tenant_id is omitted from the payload\n * (backwards-compat — chains predating this change verify identically).\n */\n private readonly tenantId: string | undefined;\n /**\n * G5 closure (Pass 018, v0.8.2): workspace key store. When set, every\n * append signs the record's HMAC with the workspace's currently active\n * DEK and stamps `key_id` on the record so the verifier can look up\n * the right DEK after rotation. When null, falls back to the\n * single-key 4-tier resolution below (backwards-compat for chains\n * predating G5 closure).\n */\n private readonly keyStore: KeyStore | null;\n /** Workspace id used to look up DEKs in the key store. Defaults to tenantId. */\n private readonly workspaceId: string | undefined;\n /**\n * Review item 42 (backward-compat path): single-key fallback. Resolved\n * at construction when no `keyStore` is provided:\n * 1. Explicit opts.hmacKey\n * 2. process.env.WOTW_PROVENANCE_HMAC_KEY\n * 3. Derived from tenant_id (sha256(\"wotw-provenance-v1\", tenant_id))\n * 4. undefined → no HMAC field on records (pre-G5)\n * Used by the verifier to validate records that have an `hmac` field\n * but no `key_id` (i.e., produced by a pre-v0.8.2 daemon).\n */\n private readonly hmacKey: string | undefined;\n\n constructor(opts: {\n path: string;\n sink?: ProvenanceSink | null;\n tenantId?: string;\n workspaceId?: string;\n keyStore?: KeyStore | null;\n hmacKey?: string;\n }) {\n this.path = opts.path;\n this.nextSeq = 1;\n this.lastChainHash = GENESIS_HASH;\n this.lastId = null;\n this.totalRecords = 0;\n this.initialized = false;\n this.writeLock = Promise.resolve();\n this.sink = opts.sink ?? null;\n this.tenantId = opts.tenantId;\n this.keyStore = opts.keyStore ?? null;\n this.workspaceId = opts.workspaceId ?? opts.tenantId;\n // Resolve fallback HMAC key per the documented preference order. Used\n // both as the signing key when no keyStore is provided, AND as the\n // backward-compat verification key for records lacking a key_id\n // (produced by a pre-v0.8.2 daemon).\n if (opts.hmacKey) {\n this.hmacKey = opts.hmacKey;\n } else if (process.env.WOTW_PROVENANCE_HMAC_KEY) {\n this.hmacKey = process.env.WOTW_PROVENANCE_HMAC_KEY;\n } else if (opts.tenantId) {\n this.hmacKey = createHmac(\"sha256\", \"wotw-provenance-v1\").update(opts.tenantId).digest(\"hex\");\n } else {\n this.hmacKey = undefined;\n }\n }\n\n /**\n * Prepare the chain for use. Creates the parent directory and reads the\n * tail of the file to recover the next sequence number and the last chain\n * hash. Safe to call multiple times.\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n await ensureDir(dirname(this.path));\n if (!fileExists(this.path)) {\n // Touch an empty file so downstream `stat` calls succeed.\n const handle = await open(this.path, \"a\");\n await handle.close();\n this.initialized = true;\n return;\n }\n const records = await this.readAll();\n // Corruption detection: if the file has content but all records failed to parse,\n // the chain is corrupted and continuing would silently reset to genesis.\n if (records.length === 0) {\n const { stat: fsStat } = await import(\"node:fs/promises\");\n try {\n const st = await fsStat(this.path);\n if (st.size > 0) {\n throw new Error(\n \"provenance chain file exists but contains no valid records — file may be corrupted\",\n );\n }\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"provenance chain file exists\")) throw err;\n // stat failure — file may have been deleted between exists check and stat\n }\n }\n this.totalRecords = records.length;\n if (records.length > 0) {\n const last = records[records.length - 1]!;\n // Review item 38: verify the tail record's own id + chain_hash\n // before adopting it. Pre-fix, init() read seq/chain_hash/id\n // without verifying — a tampered tail would propagate forward.\n // Always-on minimum-bar verify of just the tail; full chain\n // verify is gated by config.provenance.verify_on_startup (item 37).\n const tailPayload: Record<string, unknown> = {\n seq: last.seq,\n timestamp: last.timestamp,\n type: last.type,\n source_files: last.source_files,\n source_hashes: last.source_hashes,\n prompt_hash: last.prompt_hash,\n model_id: last.model_id,\n response_hash: last.response_hash,\n wiki_files_written: last.wiki_files_written,\n wiki_file_hashes_after: last.wiki_file_hashes_after,\n previous_id: last.previous_id,\n previous_chain_hash: last.previous_chain_hash,\n };\n if (last.metadata !== undefined) tailPayload.metadata = last.metadata;\n if (last.tenant_id !== undefined) tailPayload.tenant_id = last.tenant_id;\n const recomputedId = sha256Canonical(tailPayload);\n if (recomputedId !== last.id) {\n throw new Error(\n `provenance tail self-inconsistent: stored id=${last.id} but recomputed id=${recomputedId}. Refuse to continue.`,\n );\n }\n const recomputedChainHash = sha256Hex(`${last.previous_chain_hash}${last.id}`);\n if (recomputedChainHash !== last.chain_hash) {\n throw new Error(\n `provenance tail chain_hash inconsistent: stored=${last.chain_hash} recomputed=${recomputedChainHash}. Refuse to continue.`,\n );\n }\n // Tail HMAC verify (G5 closure, Pass 018). The tail is the most\n // likely place for tampering (an attacker who can't recompute the\n // whole chain at least tries to replace just the last record).\n // Check matches verify()'s logic — see verifyHmac().\n const hmacError = this.verifyHmac(last);\n if (hmacError) {\n throw new Error(\n `provenance tail hmac verification failed: ${hmacError.reason}. Refuse to continue.`,\n );\n }\n this.nextSeq = last.seq + 1;\n this.lastChainHash = last.chain_hash;\n this.lastId = last.id;\n }\n this.initialized = true;\n }\n\n /**\n * Append a new record to the chain. Computes `seq`, `previous_id`,\n * `previous_chain_hash`, `id` and `chain_hash` for the caller.\n *\n * Returns the fully-populated record that was written to disk.\n */\n async append(input: ProvenanceAppendInput): Promise<ProvenanceRecord> {\n if (!this.initialized) await this.init();\n const log = getLogger(\"provenance\");\n\n // Serialize through the write lock so concurrent callers cannot\n // interleave and corrupt the hash chain.\n const release = await this.acquireLock();\n try {\n const seq = this.nextSeq;\n const previousId = this.lastId;\n const previousChainHash = this.lastChainHash;\n const timestamp = new Date().toISOString();\n\n // Build the payload whose hash becomes both `id` and part of `chain_hash`.\n // Review item 43: include tenant_id when present so cross-tenant\n // record confusion is detectable (record from tenant A cannot\n // match a verifier expecting tenant B without recomputing id).\n const effectiveTenantId = input.tenant_id ?? this.tenantId;\n const payload: Record<string, unknown> = {\n seq,\n timestamp,\n type: input.type,\n source_files: input.source_files,\n source_hashes: input.source_hashes,\n prompt_hash: input.prompt_hash,\n model_id: input.model_id,\n response_hash: input.response_hash,\n wiki_files_written: input.wiki_files_written,\n wiki_file_hashes_after: input.wiki_file_hashes_after,\n previous_id: previousId,\n previous_chain_hash: previousChainHash,\n ...(input.metadata ? { metadata: input.metadata } : {}),\n ...(effectiveTenantId ? { tenant_id: effectiveTenantId } : {}),\n };\n // The record id is the SHA-256 of the canonical payload. Deterministic\n // and content-addressable: identical operations get the same id.\n const id = sha256Canonical(payload);\n // The chain hash is SHA-256 of `previous_chain_hash + id`. Hashing the\n // id (rather than the whole payload twice) keeps verification cheap.\n const chainHash = sha256Hex(previousChainHash + id);\n // HMAC over `id || chain_hash`. Two paths:\n // 1. G5-closed (Pass 018, v0.8.2+): keyStore is set + workspaceId\n // resolves to an active DEK. Sign with the DEK, stamp key_id.\n // Verifier looks up the same DEK via key_id even after rotation.\n // 2. Backward-compat (G5-scaffolding or pre-G5): keyStore null,\n // use the single-key fallback resolved at construction. No\n // key_id on the record. Verifier falls back to the same\n // single-key resolution at verify time.\n let hmac: string | undefined;\n let keyId: string | undefined;\n if (this.keyStore && this.workspaceId) {\n const resolved = this.keyStore.active(this.workspaceId);\n if (resolved) {\n hmac = createHmac(\"sha256\", resolved.dek).update(`${id}|${chainHash}`).digest(\"hex\");\n keyId = resolved.key_id;\n }\n }\n if (!hmac && this.hmacKey) {\n hmac = createHmac(\"sha256\", this.hmacKey).update(`${id}|${chainHash}`).digest(\"hex\");\n }\n\n const record: ProvenanceRecord = {\n id,\n seq,\n timestamp,\n type: input.type,\n source_files: input.source_files,\n source_hashes: input.source_hashes,\n prompt_hash: input.prompt_hash,\n model_id: input.model_id,\n response_hash: input.response_hash,\n wiki_files_written: input.wiki_files_written,\n wiki_file_hashes_after: input.wiki_file_hashes_after,\n previous_id: previousId,\n previous_chain_hash: previousChainHash,\n chain_hash: chainHash,\n ...(input.metadata ? { metadata: input.metadata } : {}),\n ...(effectiveTenantId ? { tenant_id: effectiveTenantId } : {}),\n ...(hmac ? { hmac } : {}),\n ...(keyId ? { key_id: keyId } : {}),\n ...(input.fact_hashes_added && input.fact_hashes_added.length > 0\n ? { fact_hashes_added: input.fact_hashes_added }\n : {}),\n ...(input.fact_hashes_superseded && input.fact_hashes_superseded.length > 0\n ? { fact_hashes_superseded: input.fact_hashes_superseded }\n : {}),\n };\n\n // Append a single line. We open in append mode so multiple processes\n // on POSIX are reasonably safe (single-writer assumption still holds).\n const line = `${JSON.stringify(record)}\\n`;\n const handle = await open(this.path, \"a\");\n try {\n await handle.write(line);\n await handle.sync();\n } finally {\n await handle.close();\n }\n\n // Update in-memory state only after a successful write.\n this.nextSeq = seq + 1;\n this.lastChainHash = chainHash;\n this.lastId = id;\n this.totalRecords += 1;\n\n log.info(\n { seq, type: input.type, id: id.slice(0, 12), sources: input.source_files.length },\n \"provenance record appended\",\n );\n\n // Fire-and-forget cloud sync. JSONL is canonical; sink failures are\n // logged but never throw. Run outside the writeLock release path so\n // a slow sink doesn't serialize the daemon's append throughput.\n if (this.sink) {\n const sink = this.sink;\n void sink\n .append(record)\n .catch((err) =>\n log.warn(\n { seq, err: err instanceof Error ? err.message : String(err) },\n \"provenance sink unexpected error\",\n ),\n );\n }\n\n return record;\n } finally {\n release();\n }\n }\n\n /**\n * Walk the entire file and verify every record. Returns a detailed\n * result describing the first error encountered, if any.\n */\n async verify(): Promise<VerificationResult> {\n const records = await this.readAll();\n const errors: VerificationError[] = [];\n let prevChainHash = GENESIS_HASH;\n let prevId: string | null = null;\n let expectedSeq = 1;\n\n for (const r of records) {\n if (r.seq !== expectedSeq) {\n errors.push({\n seq: r.seq,\n id: r.id,\n reason: `seq mismatch: expected ${expectedSeq}, got ${r.seq}`,\n });\n }\n if (r.previous_id !== prevId) {\n errors.push({\n seq: r.seq,\n id: r.id,\n reason: `previous_id mismatch: expected ${prevId}, got ${r.previous_id}`,\n });\n }\n if (r.previous_chain_hash !== prevChainHash) {\n errors.push({\n seq: r.seq,\n id: r.id,\n reason: `previous_chain_hash mismatch: expected ${prevChainHash}, got ${r.previous_chain_hash}`,\n });\n }\n\n // Recompute id and chain_hash from the record's own content.\n // Canonical payload must EXACTLY match what append() built (lines\n // ~248-263), or recomputed id diverges. Same fields, same\n // conditional-inclusion of metadata + tenant_id. Fields that were\n // deliberately excluded by canonical-payload-exclusion (hmac,\n // key_id, fact_hashes_*) MUST stay excluded here too — that's how\n // forward/backward compat works.\n const payload: Record<string, unknown> = {\n seq: r.seq,\n timestamp: r.timestamp,\n type: r.type,\n source_files: r.source_files,\n source_hashes: r.source_hashes,\n prompt_hash: r.prompt_hash,\n model_id: r.model_id,\n response_hash: r.response_hash,\n wiki_files_written: r.wiki_files_written,\n wiki_file_hashes_after: r.wiki_file_hashes_after,\n previous_id: r.previous_id,\n previous_chain_hash: r.previous_chain_hash,\n };\n if (r.metadata !== undefined) payload.metadata = r.metadata;\n if (r.tenant_id !== undefined) payload.tenant_id = r.tenant_id;\n const expectedId = sha256Canonical(payload);\n if (expectedId !== r.id) {\n errors.push({\n seq: r.seq,\n id: r.id,\n reason: `id hash mismatch: expected ${expectedId}, got ${r.id}`,\n });\n }\n const expectedChainHash = sha256Hex(r.previous_chain_hash + r.id);\n if (expectedChainHash !== r.chain_hash) {\n errors.push({\n seq: r.seq,\n id: r.id,\n reason: `chain_hash mismatch: expected ${expectedChainHash}, got ${r.chain_hash}`,\n });\n }\n\n // HMAC verification (G5 closure, Pass 018). Two paths:\n // - key_id present → look up DEK via keyStore.resolveById(); the\n // resolved DEK could be in any state (active/rotating/archived/\n // revoked). Revoked records still verify cryptographically;\n // operators decide whether to trust them based on key_state.\n // - key_id absent, hmac present → backward-compat with the\n // single-key 4-tier resolution from the G5-scaffolding commit\n // `1875925`. Uses this.hmacKey.\n // - hmac absent → pre-G5 record, no check.\n if (r.hmac !== undefined) {\n const hmacError = this.verifyHmac(r);\n if (hmacError) errors.push(hmacError);\n }\n\n prevChainHash = r.chain_hash;\n prevId = r.id;\n expectedSeq = r.seq + 1;\n }\n\n return {\n ok: errors.length === 0,\n totalRecords: records.length,\n verifiedRecords: records.length - errors.length,\n errors,\n };\n }\n\n /** Read every record in the chain file. */\n async readAll(): Promise<ProvenanceRecord[]> {\n if (!fileExists(this.path)) return [];\n const text = await readFile(this.path, \"utf8\");\n const out: ProvenanceRecord[] = [];\n const lines = text.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (!line) continue;\n try {\n out.push(JSON.parse(line) as ProvenanceRecord);\n } catch (err) {\n getLogger(\"provenance\").warn({ line: i + 1, err }, \"skipping malformed chain line\");\n }\n }\n return out;\n }\n\n /** Return the last N records (most recent last). */\n async readRecent(limit: number): Promise<ProvenanceRecord[]> {\n const all = await this.readAll();\n if (limit >= all.length) return all;\n return all.slice(all.length - limit);\n }\n\n /**\n * Return every record that touched a specific wiki file, ordered oldest\n * to newest. A record \"touches\" a file if the file appears in\n * `wiki_files_written` or `source_files`.\n */\n async recordsFor(filePath: string): Promise<ProvenanceRecord[]> {\n const all = await this.readAll();\n return all.filter(\n (r) => r.wiki_files_written.includes(filePath) || r.source_files.includes(filePath),\n );\n }\n\n /** Total number of records in the chain. */\n count(): number {\n return this.totalRecords;\n }\n\n /** Current tail-of-chain hash (GENESIS_HASH if empty). */\n head(): string {\n return this.lastChainHash;\n }\n\n /** File size of the chain on disk in bytes, or 0 if missing. */\n async sizeBytes(): Promise<number> {\n try {\n const s = await stat(this.path);\n return s.size;\n } catch {\n return 0;\n }\n }\n\n /**\n * Compute a dump signature: SHA-256 over every record's canonical JSON\n * concatenated in-order. Useful as a compact fingerprint of the whole\n * chain for external comparison.\n */\n async signature(): Promise<string> {\n const records = await this.readAll();\n return sha256Hex(records.map((r) => canonicalJson(r)).join(\"\\n\"));\n }\n\n /**\n * Recompute the HMAC for a record and compare against the stored\n * value. Returns null on success, a VerificationError on mismatch.\n *\n * Key resolution mirrors append():\n * 1. record.key_id set + keyStore available → resolve DEK by key_id\n * across all states. Missing row OR keyStore-absent → error.\n * 2. record.key_id absent + this.hmacKey set → backward-compat,\n * use the single-key fallback.\n * 3. Neither → error (record claims hmac but verifier can't resolve a key).\n */\n private verifyHmac(r: ProvenanceRecord): VerificationError | null {\n if (r.hmac === undefined) return null;\n let key: Buffer | string | undefined;\n let keyDescription = \"\";\n if (r.key_id !== undefined) {\n if (!this.keyStore) {\n return {\n seq: r.seq,\n id: r.id,\n reason: `record carries key_id=${r.key_id.slice(0, 8)}… but daemon has no keyStore configured`,\n };\n }\n const resolved = this.keyStore.resolveById(r.key_id);\n if (!resolved) {\n return {\n seq: r.seq,\n id: r.id,\n reason: `record key_id=${r.key_id.slice(0, 8)}… not found in keyStore`,\n };\n }\n key = resolved.dek;\n keyDescription = `key_id=${r.key_id.slice(0, 8)}… state=${resolved.key_state}`;\n } else if (this.hmacKey) {\n key = this.hmacKey;\n keyDescription = \"fallback 4-tier resolution\";\n } else {\n return {\n seq: r.seq,\n id: r.id,\n reason: \"record carries hmac but verifier has no key (no keyStore, no fallback hmacKey)\",\n };\n }\n const expected = createHmac(\"sha256\", key).update(`${r.id}|${r.chain_hash}`).digest();\n const stored = Buffer.from(r.hmac, \"hex\");\n if (stored.length !== expected.length) {\n return {\n seq: r.seq,\n id: r.id,\n reason: `hmac length mismatch: stored ${stored.length} bytes vs expected ${expected.length}`,\n };\n }\n if (!timingSafeEqual(stored, expected)) {\n return {\n seq: r.seq,\n id: r.id,\n reason: `hmac mismatch (${keyDescription})`,\n };\n }\n return null;\n }\n\n /**\n * Acquire the write lock. Returns a release callback. Implements a minimal\n * promise-chained mutex so callers automatically queue up behind each\n * other without races.\n */\n private async acquireLock(): Promise<() => void> {\n let release!: () => void;\n const next = new Promise<void>((resolve) => {\n release = resolve;\n });\n const previous = this.writeLock;\n this.writeLock = this.writeLock.then(() => next);\n await previous;\n return release;\n }\n}\n","/**\n * Model router. Maps logical operation types (\"ingest\", \"query\", \"lint\",\n * \"compound_eval\") to concrete model IDs pulled from config, and exposes\n * per-model pricing for the cost tracker.\n *\n * Pricing numbers are USD per 1M tokens. They're used to convert usage\n * reports from the Anthropic SDK into dollar amounts. When a model ID is\n * unknown, we fall back to conservative defaults so the budget guardrails\n * still trip before we blow through real money.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport type { ModelId, OperationType, WotwConfig } from \"../utils/types.js\";\n\n/** Price table in USD per 1M tokens. */\nexport interface ModelPricing {\n input: number;\n output: number;\n}\n\n/** Known Anthropic model pricing as of 2026. Adjust in a single place. */\nexport const PRICING: Readonly<Record<string, ModelPricing>> = {\n \"claude-opus-4-6\": { input: 15, output: 75 },\n \"claude-opus-4-5\": { input: 15, output: 75 },\n \"claude-sonnet-4-6\": { input: 3, output: 15 },\n \"claude-sonnet-4-5\": { input: 3, output: 15 },\n \"claude-haiku-4-5\": { input: 1, output: 5 },\n \"claude-haiku-4-5-20251001\": { input: 1, output: 5 },\n};\n\n/** Conservative default used when we can't find a price entry. */\nconst DEFAULT_PRICING: ModelPricing = { input: 15, output: 75 };\n\nexport class ModelRouter {\n private readonly cfg: WotwConfig;\n\n constructor(cfg: WotwConfig) {\n this.cfg = cfg;\n }\n\n /** Resolve the model ID for a given operation type. */\n modelFor(op: OperationType | \"compound_eval\"): ModelId {\n switch (op) {\n case \"ingest\":\n return this.cfg.models.ingest;\n case \"query\":\n return this.cfg.models.query;\n case \"lint\":\n return this.cfg.models.lint;\n case \"compound\":\n case \"compound_eval\":\n return this.cfg.models.compound_eval;\n case \"merge\":\n return this.cfg.models.ingest;\n default:\n return this.cfg.models.ingest;\n }\n }\n\n /** Look up pricing for a model ID. Falls back to conservative defaults. */\n pricingFor(modelId: ModelId): ModelPricing {\n const p = PRICING[modelId];\n if (p) return p;\n getLogger(\"model-router\").warn(\n { model: modelId },\n \"unknown model — using Opus-tier pricing as fallback\",\n );\n return DEFAULT_PRICING;\n }\n\n /** Convert usage tokens to USD cost for a given model. */\n computeCost(modelId: ModelId, inputTokens: number, outputTokens: number): number {\n const p = this.pricingFor(modelId);\n return (inputTokens * p.input + outputTokens * p.output) / 1_000_000;\n }\n}\n","/**\n * Exponential backoff retry wrapper, used primarily for LLM calls and git operations.\n */\n\nexport interface RetryOptions {\n retries: number;\n initialDelayMs: number;\n maxDelayMs: number;\n factor: number;\n onRetry?: (error: unknown, attempt: number, nextDelayMs: number) => void;\n shouldRetry?: (error: unknown) => boolean;\n}\n\nconst DEFAULT_OPTS: RetryOptions = {\n retries: 5,\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n factor: 2,\n};\n\n/**\n * Retry `fn` with exponential backoff. Rethrows the last error if all attempts fail.\n */\nexport async function retry<T>(fn: () => Promise<T>, opts: Partial<RetryOptions> = {}): Promise<T> {\n const options: RetryOptions = { ...DEFAULT_OPTS, ...opts };\n let lastError: unknown;\n let delay = options.initialDelayMs;\n for (let attempt = 0; attempt <= options.retries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (options.shouldRetry && !options.shouldRetry(err)) throw err;\n if (attempt === options.retries) break;\n const nextDelay = Math.min(delay, options.maxDelayMs);\n if (options.onRetry) options.onRetry(err, attempt + 1, nextDelay);\n await sleep(nextDelay);\n delay = Math.min(delay * options.factor, options.maxDelayMs);\n }\n }\n throw lastError;\n}\n\n/**\n * Promise-based sleep.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Git committer for the wiki directory. Wraps simple-git with a narrow\n * surface area: stage specific paths, commit with a generated message,\n * retry on lock contention. The committer does NOT push — remote sync is\n * a user decision.\n */\nimport { relative } from \"node:path\";\nimport { errMsg } from \"../utils/errors.js\";\nimport { commitAll, ensureGitRepo } from \"../utils/git.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport { retry } from \"../utils/retry.js\";\n\nexport interface CommitRequest {\n /** Absolute path to the wiki root (the git repo). */\n wikiRoot: string;\n /** Absolute paths that should be included in the commit. */\n paths: string[];\n /** Short identifier for the operation (batch id, query id, etc.). */\n operationId: string;\n /** Human-readable operation label (e.g. \"ingest\", \"compound\"). */\n operation: string;\n /** Extra metadata lines appended to the commit body. */\n metadata?: Record<string, string | number>;\n}\n\nexport interface CommitResult {\n committed: boolean;\n sha: string | null;\n message: string;\n fileCount: number;\n reason?: string;\n}\n\n/**\n * Stage the given files and commit them. Returns { committed: false } when\n * there's nothing to commit (idempotent). Retries on transient lock errors.\n */\nexport async function commitWikiChanges(req: CommitRequest): Promise<CommitResult> {\n const log = getLogger(\"git-committer\");\n await ensureGitRepo(req.wikiRoot, \"chore: wotw init — scaffold wiki store\");\n\n const relativePaths = req.paths\n .map((p) => relative(req.wikiRoot, p).replace(/\\\\/g, \"/\"))\n .filter((p) => p.length > 0 && !p.startsWith(\"..\"));\n\n if (relativePaths.length === 0) {\n return { committed: false, sha: null, message: \"\", fileCount: 0, reason: \"no eligible paths\" };\n }\n\n const message = buildCommitMessage(req);\n\n return retry(\n async () => {\n const sha = await commitAll(req.wikiRoot, message, relativePaths);\n if (!sha) {\n return {\n committed: false,\n sha: null,\n message,\n fileCount: relativePaths.length,\n reason: \"nothing to commit\",\n };\n }\n log.info({ sha, fileCount: relativePaths.length, operationId: req.operationId }, \"committed\");\n return { committed: true, sha, message, fileCount: relativePaths.length };\n },\n {\n retries: 3,\n initialDelayMs: 200,\n maxDelayMs: 2_000,\n factor: 2,\n shouldRetry: (err) => /index\\.lock/i.test(errMsg(err)),\n onRetry: (err, attempt, delay) =>\n log.warn({ err, attempt, delay }, \"retrying git commit after lock contention\"),\n },\n );\n}\n\nfunction buildCommitMessage(req: CommitRequest): string {\n const header = `wotw: ${req.operation} ${req.operationId}`;\n const body: string[] = [];\n if (req.metadata) {\n for (const [k, v] of Object.entries(req.metadata)) {\n body.push(`${k}: ${v}`);\n }\n }\n body.push(`files: ${req.paths.length}`);\n return `${header}\\n\\n${body.join(\"\\n\")}`;\n}\n","/**\n * CLI invoker. Spawns the native `claude` binary for an ingestion or query\n * operation when the daemon is running in CLI mode.\n *\n * Why spawn a subprocess instead of using the Agent SDK here?\n * - CLI mode is intended for users who already pay for Claude Pro/Max.\n * The CLI uses their existing subscription at zero marginal cost.\n * - The SDK, by contrast, requires an API key and bills per token.\n *\n * The CLI's streaming tool-use traffic isn't exposed to us via a clean\n * protocol, so we determine which files the agent wrote by snapshotting the\n * wiki directory tree (path → mtime + size) before and after the run and\n * diffing the results. This is robust to any future changes in CLI output\n * format, works across stdio/stream-json/text modes, and doesn't require\n * parsing the CLI's messages at all.\n *\n * Arguments passed to `claude`:\n *\n * claude --print --dangerously-skip-permissions\n * --model <cli_model>\n * --append-system-prompt <systemPrompt>\n * [--max-turns <n>]\n *\n * The user prompt is piped on stdin rather than an argv entry so we don't\n * hit ARG_MAX limits on large batch prompts.\n */\nimport { spawn } from \"node:child_process\";\nimport { readdirSync, statSync, type Dirent } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { InvokeOptions, InvokeResult } from \"./llm-invoker.js\";\n\nexport interface CliInvokerConfig {\n /** Path to the `claude` binary (absolute or bare name on PATH). */\n cliPath: string;\n /** Model ID to pass via `--model`. */\n cliModel: string;\n /** Kill the subprocess if it runs longer than this (ms). Default 10 min. */\n timeoutMs?: number;\n}\n\n/**\n * Invoke the Claude CLI for an ingestion/query/compound operation.\n *\n * @param config CLI-specific configuration (binary path, model, timeout)\n * @param opts operation-specific options (cwd, prompts, maxTurns, ...)\n * @returns an {@link InvokeResult} with cost=0 and wiki-diff-derived `writtenPaths`\n */\nexport async function invokeClaudeCli(\n config: CliInvokerConfig,\n opts: InvokeOptions,\n): Promise<InvokeResult> {\n const log = getLogger(\"cli-invoker\");\n const timeoutMs = config.timeoutMs ?? 10 * 60 * 1000;\n\n // Snapshot the wiki directory before the run so we can compute which\n // files changed. We only care about files under cwd — raw/ is outside\n // and we never expect the agent to touch it.\n const before = snapshotTree(opts.cwd);\n\n const args = [\"--print\", \"--dangerously-skip-permissions\", \"--model\", config.cliModel];\n // `--append-system-prompt` is what the CLI uses to layer extra instructions\n // on top of the bundled system prompt. Equivalent to the SDK's\n // `systemPrompt.append` field.\n if (opts.systemPrompt && opts.systemPrompt.trim().length > 0) {\n args.push(\"--append-system-prompt\", opts.systemPrompt);\n }\n if (Number.isFinite(opts.maxTurns) && opts.maxTurns > 0) {\n // The CLI's --max-turns flag is accepted by recent versions. If an older\n // CLI is on PATH and rejects the flag, we'll surface the stderr message\n // to the caller — the ingestion queue then skips the batch cleanly.\n args.push(\"--max-turns\", String(opts.maxTurns));\n }\n\n log.info(\n {\n cliPath: config.cliPath,\n model: config.cliModel,\n cwd: opts.cwd,\n maxTurns: opts.maxTurns,\n userPromptChars: opts.userPrompt.length,\n },\n \"spawning claude CLI\",\n );\n\n const started = Date.now();\n const child = spawn(config.cliPath, args, {\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n // Full-env propagation (L-SEC-2). We deliberately forward the entire\n // parent environment so the `claude` binary can find `git`, locate\n // user dot-config (`~/.claude/*`), read the subscription session\n // cookie, etc. This also means `ANTHROPIC_API_KEY` will be inherited\n // if the operator has one set in their shell — the CLI will happily\n // fall back to API billing in that case, which is the documented\n // behavior of `claude --print`. Operators who want strict\n // subscription-only behavior must unset `ANTHROPIC_API_KEY` in the\n // shell that starts the daemon. This trade-off is covered in\n // docs/execution-modes.md.\n env: { ...process.env },\n });\n\n // Honor an external abort controller so the daemon can cancel on shutdown.\n const abortHandler = (): void => {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* process may already be dead */\n }\n };\n opts.abortController?.signal.addEventListener(\"abort\", abortHandler);\n\n // Timeout safety net — unref so an orphaned timer doesn't keep the event\n // loop alive in test suites.\n const timer = setTimeout(() => {\n log.warn({ timeoutMs }, \"claude CLI exceeded timeout, sending SIGTERM\");\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* ignore */\n }\n }, timeoutMs);\n timer.unref();\n\n // Stream the user prompt via stdin. Guard against EPIPE if the CLI exits\n // before accepting all the bytes.\n let stdinFailed = false;\n try {\n child.stdin.write(opts.userPrompt);\n child.stdin.end();\n } catch (err) {\n stdinFailed = true;\n log.warn({ err }, \"failed to write prompt to claude stdin\");\n }\n\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.setEncoding(\"utf8\");\n child.stderr.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n stdout += chunk;\n });\n child.stderr.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n const exit: { code: number | null; signal: NodeJS.Signals | null } = await new Promise(\n (resolve) => {\n child.on(\"error\", (err) => {\n log.error({ err }, \"claude CLI spawn error\");\n resolve({ code: -1, signal: null });\n });\n child.on(\"close\", (code, signal) => {\n resolve({ code, signal });\n });\n },\n );\n clearTimeout(timer);\n opts.abortController?.signal.removeEventListener(\"abort\", abortHandler);\n\n const durationMs = Date.now() - started;\n const success = exit.code === 0;\n\n if (!success) {\n log.warn(\n {\n exitCode: exit.code,\n signal: exit.signal,\n stderrTail: stderr.slice(-512),\n },\n \"claude CLI exited non-zero\",\n );\n }\n\n // Diff the wiki tree to find which files were written/modified.\n const after = snapshotTree(opts.cwd);\n const writtenPaths = diffSnapshots(before, after);\n\n if (stdinFailed) {\n if (writtenPaths.length > 0) {\n log.error(\"agent wrote files without receiving prompt — results may be hallucinated\");\n }\n return {\n finalText: stdout,\n totalCostUsd: 0,\n inputTokens: 0,\n outputTokens: 0,\n durationMs,\n numTurns: 0,\n sessionId: null,\n writtenPaths: [],\n stopReason: \"stdin_write_failed\",\n success: false,\n };\n }\n\n // Estimate tokens from byte sizes (rough 4 bytes/token approximation).\n const promptBytes = Buffer.byteLength(opts.userPrompt, \"utf8\");\n const totalOutputBytes = writtenPaths.reduce((sum, p) => {\n try {\n return sum + statSync(p).size;\n } catch {\n return sum;\n }\n }, 0);\n const inputTokens = Math.ceil(promptBytes / 4);\n const outputTokens = Math.ceil(totalOutputBytes / 4);\n\n return {\n finalText: stdout,\n // CLI mode is covered by the user's subscription — no per-op cost.\n totalCostUsd: 0,\n inputTokens,\n outputTokens,\n durationMs,\n numTurns: 0,\n sessionId: null,\n writtenPaths: writtenPaths.sort(),\n stopReason: success ? \"end_turn\" : (exit.signal ?? `exit_${exit.code}`),\n success,\n };\n}\n\n/** Snapshot entry: size + mtime-ms for cheap diffing. */\ninterface FileStat {\n size: number;\n mtimeMs: number;\n}\n\n/**\n * Walk `root` recursively and return a map of absolute path → stat summary.\n * Skips directories that are known to be noisy or irrelevant to wiki state:\n * `.git`, `node_modules`, hidden files at the wiki root. Errors from\n * individual stat calls are swallowed so a transient ENOENT doesn't derail\n * the whole snapshot.\n */\nfunction snapshotTree(root: string): Map<string, FileStat> {\n const out = new Map<string, FileStat>();\n const stack: string[] = [root];\n while (stack.length > 0) {\n const dir = stack.pop();\n if (!dir) continue;\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.name === \".git\" || entry.name === \"node_modules\") continue;\n // Skip the immutable raw/ directory — the agent never writes there.\n if (dir === root && entry.name === \"raw\") continue;\n const abs = join(dir, entry.name);\n if (entry.isDirectory()) {\n stack.push(abs);\n } else if (entry.isFile()) {\n try {\n const st = statSync(abs);\n out.set(abs, { size: st.size, mtimeMs: st.mtimeMs });\n } catch {\n /* skip unreadable entry */\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Diff two snapshots and return the list of paths that were either created\n * or modified between them. A path is considered modified if its size OR\n * mtime changed. This catches the normal case (agent writes → mtime bumps)\n * and the pathological case of an idempotent re-write of identical content.\n */\nfunction diffSnapshots(before: Map<string, FileStat>, after: Map<string, FileStat>): string[] {\n const changed: string[] = [];\n for (const [path, a] of after) {\n const b = before.get(path);\n if (!b) {\n changed.push(path);\n continue;\n }\n if (a.size !== b.size || a.mtimeMs !== b.mtimeMs) {\n changed.push(path);\n }\n }\n return changed;\n}\n","/**\n * Wraps both runtimes so the ingestion queue can call a single structured\n * function and get back an identical result shape regardless of whether we\n * spawned the `claude` CLI binary or the Agent SDK. The invoker owns:\n *\n * - Runtime dispatch (CLI vs API) based on the resolved execution mode\n * - System prompt composition (via prompt-builder)\n * - Session cwd pinning (always the wiki_root)\n * - Tool restriction (Read, Write, Edit, Glob, Grep only) — API mode\n * - Abort controller for shutdown safety\n * - Retry on transient SDK errors\n *\n * CLI mode does NOT use the Agent SDK at all. See {@link ./cli-invoker.ts}.\n * API mode uses the SDK's `query()` async generator and tracks\n * per-operation cost via `total_cost_usd`.\n */\nimport { query, type Options, type SDKMessage } from \"@anthropic-ai/claude-agent-sdk\";\nimport { getLogger } from \"../utils/logger.js\";\nimport { retry, type RetryOptions } from \"../utils/retry.js\";\nimport type { ModelId, RuntimeMode } from \"../utils/types.js\";\nimport { invokeClaudeCli, type CliInvokerConfig } from \"./cli-invoker.js\";\n\nexport interface InvokeOptions {\n /** Absolute path to pin the agent's working directory. */\n cwd: string;\n /** Additional readable/writable directories beyond cwd. */\n additionalDirectories?: string[];\n /** System prompt (our CLAUDE.md). */\n systemPrompt: string;\n /** First user turn text. */\n userPrompt: string;\n /** Model ID to use. */\n model: ModelId;\n /** Hard cap on agentic turns before the loop is forcibly ended. */\n maxTurns: number;\n /** Optional abort controller so callers can cancel mid-flight. */\n abortController?: AbortController;\n /** Retry policy for transient failures. */\n retry?: RetryOptions;\n /** Resume an existing session ID for multi-batch conversations. */\n resumeSessionId?: string;\n /**\n * Which runtime to invoke. Defaults to `api` to preserve the historical\n * behavior of unit tests and callers that haven't been updated yet.\n */\n runtimeMode?: RuntimeMode;\n /**\n * CLI-specific configuration (path, model). Required when\n * `runtimeMode === \"cli\"`.\n */\n cliConfig?: CliInvokerConfig;\n /**\n * Tool whitelist for the agent. Defaults to {@link INGESTION_TOOLS}\n * (Read/Write/Edit/Glob/Grep/TodoWrite). Pass a narrower list for\n * read-only callers (e.g. the query engine uses `Read/Glob/Grep` only).\n * Only applied in API mode — the CLI relies on\n * `--dangerously-skip-permissions` to grant tool access.\n */\n allowedTools?: string[];\n}\n\nexport interface InvokeResult {\n /** Terminal assistant text (may be empty). */\n finalText: string;\n /** Aggregated cost in USD as reported by the SDK. */\n totalCostUsd: number;\n /** Total input tokens across all turns. */\n inputTokens: number;\n /** Total output tokens across all turns. */\n outputTokens: number;\n /** Wall-clock duration (ms). */\n durationMs: number;\n /** Number of agentic turns taken. */\n numTurns: number;\n /** Session ID (for resume). */\n sessionId: string | null;\n /** All absolute paths the agent appeared to Write or Edit. */\n writtenPaths: string[];\n /** Stop reason reported by the SDK. */\n stopReason: string | null;\n /** True if the result subtype was 'success'. */\n success: boolean;\n}\n\nconst INGESTION_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Glob\", \"Grep\", \"TodoWrite\"];\n\n/**\n * Run the agent loop for a single ingestion batch. Dispatches to the CLI\n * invoker when `runtimeMode === \"cli\"`; otherwise uses the Agent SDK.\n */\nexport async function invokeIngestionAgent(opts: InvokeOptions): Promise<InvokeResult> {\n const log = getLogger(\"llm-invoker\");\n\n if (opts.runtimeMode === \"cli\") {\n if (!opts.cliConfig) {\n throw new Error(\"invokeIngestionAgent: runtimeMode=cli requires cliConfig\");\n }\n // CLI mode bypasses retry-with-backoff — the subprocess contract is\n // simpler (spawn returns a single exit code) and the caller's\n // ingestion queue already has a skip-on-failure branch. Wrapping the\n // CLI call in our generic retry helper would mostly just mask real\n // configuration errors (wrong binary, missing model) behind delays.\n return invokeClaudeCli(opts.cliConfig, opts);\n }\n\n const retryOpts: Partial<RetryOptions> = opts.retry ?? {\n retries: 2,\n initialDelayMs: 2_000,\n maxDelayMs: 30_000,\n factor: 2,\n onRetry: (err, attempt, delay) => {\n log.warn({ err, attempt, delay }, \"retrying ingestion agent invocation\");\n },\n };\n\n let attemptNum = 0;\n return retry(async () => {\n attemptNum += 1;\n const abort = opts.abortController ?? new AbortController();\n log.info(\n {\n attempt: attemptNum,\n model: opts.model,\n cwd: opts.cwd,\n maxTurns: opts.maxTurns,\n resumeSessionId: opts.resumeSessionId ?? null,\n },\n \"invoking ingestion agent\",\n );\n\n const tools = opts.allowedTools ?? INGESTION_TOOLS;\n const queryOptions: Options = {\n cwd: opts.cwd,\n additionalDirectories: opts.additionalDirectories,\n systemPrompt: { type: \"preset\", preset: \"claude_code\", append: opts.systemPrompt },\n model: opts.model,\n maxTurns: opts.maxTurns,\n allowedTools: tools,\n tools,\n // Review item 26: when allowedTools is empty (single-pass mode\n // post-Phase 6), \"bypassPermissions\" is a meaningless attack-\n // surface widener — there are no tools to grant. Default to\n // the SDK's \"default\" mode for the empty-tools path; keep the\n // bypass for the legacy multi-turn / tools-allowed shape.\n permissionMode: tools.length === 0 ? \"default\" : \"bypassPermissions\",\n abortController: abort,\n // Point SDK at the user-installed @anthropic-ai/claude-code launcher.\n // SDK default resolution looks for a platform-specific sibling package\n // binary (not installed in our container — we use claude-code's\n // install.cjs to fetch the native binary), then falls back to ./cli.js\n // adjacent to the SDK module (also not present in 2.1.x layout).\n // Without this override the SDK throws \"Native CLI binary for\n // ${process.platform}-${process.arch} not found\" at spawn time.\n pathToClaudeCodeExecutable:\n process.env.WOTW_CLAUDE_CLI_PATH ?? \"/app/node_modules/.bin/claude\",\n ...(opts.resumeSessionId ? { resume: opts.resumeSessionId } : {}),\n };\n\n const started = Date.now();\n const q = query({ prompt: opts.userPrompt, options: queryOptions });\n\n let finalText = \"\";\n let totalCostUsd = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let numTurns = 0;\n let sessionId: string | null = null;\n let stopReason: string | null = null;\n let success = false;\n const writtenPaths = new Set<string>();\n\n try {\n for await (const msg of q as AsyncIterable<SDKMessage>) {\n if (msg.type === \"system\") {\n if (\"session_id\" in msg && typeof msg.session_id === \"string\") {\n sessionId = msg.session_id;\n }\n } else if (msg.type === \"assistant\") {\n // Track tool_use blocks for Write/Edit — we'll scrape file paths.\n const content = (msg as { message?: { content?: unknown[] } }).message?.content;\n if (Array.isArray(content)) {\n for (const block of content) {\n const b = block as {\n type?: string;\n name?: string;\n input?: Record<string, unknown>;\n };\n if (b.type === \"tool_use\" && (b.name === \"Write\" || b.name === \"Edit\")) {\n const p = b.input?.file_path;\n if (typeof p === \"string\") writtenPaths.add(p);\n }\n }\n }\n } else if (msg.type === \"result\") {\n const r = msg as {\n subtype?: string;\n total_cost_usd?: number;\n usage?: { input_tokens?: number; output_tokens?: number };\n result?: string;\n num_turns?: number;\n stop_reason?: string | null;\n session_id?: string;\n };\n success = r.subtype === \"success\";\n totalCostUsd = r.total_cost_usd ?? 0;\n inputTokens = r.usage?.input_tokens ?? 0;\n outputTokens = r.usage?.output_tokens ?? 0;\n finalText = r.result ?? \"\";\n numTurns = r.num_turns ?? 0;\n stopReason = r.stop_reason ?? null;\n if (!sessionId && r.session_id) sessionId = r.session_id;\n }\n }\n } catch (err) {\n log.error({ err, attempt: attemptNum }, \"agent SDK stream failed\");\n throw err;\n }\n\n const durationMs = Date.now() - started;\n\n if (!success) {\n log.warn({ stopReason, numTurns, durationMs }, \"ingestion agent did not return success\");\n }\n\n return {\n finalText,\n totalCostUsd,\n inputTokens,\n outputTokens,\n durationMs,\n numTurns,\n sessionId,\n writtenPaths: [...writtenPaths].sort(),\n stopReason,\n success,\n };\n }, retryOpts);\n}\n","/**\n * AnthropicProvider — single-pass completion via the Anthropic Messages API.\n *\n * This provider intentionally does NOT use @anthropic-ai/claude-agent-sdk\n * (the Claude Code Agent SDK). The architectural design Layer 6 locks the\n * provider interface as single-pass `complete(prompt, options) → string`.\n * The Agent SDK's multi-turn tool-use shape is preserved only in legacy\n * call sites that have not yet been migrated (queue.ts, heal-handlers.ts,\n * compounding/engine.ts, query-engine.ts as of Phase 1).\n *\n * Cost computation lives here, not in ModelRouter. The provider knows its\n * own pricing model best, and centralising cost in the provider keeps the\n * abstraction self-contained. Unknown models fall back to Opus-tier\n * pricing (conservative — overestimates cost so budget guardrails trip\n * before real money is spent).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport type { TextBlock } from \"@anthropic-ai/sdk/resources/messages\";\nimport type {\n CompletionOptions,\n CompletionResult,\n FinishReason,\n LLMProvider,\n ValidateConnectionResult,\n} from \"../types-vendored.js\";\n\n/** Price table in USD per 1M tokens. Mirrors src/ingestion/model-router.ts. */\ninterface ModelPricing {\n input: number;\n output: number;\n}\n\n/** Known Anthropic model pricing as of 2026. */\nconst PRICING: Readonly<Record<string, ModelPricing>> = {\n \"claude-opus-4-7\": { input: 15, output: 75 },\n \"claude-opus-4-6\": { input: 15, output: 75 },\n \"claude-opus-4-5\": { input: 15, output: 75 },\n \"claude-sonnet-4-6\": { input: 3, output: 15 },\n \"claude-sonnet-4-5\": { input: 3, output: 15 },\n \"claude-haiku-4-5\": { input: 1, output: 5 },\n \"claude-haiku-4-5-20251001\": { input: 1, output: 5 },\n};\n\n/** Conservative fallback for unknown models (Opus-tier). */\nconst DEFAULT_PRICING: ModelPricing = { input: 15, output: 75 };\n\n/**\n * Normalize Anthropic's StopReason to our FinishReason enum.\n *\n * Anthropic's `tool_use`, `pause_turn`, and `refusal` map to \"error\"\n * because they're unexpected in a single-pass completion call (we don't\n * pass tools, we don't enable thinking-pause budgets, and a refusal is\n * a failure mode). `null` (which the SDK type allows) also maps to\n * \"error\" — completion ended without a recognised reason.\n */\nfunction normalizeFinishReason(\n reason:\n | \"end_turn\"\n | \"max_tokens\"\n | \"stop_sequence\"\n | \"tool_use\"\n | \"pause_turn\"\n | \"refusal\"\n | null,\n): FinishReason {\n switch (reason) {\n case \"end_turn\":\n return \"end_turn\";\n case \"max_tokens\":\n return \"max_tokens\";\n case \"stop_sequence\":\n return \"stop_sequence\";\n default:\n return \"error\";\n }\n}\n\n/** Configuration for constructing an AnthropicProvider instance. */\nexport interface AnthropicProviderConfig {\n /**\n * API key. If omitted, the Anthropic SDK reads from\n * process.env.ANTHROPIC_API_KEY at construction time.\n */\n apiKey?: string;\n /**\n * Optional base URL override (for testing or custom endpoints). The\n * SDK default points at api.anthropic.com.\n */\n baseURL?: string;\n /**\n * Optional pre-constructed SDK client. When provided, apiKey + baseURL\n * are ignored. Primarily used in tests to inject a mock.\n */\n client?: Anthropic;\n}\n\nexport class AnthropicProvider implements LLMProvider {\n readonly name = \"anthropic\" as const;\n readonly supportsTools = true;\n\n private readonly client: Anthropic;\n\n constructor(config: AnthropicProviderConfig = {}) {\n if (config.client) {\n this.client = config.client;\n } else {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n }\n }\n\n async complete(prompt: string, options: CompletionOptions): Promise<string> {\n const result = await this.completeWithUsage(prompt, options);\n return result.text;\n }\n\n async completeWithUsage(prompt: string, options: CompletionOptions): Promise<CompletionResult> {\n const started = Date.now();\n\n const response = await this.client.messages.create(\n {\n model: options.model,\n max_tokens: options.maxTokens ?? 4096,\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.systemPrompt ? { system: options.systemPrompt } : {}),\n ...(options.stopSequences && options.stopSequences.length > 0\n ? { stop_sequences: options.stopSequences }\n : {}),\n messages: [{ role: \"user\", content: prompt }],\n },\n options.abortSignal ? { signal: options.abortSignal } : undefined,\n );\n\n const durationMs = Date.now() - started;\n\n // Extract text from response content blocks. Single-pass completion\n // typically returns one or more text blocks; concatenate them. Non-text\n // blocks (tool_use, thinking, etc.) are filtered out — we don't pass\n // tools at the interface boundary, but the SDK's response shape is a\n // union so we narrow defensively.\n const text = response.content\n .filter((block): block is TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\n const inputTokens = response.usage.input_tokens;\n const outputTokens = response.usage.output_tokens;\n const totalCostUsd = this.computeCost(options.model, inputTokens, outputTokens);\n\n return {\n text,\n usage: {\n inputTokens,\n outputTokens,\n totalCostUsd,\n durationMs,\n finishReason: normalizeFinishReason(response.stop_reason),\n },\n };\n }\n\n async validateConnection(): Promise<ValidateConnectionResult> {\n try {\n // Cheap validation: list models. Anthropic's models endpoint\n // returns paginated results; we just need a 200.\n await this.client.models.list({ limit: 1 });\n return { valid: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n return { valid: false, error };\n }\n }\n\n /**\n * Compute USD cost for a completion. Uses the model-specific price\n * entry if known; falls back to Opus-tier pricing for unknown models.\n * Exposed for tests + diagnostics.\n */\n computeCost(modelId: string, inputTokens: number, outputTokens: number): number {\n const pricing = PRICING[modelId] ?? DEFAULT_PRICING;\n return (inputTokens * pricing.input + outputTokens * pricing.output) / 1_000_000;\n }\n}\n","/**\n * OpenAIProvider — single-pass completion via the OpenAI Chat Completions API.\n *\n * Conforms to the LLMProvider abstraction defined in the canonical\n * src/llm/types-vendored.ts (vendored from wotw-cloud). The interface\n * locks `complete(prompt, options) → Promise<string>` as the surface;\n * this provider's response shape (chat completion choice with .message.content)\n * maps to that interface.\n *\n * Stop-reason mapping:\n * - \"stop\" → \"end_turn\"\n * - \"length\" → \"max_tokens\"\n * - everything else (tool_calls, content_filter, function_call) → \"error\"\n *\n * Pricing: GPT-4o + GPT-4o-mini + o1 + o1-mini as of 2026. Unknown models\n * fall back to GPT-4o pricing as a conservative ceiling.\n */\nimport OpenAI from \"openai\";\nimport type {\n CompletionOptions,\n CompletionResult,\n FinishReason,\n LLMProvider,\n ValidateConnectionResult,\n} from \"../types-vendored.js\";\n\ninterface ModelPricing {\n input: number;\n output: number;\n}\n\nconst PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n o1: { input: 15, output: 60 },\n \"o1-mini\": { input: 3, output: 12 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n};\n\n// Review item 14: pre-fix `{ input: 2.5, output: 10 }` matches gpt-4o, but\n// unknown reasoning models (o1-preview, o1-pro, o3-* family) price ~$15/$60\n// per 1M tokens. Costed under DEFAULT_PRICING the daemon under-estimates\n// by 6×. Make the default a conservative ceiling so the daily-budget gate\n// fires before the real bill blows past the cap.\nconst DEFAULT_PRICING: ModelPricing = { input: 15, output: 60 };\n\nfunction normalizeFinishReason(reason: string | null | undefined): FinishReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"length\":\n return \"max_tokens\";\n default:\n return \"error\";\n }\n}\n\nexport interface OpenAIProviderConfig {\n apiKey?: string;\n baseURL?: string;\n client?: OpenAI;\n}\n\nexport class OpenAIProvider implements LLMProvider {\n readonly name = \"openai\" as const;\n readonly supportsTools = true;\n\n private readonly client: OpenAI;\n\n constructor(config: OpenAIProviderConfig = {}) {\n if (config.client) {\n this.client = config.client;\n } else {\n this.client = new OpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n }\n }\n\n async complete(prompt: string, options: CompletionOptions): Promise<string> {\n const result = await this.completeWithUsage(prompt, options);\n return result.text;\n }\n\n async completeWithUsage(prompt: string, options: CompletionOptions): Promise<CompletionResult> {\n const started = Date.now();\n\n const messages: { role: \"system\" | \"user\"; content: string }[] = [];\n if (options.systemPrompt) {\n messages.push({ role: \"system\", content: options.systemPrompt });\n }\n messages.push({ role: \"user\", content: prompt });\n\n // Review items 9 + 10: o-series reasoning models (o1, o1-mini,\n // o3, o3-mini) reject `max_tokens` (use `max_completion_tokens`)\n // and reject `temperature` ≠ 1.0. The Chat Completions endpoint\n // returns 400 \"Unsupported parameter\" before doing any work — so\n // we shape the request per family.\n const isOSeries = /^o[1-9]/.test(options.model);\n const tokenField: Record<string, number> = isOSeries\n ? { max_completion_tokens: options.maxTokens ?? 4096 }\n : { max_tokens: options.maxTokens ?? 4096 };\n const tempField: Record<string, number> =\n isOSeries || options.temperature === undefined ? {} : { temperature: options.temperature };\n const response = await this.client.chat.completions.create(\n {\n model: options.model,\n messages,\n ...tokenField,\n ...tempField,\n ...(options.stopSequences && options.stopSequences.length > 0\n ? { stop: options.stopSequences }\n : {}),\n },\n options.abortSignal ? { signal: options.abortSignal } : undefined,\n );\n\n const durationMs = Date.now() - started;\n const choice = response.choices[0];\n const text = choice?.message?.content ?? \"\";\n const inputTokens = response.usage?.prompt_tokens ?? 0;\n const outputTokens = response.usage?.completion_tokens ?? 0;\n const totalCostUsd = this.computeCost(options.model, inputTokens, outputTokens);\n\n return {\n text,\n usage: {\n inputTokens,\n outputTokens,\n totalCostUsd,\n durationMs,\n finishReason: normalizeFinishReason(choice?.finish_reason),\n },\n };\n }\n\n async validateConnection(): Promise<ValidateConnectionResult> {\n try {\n // Cheap validation: list models.\n await this.client.models.list();\n return { valid: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n return { valid: false, error };\n }\n }\n\n computeCost(modelId: string, inputTokens: number, outputTokens: number): number {\n const pricing = PRICING[modelId] ?? DEFAULT_PRICING;\n return (inputTokens * pricing.input + outputTokens * pricing.output) / 1_000_000;\n }\n}\n","/**\n * GeminiProvider — single-pass completion via Google Generative AI SDK.\n *\n * Conforms to LLMProvider. The SDK's `generateContent` returns a response\n * whose `.candidates[0].content.parts` array contains text parts; we\n * concatenate them.\n *\n * Stop-reason mapping (FinishReason from @google/generative-ai):\n * - \"STOP\" → \"end_turn\"\n * - \"MAX_TOKENS\" → \"max_tokens\"\n * - \"SAFETY\", \"RECITATION\", \"OTHER\", undefined → \"error\"\n *\n * Safety filters: by default the SDK applies strict content policies that\n * can reject legitimate technical/academic content. Provider sets all\n * safety thresholds to BLOCK_ONLY_HIGH so the daemon's wiki maintenance\n * tasks aren't blocked by ambiguous content. Hosted-mode operators can\n * lock this further by setting GEMINI_SAFETY_STRICT=true to revert to\n * defaults — not exposed in the LLMProvider interface, internal to the\n * provider for now.\n *\n * Pricing: Gemini 2.0 Pro + Flash as of 2026. Unknown models fall back to\n * Gemini 2.0 Pro pricing as a conservative ceiling.\n */\nimport { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from \"@google/generative-ai\";\nimport type {\n CompletionOptions,\n CompletionResult,\n FinishReason,\n LLMProvider,\n ValidateConnectionResult,\n} from \"../types-vendored.js\";\n\ninterface ModelPricing {\n input: number;\n output: number;\n}\n\nconst PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gemini-2.0-pro\": { input: 1.25, output: 5 },\n \"gemini-2.0-flash\": { input: 0.075, output: 0.3 },\n \"gemini-1.5-pro\": { input: 1.25, output: 5 },\n \"gemini-1.5-flash\": { input: 0.075, output: 0.3 },\n};\n\n// Review item 14: pre-fix `{ input: 1.25, output: 5 }` matches gemini-1.5-pro\n// at low context, but newer Gemini families (2.5-pro at >200K context,\n// 3.0-pro thinking mode) charge $5-12 / $25-60 per 1M. Default to a\n// conservative ceiling so cost guardrails fire before the real bill does.\nconst DEFAULT_PRICING: ModelPricing = { input: 12, output: 60 };\n\nfunction normalizeFinishReason(reason: string | undefined): FinishReason {\n switch (reason) {\n case \"STOP\":\n return \"end_turn\";\n case \"MAX_TOKENS\":\n return \"max_tokens\";\n default:\n return \"error\";\n }\n}\n\nexport interface GeminiProviderConfig {\n apiKey?: string;\n /** When true, use the SDK's default safety settings (stricter). */\n strictSafety?: boolean;\n client?: GoogleGenerativeAI;\n}\n\nexport class GeminiProvider implements LLMProvider {\n readonly name = \"gemini\" as const;\n readonly supportsTools = true;\n\n private readonly client: GoogleGenerativeAI;\n private readonly strictSafety: boolean;\n // Retained for review item 12's free-tier validateConnection (REST\n // `models?key=...` call). The constructor still hands the key to the\n // SDK client, but the validate path needs it again.\n private readonly apiKey: string;\n\n constructor(config: GeminiProviderConfig = {}) {\n const apiKey = config.apiKey ?? process.env.GOOGLE_API_KEY ?? \"\";\n this.client = config.client ?? new GoogleGenerativeAI(apiKey);\n this.strictSafety = config.strictSafety ?? false;\n this.apiKey = apiKey;\n }\n\n async complete(prompt: string, options: CompletionOptions): Promise<string> {\n const result = await this.completeWithUsage(prompt, options);\n return result.text;\n }\n\n async completeWithUsage(prompt: string, options: CompletionOptions): Promise<CompletionResult> {\n const started = Date.now();\n\n const safetySettings = this.strictSafety\n ? undefined\n : [\n {\n category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,\n threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,\n },\n {\n category: HarmCategory.HARM_CATEGORY_HARASSMENT,\n threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,\n },\n {\n category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,\n threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,\n },\n {\n category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,\n threshold: HarmBlockThreshold.BLOCK_ONLY_HIGH,\n },\n ];\n\n const model = this.client.getGenerativeModel({\n model: options.model,\n ...(options.systemPrompt\n ? { systemInstruction: { role: \"system\", parts: [{ text: options.systemPrompt }] } }\n : {}),\n generationConfig: {\n maxOutputTokens: options.maxTokens ?? 4096,\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n ...(options.stopSequences && options.stopSequences.length > 0\n ? { stopSequences: options.stopSequences }\n : {}),\n },\n ...(safetySettings ? { safetySettings } : {}),\n });\n\n // Review item 13: forward abortSignal so aborted requests stop\n // accruing billable tokens client-side. Per SDK 0.24.1 docs, signal\n // is plumbed via SingleRequestOptions. Note: Google charges for\n // server-side work even after abort — the signal cancels the\n // client read, not the upstream generation. Documented honesty.\n const requestOptions = options.abortSignal ? { signal: options.abortSignal } : undefined;\n const result = await model.generateContent(\n {\n contents: [{ role: \"user\", parts: [{ text: prompt }] }],\n },\n requestOptions,\n );\n\n const durationMs = Date.now() - started;\n const candidate = result.response.candidates?.[0];\n const text = candidate?.content?.parts?.map((p) => (\"text\" in p ? p.text : \"\")).join(\"\") ?? \"\";\n\n const inputTokens = result.response.usageMetadata?.promptTokenCount ?? 0;\n const outputTokens = result.response.usageMetadata?.candidatesTokenCount ?? 0;\n const totalCostUsd = this.computeCost(options.model, inputTokens, outputTokens);\n\n return {\n text,\n usage: {\n inputTokens,\n outputTokens,\n totalCostUsd,\n durationMs,\n finishReason: normalizeFinishReason(candidate?.finishReason),\n },\n };\n }\n\n async validateConnection(): Promise<ValidateConnectionResult> {\n // Review item 12: pre-fix called a paid `generateContent` for every\n // \"validate\" press. Switch to the free models listing endpoint\n // (`/v1beta/models?key=...`) per X5-corrected recommendation —\n // GoogleGenerativeAI.listModels() doesn't exist in SDK 0.24.1, so\n // we use raw fetch. Costs nothing; still proves key + network are good.\n try {\n const apiKey = this.apiKey;\n const url = `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}`;\n const res = await fetch(url);\n if (!res.ok) {\n const body = await res.text();\n return { valid: false, error: `HTTP ${res.status}: ${body.slice(0, 200)}` };\n }\n return { valid: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n return { valid: false, error };\n }\n }\n\n computeCost(modelId: string, inputTokens: number, outputTokens: number): number {\n const pricing = PRICING[modelId] ?? DEFAULT_PRICING;\n return (inputTokens * pricing.input + outputTokens * pricing.output) / 1_000_000;\n }\n}\n","/**\n * OllamaProvider — single-pass completion via local Ollama HTTP API.\n *\n * No SDK dependency — Ollama's API is small enough to implement directly.\n * Default URL: http://localhost:11434. No API key (Ollama is local).\n *\n * Cost computation returns null — local inference has no API cost. The\n * daemon's cost tracker treats null as \"free\" downstream (CostTracker\n * accepts cost: 0 for skipping tracking).\n *\n * Conforms to LLMProvider. The /api/chat endpoint accepts:\n * { model, messages: [{ role, content }], options: { num_predict, temperature, stop } }\n * and returns:\n * { message: { role: \"assistant\", content }, prompt_eval_count, eval_count, done_reason }\n *\n * Stop reason mapping (Ollama's `done_reason`):\n * - \"stop\" → \"end_turn\"\n * - \"length\" → \"max_tokens\"\n * - everything else → \"error\"\n */\nimport type {\n CompletionOptions,\n CompletionResult,\n FinishReason,\n LLMProvider,\n ValidateConnectionResult,\n} from \"../types-vendored.js\";\n\nconst DEFAULT_OLLAMA_URL = \"http://localhost:11434\";\n\nfunction normalizeFinishReason(reason: string | undefined): FinishReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"length\":\n return \"max_tokens\";\n default:\n return \"error\";\n }\n}\n\ninterface OllamaChatResponse {\n message?: { role: string; content: string };\n prompt_eval_count?: number;\n eval_count?: number;\n done_reason?: string;\n}\n\ninterface OllamaTagsResponse {\n models?: Array<{ name: string }>;\n}\n\nexport interface OllamaProviderConfig {\n /** Base URL of the Ollama instance. Defaults to localhost:11434. */\n baseURL?: string;\n /**\n * Optional fetch implementation (for testing). Defaults to globalThis.fetch.\n */\n fetchImpl?: typeof fetch;\n}\n\nexport class OllamaProvider implements LLMProvider {\n readonly name = \"ollama\" as const;\n readonly supportsTools = false;\n\n private readonly baseURL: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(config: OllamaProviderConfig = {}) {\n this.baseURL = config.baseURL ?? DEFAULT_OLLAMA_URL;\n this.fetchImpl = config.fetchImpl ?? globalThis.fetch;\n }\n\n async complete(prompt: string, options: CompletionOptions): Promise<string> {\n const result = await this.completeWithUsage(prompt, options);\n return result.text;\n }\n\n async completeWithUsage(prompt: string, options: CompletionOptions): Promise<CompletionResult> {\n const started = Date.now();\n\n const messages: { role: \"system\" | \"user\"; content: string }[] = [];\n if (options.systemPrompt) {\n messages.push({ role: \"system\", content: options.systemPrompt });\n }\n messages.push({ role: \"user\", content: prompt });\n\n const ollamaOptions: Record<string, unknown> = {};\n // Review item 11: Ollama's default num_predict is 128 — far below\n // what vocabulary-enricher / query-expansion / ingestion need.\n // Callers that don't set maxTokens get silent 128-token truncation.\n // Match the daemon's other providers' default of 4096.\n ollamaOptions.num_predict = options.maxTokens ?? 4096;\n if (options.temperature !== undefined) ollamaOptions.temperature = options.temperature;\n if (options.stopSequences && options.stopSequences.length > 0) {\n ollamaOptions.stop = options.stopSequences;\n }\n\n const body: Record<string, unknown> = {\n model: options.model,\n messages,\n stream: false,\n };\n if (Object.keys(ollamaOptions).length > 0) body.options = ollamaOptions;\n\n const response = await this.fetchImpl(`${this.baseURL}/api/chat`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n });\n\n if (!response.ok) {\n const errText = await response.text().catch(() => \"(no body)\");\n throw new Error(`ollama chat failed: ${response.status} ${errText.slice(0, 200)}`);\n }\n\n const data = (await response.json()) as OllamaChatResponse;\n const durationMs = Date.now() - started;\n const text = data.message?.content ?? \"\";\n\n return {\n text,\n usage: {\n inputTokens: data.prompt_eval_count ?? 0,\n outputTokens: data.eval_count ?? 0,\n totalCostUsd: null,\n durationMs,\n finishReason: normalizeFinishReason(data.done_reason),\n },\n };\n }\n\n async validateConnection(): Promise<ValidateConnectionResult> {\n try {\n const response = await this.fetchImpl(`${this.baseURL}/api/tags`);\n if (!response.ok) {\n return { valid: false, error: `ollama /api/tags returned ${response.status}` };\n }\n return { valid: true };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n return { valid: false, error };\n }\n }\n\n /** List available local models. Ollama-specific extension. */\n async listModels(): Promise<string[]> {\n const response = await this.fetchImpl(`${this.baseURL}/api/tags`);\n if (!response.ok) return [];\n const data = (await response.json()) as OllamaTagsResponse;\n return (data.models ?? []).map((m) => m.name);\n }\n}\n","/**\n * Runtime-aware completion wrapper.\n *\n * The wotw daemon supports two LLM runtime modes:\n * - \"api\": metered Anthropic API call (BYOK ANTHROPIC_API_KEY).\n * - \"cli\": spawn the user's locally-installed `claude` CLI binary\n * (free with a Claude Pro/Max subscription).\n *\n * The `LLMProvider` abstraction (Phase 1 lock) is API-mode-only by design\n * — provider-agnostic across API providers (Anthropic, OpenAI, Gemini,\n * Ollama). CLI mode is Anthropic-only by construction; it cannot be\n * routed through a non-Anthropic provider.\n *\n * This wrapper holds both runtime modes together so call sites\n * (query-expansion, vocabulary-enricher, future migrated callers) don't\n * branch on `runtimeMode` themselves. API mode goes through the provider\n * abstraction; CLI mode preserves the existing `invokeIngestionAgent` /\n * `invokeClaudeCli` subprocess path.\n *\n * Phase 10 will wire per-tenant provider selection (the API mode branch\n * will pick AnthropicProvider / OpenAIProvider / GeminiProvider /\n * OllamaProvider per `wiki.llm_provider`). For Phases 2-6 the API mode\n * branch is hardcoded to AnthropicProvider — the migration target is\n * the abstraction shape, not the provider count.\n */\nimport { invokeIngestionAgent } from \"../ingestion/llm-invoker.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport type { LLMProvider } from \"./types-vendored.js\";\nimport { AnthropicProvider } from \"./providers/anthropic.js\";\nimport { OpenAIProvider } from \"./providers/openai.js\";\nimport { GeminiProvider } from \"./providers/gemini.js\";\nimport { OllamaProvider } from \"./providers/ollama.js\";\n\nexport interface RuntimeAwareCompleteOptions {\n /** Logical model identifier (provider-specific). */\n model: string;\n /** Optional system prompt. */\n systemPrompt?: string;\n /** Maximum tokens in the response. Default: 4096 (API mode), N/A (CLI). */\n maxTokens?: number;\n /** Sampling temperature, 0-1. Default: provider default. */\n temperature?: number;\n /** Stop sequences. */\n stopSequences?: string[];\n /** Abort signal. */\n abortSignal?: AbortSignal;\n /** Daemon config (needed for CLI mode subprocess + API key env var name). */\n config: WotwConfig;\n /** Resolved runtime mode (cli or api). */\n runtimeMode: RuntimeMode;\n}\n\n/**\n * Result shape unifies the API-mode (`AnthropicProvider.completeWithUsage`)\n * and CLI-mode (`invokeIngestionAgent` subprocess) outputs. CLI mode\n * doesn't report cost (free with subscription) — cost is 0.\n */\nexport interface RuntimeAwareCompleteResult {\n text: string;\n costUsd: number;\n inputTokens: number;\n outputTokens: number;\n durationMs: number;\n /**\n * Review item 16: provider stop reason — propagated up so callers can\n * detect max-tokens truncation. `\"length\"` / `\"max_tokens\"` (provider-\n * specific) means the response was cut off mid-output; downstream\n * JSON-edits parsers should treat that as a hard parse-error not a\n * model-said-no-edits. CLI mode doesn't expose this — null.\n */\n finishReason: string | null;\n}\n\n/**\n * Single-pass completion that dispatches to the right runtime.\n *\n * API mode → AnthropicProvider.completeWithUsage (Messages API direct).\n * CLI mode → invokeIngestionAgent with maxTurns=1, allowedTools=[]\n * (spawns claude binary; subprocess returns a single response).\n *\n * CLI mode currently flows through invokeIngestionAgent for compatibility;\n * Phase 6 may revisit whether the spawn machinery can be simplified now\n * that the multi-turn use cases are gone, but for Phase 2 the path is\n * preserved as-is.\n */\nexport async function runtimeAwareComplete(\n prompt: string,\n options: RuntimeAwareCompleteOptions,\n): Promise<RuntimeAwareCompleteResult> {\n if (options.runtimeMode === \"cli\") {\n const result = await invokeIngestionAgent({\n cwd: options.config.wiki_root,\n systemPrompt: options.systemPrompt ?? \"\",\n userPrompt: prompt,\n model: options.model,\n maxTurns: 1,\n allowedTools: [],\n abortController: options.abortSignal\n ? abortSignalToController(options.abortSignal)\n : undefined,\n runtimeMode: \"cli\",\n cliConfig: {\n cliPath: options.config.execution.cli_path,\n cliModel: options.config.execution.cli_model,\n },\n });\n return {\n text: result.finalText,\n costUsd: result.totalCostUsd,\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs: result.durationMs,\n // Review item 16: CLI subprocess shape doesn't expose finish reason.\n finishReason: null,\n };\n }\n\n // API mode: select provider based on config.llm.provider. Phase 10\n // wires per-tenant selection across Anthropic / OpenAI / Gemini / Ollama.\n const provider = selectProvider(options.config);\n const result = await provider.completeWithUsage(prompt, {\n model: options.model,\n systemPrompt: options.systemPrompt,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n stopSequences: options.stopSequences,\n abortSignal: options.abortSignal,\n });\n\n return {\n text: result.text,\n costUsd: result.usage.totalCostUsd ?? 0,\n inputTokens: result.usage.inputTokens,\n outputTokens: result.usage.outputTokens,\n durationMs: result.usage.durationMs,\n // Review item 16: surface provider finish reason so consumers can\n // detect max-tokens truncation. Without this, partial-JSON responses\n // are silently parsed as \"model emitted no edits\" — looks like a\n // successful no-op when it's actually a hard truncation.\n finishReason: result.usage.finishReason ?? null,\n };\n}\n\n/**\n * Construct the configured LLM provider. Reads `config.llm.provider`\n * (default \"anthropic\") and instantiates the matching provider with\n * its API key from `config.execution.api_key_env` (or for Ollama, the\n * configured `config.llm.ollama_url` or default localhost:11434).\n *\n * Per Pass 008 BYOK invariant: the daemon reads the API key from its\n * environment at provider-construction time; the cloud-side orchestrator\n * injects the correct key under the correct env var at spawn time.\n */\nfunction selectProvider(config: WotwConfig): LLMProvider {\n const providerName = config.llm.provider;\n if (providerName === \"anthropic\") {\n return new AnthropicProvider({\n apiKey: process.env[config.execution.api_key_env],\n });\n }\n if (providerName === \"openai\") {\n return new OpenAIProvider({\n apiKey: process.env[config.execution.api_key_env],\n });\n }\n if (providerName === \"gemini\") {\n return new GeminiProvider({\n apiKey: process.env[config.execution.api_key_env],\n });\n }\n // ollama\n return new OllamaProvider({ baseURL: config.llm.ollama_url });\n}\n\n/**\n * Adapter: invokeIngestionAgent expects an AbortController, but the\n * LLMProvider interface takes an AbortSignal. Wrap the signal.\n */\nfunction abortSignalToController(signal: AbortSignal): AbortController {\n const controller = new AbortController();\n if (signal.aborted) {\n controller.abort();\n } else {\n signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n return controller;\n}\n","/**\n * Structured edits response shape used by the daemon's single-pass LLM\n * call sites. The model returns:\n *\n * { \"edits\": [ { \"path\": \"<wiki-relative-or-absolute>\", \"content\": \"...\" } ] }\n *\n * The daemon parses this, sanitizes paths against `wiki_root`, and\n * applies each edit via `atomicWrite`. Phases 5 (heal-handlers) and\n * 6 (main ingestion + compounding hand-off) share this code path.\n *\n * Why a shared module: each call site that uses this shape (heal + main\n * ingestion + future compounding-with-edits) would otherwise re-implement\n * the same JSON extraction + path sanitization. Drift between them\n * would be a real bug surface — same problem we solved for chain-hash\n * via the vendored anchor pattern.\n */\nimport { isAbsolute, resolve } from \"node:path\";\n\n/** A single file edit emitted by the model. */\nexport interface DaemonEdit {\n /** Wiki-relative or absolute path. */\n path: string;\n /** Full new file content (including frontmatter). */\n content: string;\n}\n\n/** Parsed response envelope. */\nexport interface DaemonEditsResponse {\n edits: DaemonEdit[];\n}\n\n/**\n * Parse the raw model response into a DaemonEditsResponse. The prompt\n * instructs the model to emit JSON-only; this parser is defensive:\n *\n * - Strips a leading/trailing markdown code fence (```json ... ```)\n * emitted by some providers (Gemini, Ollama) despite the prompt\n * instruction.\n * - Falls back to extracting the first balanced `{...}` block when\n * the candidate has stray surrounding text.\n * - Returns null if no JSON block found or JSON.parse throws.\n * - Validates `edits` is an array; per-edit validates `path` and\n * `content` are non-empty strings.\n *\n * Returns null on any failure — caller is expected to log + skip.\n */\nexport function parseDaemonEditsResponse(text: string): DaemonEditsResponse | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n // Strip a wrapping markdown code fence. Gemini frequently emits\n // ```json\\n{...}\\n``` despite the prompt's JSON-only instruction.\n let candidate = trimmed;\n const fenceMatch = candidate.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```\\s*$/);\n if (fenceMatch && fenceMatch[1]) {\n candidate = fenceMatch[1].trim();\n }\n // Try direct parse first.\n let parsed: unknown;\n try {\n parsed = JSON.parse(candidate);\n } catch {\n // Fall back to balanced-brace extraction for stray text.\n const match = candidate.match(/\\{[\\s\\S]*\\}/);\n if (!match) return null;\n try {\n parsed = JSON.parse(match[0]);\n } catch {\n return null;\n }\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !Array.isArray((parsed as { edits?: unknown }).edits)\n ) {\n return null;\n }\n const raw = (parsed as { edits: unknown[] }).edits;\n const edits: DaemonEdit[] = [];\n for (const e of raw) {\n if (\n e &&\n typeof e === \"object\" &&\n typeof (e as DaemonEdit).path === \"string\" &&\n (e as DaemonEdit).path.length > 0 &&\n typeof (e as DaemonEdit).content === \"string\"\n ) {\n edits.push({\n path: (e as DaemonEdit).path,\n content: (e as DaemonEdit).content,\n });\n }\n }\n return { edits };\n}\n\n/**\n * Resolve an edit path (wiki-relative or absolute) to an absolute path\n * within `wikiRoot`. Rejects paths that escape via `..` or absolute\n * paths outside the wiki tree.\n *\n * Returns null on rejection — caller is expected to log + skip.\n */\nexport function resolveEditPath(wikiRoot: string, p: string): string | null {\n if (!p || typeof p !== \"string\") return null;\n const candidate = isAbsolute(p) ? resolve(p) : resolve(wikiRoot, p);\n const root = resolve(wikiRoot);\n if (candidate !== root && !candidate.startsWith(`${root}/`)) {\n return null;\n }\n return candidate;\n}\n","/**\n * Heal handlers — LLM-powered auto-fix functions for health findings.\n *\n * Each handler takes a finding + context, invokes the ingestion agent with\n * a targeted prompt, records a \"heal\" provenance entry, and commits the\n * result. Handlers are dispatched by finding kind from `wotw lint --fix`.\n */\nimport { relative, resolve, sep } from \"node:path\";\nimport { commitWikiChanges } from \"../ingestion/git-committer.js\";\nimport type { InvokeResult } from \"../ingestion/llm-invoker.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport { parseDaemonEditsResponse, resolveEditPath } from \"../llm/edits.js\";\nimport type { ModelRouter } from \"../ingestion/model-router.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport { sha256Files, sha256Hex } from \"../provenance/hash.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport { atomicWrite } from \"../utils/fs.js\";\nimport { repairBidirectionalLinks } from \"./cross-reference.js\";\nimport type { HealthFinding } from \"./health.js\";\nimport type { WikiSearch } from \"./search.js\";\nimport type { WikiStore } from \"./store.js\";\nimport { loadAllPages } from \"../ingestion/wiki-writer.js\";\n\nexport interface HealContext {\n config: WotwConfig;\n store: WikiStore;\n search: WikiSearch;\n provenance: ProvenanceChain | null;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n runtimeMode: RuntimeMode;\n}\n\nexport interface HealResult {\n finding: HealthFinding;\n fixed: boolean;\n reason?: string;\n costUsd: number;\n}\n\n// ---------------------------------------------------------------------------\n// Stale page refresh\n// ---------------------------------------------------------------------------\n\nexport async function healStale(finding: HealthFinding, ctx: HealContext): Promise<HealResult> {\n const log = getLogger(\"heal\");\n const pageRel = finding.pages[0];\n if (!pageRel) return { finding, fixed: false, reason: \"no page\", costUsd: 0 };\n\n const absPath = `${ctx.config.wiki_root}/${pageRel}`;\n const page = await ctx.store.readPage(absPath);\n if (!page) return { finding, fixed: false, reason: \"page not found\", costUsd: 0 };\n\n // Build prompt with source material if available.\n const sourceInfo = page.frontmatter.sources.map((s) => `- ${s}`).join(\"\\n\");\n const prompt = [\n `Review and refresh this wiki page. It was last updated ${page.frontmatter.updated}.`,\n `Page path: ${pageRel}`,\n `Title: ${page.frontmatter.title}`,\n sourceInfo ? `Original source files:\\n${sourceInfo}` : \"No source files on record.\",\n \"\",\n \"Instructions:\",\n \"- Read the page and its source files (if they still exist).\",\n \"- Update any outdated information based on the source material.\",\n \"- If the source material is no longer available, add `status: stale` to the frontmatter but preserve all existing content.\",\n \"- Keep all existing frontmatter fields (title, category, tags, sources, related, confidence).\",\n \"- Update the `updated:` frontmatter field to today's date.\",\n \"- Do NOT create any new files.\",\n ].join(\"\\n\");\n\n const result = await invokeHeal(ctx, prompt, \"heal-refresh\");\n if (!result) return { finding, fixed: false, reason: \"LLM invocation failed\", costUsd: 0 };\n // Review item 27: do not claim fixed:true when LLM emitted zero edits.\n // Without this gate, the lint loop refinds + regenerates + re-heals the\n // same finding next interval, burning cost indefinitely (item 30).\n if (!result.success || result.writtenPaths.length === 0) {\n return {\n finding,\n fixed: false,\n reason: \"LLM emitted no edits\",\n costUsd: result.totalCostUsd,\n };\n }\n\n // Rebuild search index after page mutation.\n ctx.search.rebuild(await loadAllPages(ctx.store));\n\n await recordHealProvenance(ctx, result, {\n heal_kind: \"refresh\",\n page: pageRel,\n });\n\n await commitHealChanges(ctx, result, \"refresh\", pageRel);\n log.info({ page: pageRel, cost: result.totalCostUsd }, \"healed stale page\");\n return { finding, fixed: true, costUsd: result.totalCostUsd };\n}\n\n// ---------------------------------------------------------------------------\n// Duplicate merge\n// ---------------------------------------------------------------------------\n\nexport async function healDuplicate(finding: HealthFinding, ctx: HealContext): Promise<HealResult> {\n const log = getLogger(\"heal\");\n if (finding.pages.length < 2) {\n return { finding, fixed: false, reason: \"need >=2 pages\", costUsd: 0 };\n }\n\n const pageContents: string[] = [];\n for (const rel of finding.pages) {\n const abs = `${ctx.config.wiki_root}/${rel}`;\n const page = await ctx.store.readPage(abs);\n if (page) {\n pageContents.push(`--- ${rel} ---\\n${page.raw}`);\n }\n }\n\n const prompt = [\n \"These wiki pages appear to cover the same topic. Merge them into a single authoritative page.\",\n \"\",\n \"Pages to merge:\",\n ...pageContents,\n \"\",\n \"Instructions:\",\n \"- Choose the best title and keep the most complete content.\",\n \"- Preserve ALL unique information from every page.\",\n \"- Keep all source references from all pages.\",\n \"- Update the `related:` frontmatter to include references from all merged pages.\",\n \"- Write the merged page to the first page's path.\",\n `- For each redundant page (${finding.pages.slice(1).join(\", \")}):`,\n \" - Clear the body.\",\n ` - Set frontmatter: status: merged, merged_into: ${finding.pages[0]}`,\n \" - Keep the title and category for reference.\",\n \"- Do NOT delete any files.\",\n ].join(\"\\n\");\n\n const result = await invokeHeal(ctx, prompt, \"heal-dedup\");\n if (!result) return { finding, fixed: false, reason: \"LLM invocation failed\", costUsd: 0 };\n if (!result.success || result.writtenPaths.length === 0) {\n return {\n finding,\n fixed: false,\n reason: \"LLM emitted no edits\",\n costUsd: result.totalCostUsd,\n };\n }\n\n // Repair bidirectional links after merge.\n const allPages = await loadAllPages(ctx.store);\n const mutated = repairBidirectionalLinks(ctx.store, allPages);\n for (const p of mutated) {\n await ctx.store.writePage(p);\n }\n ctx.search.rebuild(await loadAllPages(ctx.store));\n // Review item 32: backlink-repair writes were missing from\n // wiki_files_written. Splice them into result.writtenPaths so the\n // chain records the full set of files this heal touched, not just\n // the LLM-emitted edits.\n for (const p of mutated) {\n if (!result.writtenPaths.includes(p.path)) result.writtenPaths.push(p.path);\n }\n\n await recordHealProvenance(ctx, result, {\n heal_kind: \"dedup-merge\",\n merged_pages: finding.pages.join(\",\"),\n surviving_page: finding.pages[0] ?? \"\",\n });\n\n await commitHealChanges(ctx, result, \"dedup-merge\", finding.pages.join(\"+\"));\n log.info({ pages: finding.pages, cost: result.totalCostUsd }, \"healed duplicate pages\");\n return { finding, fixed: true, costUsd: result.totalCostUsd };\n}\n\n// ---------------------------------------------------------------------------\n// Broken link repair\n// ---------------------------------------------------------------------------\n\nexport async function healBrokenLinks(\n finding: HealthFinding,\n ctx: HealContext,\n): Promise<HealResult> {\n const log = getLogger(\"heal\");\n const pageRel = finding.pages[0];\n if (!pageRel) return { finding, fixed: false, reason: \"no page\", costUsd: 0 };\n\n // Build list of all valid page titles.\n const allPages = await loadAllPages(ctx.store);\n const validTitles = allPages\n .map((p) => `${relative(ctx.config.wiki_root, p.path)}: ${p.frontmatter.title}`)\n .join(\"\\n\");\n\n const prompt = [\n `This wiki page has broken [[wikilinks]]: ${finding.description}`,\n `Page path: ${pageRel}`,\n \"\",\n \"Valid wiki pages and their titles:\",\n validTitles,\n \"\",\n \"Instructions:\",\n \"- Read the page.\",\n \"- For each broken link, either fix it to point to the correct existing page, or remove the link if no matching page exists.\",\n \"- Do NOT create any new pages.\",\n \"- Do NOT modify any other files.\",\n ].join(\"\\n\");\n\n const result = await invokeHeal(ctx, prompt, \"heal-links\");\n if (!result) return { finding, fixed: false, reason: \"LLM invocation failed\", costUsd: 0 };\n if (!result.success || result.writtenPaths.length === 0) {\n return {\n finding,\n fixed: false,\n reason: \"LLM emitted no edits\",\n costUsd: result.totalCostUsd,\n };\n }\n\n // Rebuild search index after page mutation.\n ctx.search.rebuild(await loadAllPages(ctx.store));\n\n await recordHealProvenance(ctx, result, {\n heal_kind: \"link-repair\",\n page: pageRel,\n });\n\n await commitHealChanges(ctx, result, \"link-repair\", pageRel);\n log.info({ page: pageRel, cost: result.totalCostUsd }, \"healed broken links\");\n return { finding, fixed: true, costUsd: result.totalCostUsd };\n}\n\n// ---------------------------------------------------------------------------\n// Missing backlink repair (no LLM needed)\n// ---------------------------------------------------------------------------\n\nexport async function healMissingBacklinks(\n finding: HealthFinding,\n ctx: HealContext,\n): Promise<HealResult> {\n const log = getLogger(\"heal\");\n const allPages = await loadAllPages(ctx.store);\n const mutated = repairBidirectionalLinks(ctx.store, allPages);\n\n if (mutated.length === 0) {\n return { finding, fixed: false, reason: \"no mutations needed\", costUsd: 0 };\n }\n\n for (const p of mutated) {\n await ctx.store.writePage(p);\n }\n\n // Rebuild search after writes.\n ctx.search.rebuild(await loadAllPages(ctx.store));\n\n // Record provenance (no LLM call, so no cost).\n if (ctx.provenance) {\n const writtenPaths = mutated.map((p) => p.path);\n const hashesByAbs = await sha256Files(writtenPaths);\n const wikiRoot = ctx.config.wiki_root;\n const wikiFileHashes: Record<string, string> = {};\n for (const abs of writtenPaths) {\n const h = hashesByAbs[abs];\n if (h) wikiFileHashes[relative(wikiRoot, abs)] = h;\n }\n await ctx.provenance.append({\n type: \"heal\",\n source_files: [],\n source_hashes: [],\n prompt_hash: sha256Hex(\"backlink-repair\"),\n model_id: \"none\",\n response_hash: sha256Hex(\"backlink-repair\"),\n wiki_files_written: Object.keys(wikiFileHashes),\n wiki_file_hashes_after: wikiFileHashes,\n metadata: {\n heal_kind: \"backlink-repair\",\n pages_fixed: mutated.length,\n },\n });\n }\n\n await commitWikiChanges({\n wikiRoot: ctx.config.wiki_root,\n paths: [...mutated.map((p) => p.path), ...(ctx.provenance ? [ctx.provenance.path] : [])],\n operationId: `heal-backlink-${Date.now()}`,\n operation: \"heal\",\n metadata: { heal_kind: \"backlink-repair\", pages_fixed: mutated.length },\n });\n\n log.info({ pagesFixed: mutated.length }, \"healed missing backlinks\");\n return { finding, fixed: true, costUsd: 0 };\n}\n\n// ---------------------------------------------------------------------------\n// Contradiction resolution\n// ---------------------------------------------------------------------------\n\nexport async function healContradiction(\n finding: HealthFinding,\n ctx: HealContext,\n): Promise<HealResult> {\n const log = getLogger(\"heal\");\n if (finding.pages.length < 2) {\n return { finding, fixed: false, reason: \"need >=2 pages\", costUsd: 0 };\n }\n\n const pageContents: string[] = [];\n for (const rel of finding.pages) {\n const abs = `${ctx.config.wiki_root}/${rel}`;\n const page = await ctx.store.readPage(abs);\n if (page) {\n pageContents.push(`--- ${rel} ---\\n${page.raw}`);\n }\n }\n\n const prompt = [\n `These two wiki pages contradict each other: ${finding.description}`,\n \"\",\n ...pageContents,\n \"\",\n \"Instructions:\",\n \"- Read both pages and their source material (if available).\",\n \"- Determine which claim is better supported by the source material.\",\n \"- Update the incorrect page to resolve the contradiction.\",\n \"- If you cannot determine which is correct, add a `contradictions:` frontmatter field to both pages listing the unresolved conflict.\",\n \"- Do NOT create any new pages.\",\n ].join(\"\\n\");\n\n const result = await invokeHeal(ctx, prompt, \"heal-contradiction\");\n if (!result) return { finding, fixed: false, reason: \"LLM invocation failed\", costUsd: 0 };\n if (!result.success || result.writtenPaths.length === 0) {\n return {\n finding,\n fixed: false,\n reason: \"LLM emitted no edits\",\n costUsd: result.totalCostUsd,\n };\n }\n\n // Rebuild search index after page mutation.\n ctx.search.rebuild(await loadAllPages(ctx.store));\n\n await recordHealProvenance(ctx, result, {\n heal_kind: \"contradiction-resolve\",\n pages: finding.pages.join(\",\"),\n });\n\n await commitHealChanges(ctx, result, \"contradiction-resolve\", finding.pages.join(\"+\"));\n log.info({ pages: finding.pages, cost: result.totalCostUsd }, \"healed contradiction\");\n return { finding, fixed: true, costUsd: result.totalCostUsd };\n}\n\n// ---------------------------------------------------------------------------\n// Knowledge consolidation\n// ---------------------------------------------------------------------------\n\nexport async function healConsolidation(\n finding: HealthFinding,\n ctx: HealContext,\n): Promise<HealResult> {\n const log = getLogger(\"heal\");\n if (finding.pages.length < 2) {\n return { finding, fixed: false, reason: \"need >=2 pages\", costUsd: 0 };\n }\n\n const pageContents: string[] = [];\n for (const rel of finding.pages) {\n const abs = `${ctx.config.wiki_root}/${rel}`;\n const page = await ctx.store.readPage(abs);\n if (page) {\n pageContents.push(`--- ${rel} ---\\n${page.raw}`);\n }\n }\n\n const suggestedTitle =\n finding.description.match(/\"([^\"]+)\"/)?.[1] ??\n finding.pages[0]?.split(\"/\").pop()?.replace(/\\.md$/, \"\").replace(/[-_]/g, \" \") ??\n \"consolidated topic\";\n\n const prompt = [\n `These ${finding.pages.length} pages all cover the same topic area: \"${suggestedTitle}\".`,\n \"Merge them into a single authoritative page that preserves all unique information,\",\n \"removes redundancy, and maintains all source references.\",\n `Title the consolidated page: \"${suggestedTitle}\".`,\n \"\",\n \"Pages to consolidate:\",\n ...pageContents,\n \"\",\n \"Instructions:\",\n \"- Write the consolidated page to the first page's path.\",\n \"- Preserve ALL unique information from every page.\",\n \"- Keep all source references from all pages in the `sources:` frontmatter.\",\n \"- Merge all `related:` links.\",\n \"- Merge all `tags:` values (deduplicate).\",\n \"- Merge all `key_terms:` values (deduplicate).\",\n \"- Use the highest confidence level among the source pages.\",\n `- For each original page EXCEPT the first (${finding.pages.slice(1).join(\", \")}):`,\n ` - Clear the body.`,\n ` - Set frontmatter: status: consolidated, consolidated_into: ${finding.pages[0]}`,\n \" - Keep the title and category for reference.\",\n \"- Do NOT delete any files.\",\n ].join(\"\\n\");\n\n const result = await invokeHeal(ctx, prompt, \"heal-consolidation\");\n if (!result) return { finding, fixed: false, reason: \"LLM invocation failed\", costUsd: 0 };\n if (!result.success || result.writtenPaths.length === 0) {\n return {\n finding,\n fixed: false,\n reason: \"LLM emitted no edits\",\n costUsd: result.totalCostUsd,\n };\n }\n\n // Repair bidirectional links after consolidation.\n const allPages = await loadAllPages(ctx.store);\n const mutated = repairBidirectionalLinks(ctx.store, allPages);\n for (const p of mutated) {\n await ctx.store.writePage(p);\n }\n ctx.search.rebuild(await loadAllPages(ctx.store));\n // Review item 32: include backlink-repair writes in wiki_files_written.\n for (const p of mutated) {\n if (!result.writtenPaths.includes(p.path)) result.writtenPaths.push(p.path);\n }\n\n await recordHealProvenance(ctx, result, {\n heal_kind: \"consolidation\",\n source_pages: finding.pages.join(\",\"),\n consolidated_page: finding.pages[0] ?? \"\",\n });\n\n await commitHealChanges(ctx, result, \"consolidation\", finding.pages.join(\"+\"));\n log.info({ pages: finding.pages, cost: result.totalCostUsd }, \"consolidated topic pages\");\n return { finding, fixed: true, costUsd: result.totalCostUsd };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatcher\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch a finding to the appropriate heal handler. Returns null for\n * finding kinds that cannot be auto-healed.\n */\n/**\n * Review item 30: in-memory idempotency / backoff map for heal attempts.\n * Pre-fix, the same finding could be re-found by lint on each interval\n * and re-healed → unbounded provenance growth + cost burn in auto_fix\n * mode. Now: each finding.id is throttled to one attempt per HEAL_BACKOFF_MS\n * unless the prior attempt SUCCEEDED (fixed:true). Keyed by finding.id\n * so re-finding the same issue under the same id is recognized.\n *\n * Lives at module scope: tied to daemon process lifetime, which matches\n * the lint-scheduler's lifetime. Restart clears the map by design —\n * a fresh daemon takes a fresh look at the wiki.\n */\nconst healAttempts = new Map<string, { attemptedAt: number; lastFixed: boolean }>();\nconst HEAL_BACKOFF_MS = 6 * 60 * 60 * 1000; // 6 hours\nfunction recordHealAttempt(id: string, fixed: boolean): void {\n healAttempts.set(id, { attemptedAt: Date.now(), lastFixed: fixed });\n}\nfunction shouldSkipHeal(id: string): boolean {\n const last = healAttempts.get(id);\n if (!last) return false;\n if (last.lastFixed) return false; // successful fixes don't block future re-attempts\n return Date.now() - last.attemptedAt < HEAL_BACKOFF_MS;\n}\n\n/** Test-only: reset the backoff state. */\nexport function _resetHealBackoffForTests(): void {\n healAttempts.clear();\n}\n\nexport async function healFinding(\n finding: HealthFinding,\n ctx: HealContext,\n): Promise<HealResult | null> {\n // Review item 30: idempotency / backoff. Skip if a recent attempt\n // for this exact finding.id failed; let it rediscover after the\n // backoff window so cost-burn loops cannot form.\n if (shouldSkipHeal(finding.id)) {\n return { finding, fixed: false, reason: \"heal backoff active\", costUsd: 0 };\n }\n const dispatch = async (): Promise<HealResult | null> => {\n switch (finding.kind) {\n case \"stale\":\n return healStale(finding, ctx);\n case \"duplicate\":\n return healDuplicate(finding, ctx);\n case \"broken-link\":\n return healBrokenLinks(finding, ctx);\n case \"missing-backlink\":\n return healMissingBacklinks(finding, ctx);\n case \"contradiction\":\n return healContradiction(finding, ctx);\n case \"consolidation\":\n return healConsolidation(finding, ctx);\n case \"orphan\":\n // Already handled by archive system — not auto-fixable.\n return null;\n default:\n return null;\n }\n };\n const result = await dispatch();\n if (result) {\n recordHealAttempt(finding.id, result.fixed);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function invokeHeal(\n ctx: HealContext,\n userPrompt: string,\n label: string,\n): Promise<InvokeResult | null> {\n const log = getLogger(\"heal\");\n const model =\n ctx.runtimeMode === \"cli\" ? ctx.config.execution.cli_model : ctx.modelRouter.modelFor(\"lint\");\n\n // Budget pre-flight (API mode only).\n if (ctx.runtimeMode !== \"cli\") {\n const estimated = ctx.modelRouter.computeCost(model, 8_000, 4_000);\n if (ctx.costTracker.wouldExceedDaily(estimated)) {\n log.warn({ label }, \"skipping heal — daily budget exceeded\");\n return null;\n }\n }\n\n const systemPrompt = [\n \"You are a wiki maintenance agent. Apply the fix described in the user message.\",\n \"\",\n \"Return ONLY valid JSON with this shape, no other text:\",\n `{ \"edits\": [ { \"path\": \"wiki-relative-path-or-absolute.md\", \"content\": \"full new file content with frontmatter\" } ] }`,\n \"\",\n 'If no changes are needed, return: { \"edits\": [] }',\n \"\",\n \"Rules:\",\n \"- The `content` field is the COMPLETE new file content, including YAML frontmatter.\",\n \"- Only emit edits for files the user prompt mentions or references.\",\n \"- Do NOT create new files unless explicitly asked.\",\n \"- Do NOT include any text outside the JSON object.\",\n ].join(\"\\n\");\n\n const started = Date.now();\n let rawText = \"\";\n let costUsd = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let durationMs = 0;\n\n try {\n const result = await runtimeAwareComplete(userPrompt, {\n systemPrompt,\n model,\n maxTokens: 16_384,\n config: ctx.config,\n runtimeMode: ctx.runtimeMode,\n });\n rawText = result.text;\n costUsd = result.costUsd;\n inputTokens = result.inputTokens;\n outputTokens = result.outputTokens;\n durationMs = result.durationMs;\n } catch (err) {\n log.error({ err, label }, \"heal LLM invocation failed\");\n return null;\n }\n\n ctx.costTracker.logUsage({\n operation: \"heal\",\n model,\n costUsd,\n inputTokens,\n outputTokens,\n });\n\n const parsed = parseDaemonEditsResponse(rawText);\n const writtenPaths: string[] = [];\n // Review item 28: heal writes used to call atomicWrite directly, bypassing\n // reconcileWrittenPages (which adds frontmatter normalization + provenance\n // footer + raw/ write-block). Now we (a) reject edits that target raw/, and\n // (b) reconcile through wiki-writer after writing so heal pages end up\n // shaped identically to ingestion pages.\n const rawPath = resolve(ctx.config.raw_path);\n if (parsed) {\n for (const edit of parsed.edits) {\n const absPath = resolveEditPath(ctx.config.wiki_root, edit.path);\n if (!absPath) {\n log.warn(\n { path: edit.path, label },\n \"heal edit rejected — path resolves outside wiki_root\",\n );\n continue;\n }\n if (absPath === rawPath || absPath.startsWith(`${rawPath}${sep}`)) {\n log.warn(\n { path: edit.path, label },\n \"heal edit rejected — model attempted to write inside raw/\",\n );\n continue;\n }\n try {\n await atomicWrite(absPath, edit.content);\n writtenPaths.push(absPath);\n } catch (err) {\n log.warn({ err, path: edit.path, label }, \"heal edit failed to write — skipping\");\n }\n }\n } else if (rawText.trim().length > 0) {\n log.warn({ label, sample: rawText.slice(0, 200) }, \"heal response was not valid JSON edits\");\n }\n\n // Reconcile written pages through the same pipeline ingestion uses so\n // heal outputs get last_compiled, source_count, last_confirmed,\n // superseded_by normalization + provenance footer applied uniformly.\n // Staging is disabled for heal — heal is fix-in-place semantics.\n if (writtenPaths.length > 0) {\n try {\n const { reconcileWrittenPages } = await import(\"../ingestion/wiki-writer.js\");\n const { pages, skipped } = await reconcileWrittenPages(ctx.store, writtenPaths, {\n staging: false,\n });\n if (skipped.length > 0) {\n log.warn({ label, skipped }, \"heal: some written paths skipped during reconcile\");\n }\n // Replace writtenPaths with paths that actually survived reconcile.\n writtenPaths.length = 0;\n for (const p of pages) writtenPaths.push(p.path);\n } catch (err) {\n log.warn({ err, label }, \"heal: reconcile failed\");\n }\n }\n\n // Compose an InvokeResult-shaped object for the existing per-handler\n // contract. Phase 5 preserves the handler-facing shape; future phases\n // can simplify if/when invokeHeal's callers migrate.\n const _started = started; // appease eslint unused-var when durationMs path used\n void _started;\n\n return {\n finalText: rawText,\n totalCostUsd: costUsd,\n inputTokens,\n outputTokens,\n durationMs,\n numTurns: 1,\n sessionId: null,\n writtenPaths,\n stopReason: \"end_turn\",\n success: writtenPaths.length > 0,\n };\n}\n\nasync function recordHealProvenance(\n ctx: HealContext,\n result: InvokeResult,\n metadata: Record<string, string | number | boolean>,\n): Promise<void> {\n if (!ctx.provenance) return;\n const wikiRoot = ctx.config.wiki_root;\n const toRel = (abs: string): string => relative(wikiRoot, abs) || abs;\n\n const writtenRels = result.writtenPaths.map(toRel);\n const hashesByAbs = await sha256Files(result.writtenPaths);\n const wikiFileHashes: Record<string, string> = {};\n for (const abs of result.writtenPaths) {\n const h = hashesByAbs[abs];\n if (h) wikiFileHashes[toRel(abs)] = h;\n }\n\n try {\n await ctx.provenance.append({\n type: \"heal\",\n source_files: [],\n source_hashes: [],\n prompt_hash: sha256Hex(\"heal\"),\n model_id:\n result.writtenPaths.length > 0\n ? ctx.runtimeMode === \"cli\"\n ? ctx.config.execution.cli_model\n : ctx.modelRouter.modelFor(\"lint\")\n : \"none\",\n response_hash: sha256Hex(result.finalText || \"\"),\n wiki_files_written: writtenRels,\n wiki_file_hashes_after: wikiFileHashes,\n metadata: {\n cost_usd: Number(result.totalCostUsd.toFixed(6)),\n duration_ms: result.durationMs,\n ...metadata,\n },\n });\n } catch (err) {\n getLogger(\"heal\").error({ err }, \"failed to append heal provenance record\");\n }\n}\n\nasync function commitHealChanges(\n ctx: HealContext,\n result: InvokeResult,\n healKind: string,\n target: string,\n): Promise<void> {\n const paths = [...result.writtenPaths, ...(ctx.provenance ? [ctx.provenance.path] : [])];\n try {\n await commitWikiChanges({\n wikiRoot: ctx.config.wiki_root,\n paths: [...new Set(paths)],\n operationId: `heal-${healKind}-${Date.now()}`,\n operation: \"heal\",\n metadata: { heal_kind: healKind, target },\n });\n } catch (err) {\n getLogger(\"heal\").warn({ err, healKind }, \"heal commit failed (non-fatal)\");\n }\n}\n","/**\n * `wotw lint` — run health checks over the wiki: staleness, duplicates,\n * broken links, orphans, missing backlinks. With `--fix`, auto-heal\n * fixable findings via LLM-powered repair passes.\n *\n * The work is split into a pure {@link runLintPass} function that returns\n * a structured {@link LintResult} and a thin CLI wrapper. The scheduler\n * subsystem (`src/daemon/lint-scheduler.ts`) calls {@link runLintPass}\n * directly so it can log WARN/INFO based on the result without duplicating\n * the sweep logic.\n */\nimport type { Command } from \"commander\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { CostTracker } from \"../../ingestion/cost-tracker.js\";\nimport { resolveExecutionMode } from \"../../ingestion/execution-mode.js\";\nimport { ModelRouter } from \"../../ingestion/model-router.js\";\nimport { ProvenanceChain } from \"../../provenance/chain.js\";\nimport { readTextOrNullAsync } from \"../../utils/fs.js\";\nimport type { RuntimeMode, WotwConfig } from \"../../utils/types.js\";\nimport { healFinding, type HealContext, type HealResult } from \"../../wiki/heal-handlers.js\";\nimport { computeHealthReport, type HealthReport } from \"../../wiki/health.js\";\nimport { parsePage } from \"../../wiki/page.js\";\nimport { WikiSearch } from \"../../wiki/search.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { loadAllPages } from \"../../ingestion/wiki-writer.js\";\nimport { chalk, info, line, success, warn } from \"../output.js\";\n\ninterface LintOptions {\n fix?: boolean;\n yes?: boolean;\n json?: boolean;\n}\n\n/**\n * Structured result of a single lint pass.\n */\nexport interface LintResult {\n wikiRoot: string;\n totalPages: number;\n orphanedPages: number;\n issueCount: number;\n /** True if the wiki directory did not exist at all. */\n missingWikiDir: boolean;\n /** Full health report (null when wiki dir is missing). */\n healthReport: HealthReport | null;\n /** Results of auto-heal operations (empty when --fix is not used). */\n healResults: HealResult[];\n}\n\n/**\n * Attach the `lint` subcommand.\n */\nexport function registerLintCommand(program: Command): void {\n program\n .command(\"lint\")\n .description(\"Run health checks over the wiki (staleness, duplicates, orphans, broken links)\")\n .option(\"--fix\", \"Attempt to auto-fix fixable findings via LLM\")\n .option(\"-y, --yes\", \"Skip confirmation prompt (non-interactive)\")\n .option(\"--json\", \"Emit machine-readable JSON instead of a summary\")\n .action(async (opts: LintOptions) => {\n await runLint(opts);\n });\n}\n\n/**\n * CLI entry point. Loads config, runs a lint pass, optionally heals, and\n * prints a summary.\n */\nexport async function runLint(opts: LintOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const result = await runLintPass(config, opts);\n\n if (result.missingWikiDir) {\n warn(`No wiki directory at ${join(result.wikiRoot, \"wiki\")}. Run 'wotw init' first.`);\n return;\n }\n\n if (opts.json === true && result.healthReport) {\n line(\n JSON.stringify(\n {\n ...result,\n healthReport: result.healthReport,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n const report = result.healthReport;\n if (!report) {\n info(`Structural sweep: found ${result.totalPages} wiki pages.`);\n return;\n }\n\n // Print health summary.\n const avgScore =\n report.scores.length > 0\n ? Math.round(report.scores.reduce((s, p) => s + p.score, 0) / report.scores.length)\n : 0;\n const belowFifty = report.scores.filter((s) => s.score < 50).length;\n\n info(\n `Health: ${report.scores.length} pages, avg score ${avgScore}, ${belowFifty} page(s) below 50`,\n );\n\n if (result.orphanedPages > 0) {\n warn(`${result.orphanedPages} orphaned page(s) (source deleted but page retained).`);\n }\n\n if (report.summary.total > 0) {\n info(\n `Findings: ${report.summary.total} total ` +\n `(${chalk.red(String(report.summary.high))} high, ` +\n `${chalk.yellow(String(report.summary.medium))} medium, ` +\n `${String(report.summary.low)} low) — ` +\n `${report.summary.autoFixable} auto-fixable`,\n );\n } else {\n success(\"No issues found.\");\n }\n\n // Print heal results.\n if (result.healResults.length > 0) {\n const fixed = result.healResults.filter((r) => r.fixed).length;\n const totalCost = result.healResults.reduce((s, r) => s + r.costUsd, 0);\n info(\n `Auto-heal: ${fixed}/${result.healResults.length} findings fixed` +\n (totalCost > 0 ? ` ($${totalCost.toFixed(4)})` : \"\"),\n );\n }\n}\n\n/**\n * Execute a full lint pass. Returns a {@link LintResult} the caller can\n * log / serialize / trigger alerts on. This is the function the scheduler\n * subsystem invokes.\n */\nexport async function runLintPass(config: WotwConfig, opts?: LintOptions): Promise<LintResult> {\n const wikiRoot = config.wiki_root;\n const wikiDir = join(wikiRoot, \"wiki\");\n if (!existsSync(wikiDir)) {\n return {\n wikiRoot,\n totalPages: 0,\n orphanedPages: 0,\n issueCount: 0,\n missingWikiDir: true,\n healthReport: null,\n healResults: [],\n };\n }\n\n // Count pages + orphans.\n const files = walkMarkdown(wikiDir);\n let orphanedPages = 0;\n for (const file of files) {\n const raw = await readTextOrNullAsync(file);\n if (raw === null) continue;\n const page = parsePage(file, raw);\n if (page.frontmatter.status === \"orphaned\") orphanedPages += 1;\n }\n\n // Build full health report.\n const store = new WikiStore({ wikiRoot });\n const search = new WikiSearch();\n const allPages = await loadAllPages(store);\n search.rebuild(allPages);\n\n // Initialize provenance chain for reading.\n let provenance: ProvenanceChain | null = null;\n if (config.provenance.enabled) {\n provenance = new ProvenanceChain({ path: config.provenance.chain_file });\n await provenance.init();\n }\n\n const report = await computeHealthReport(store, provenance, search, { config });\n\n const healResults: HealResult[] = [];\n\n // Auto-heal if requested.\n if (opts?.fix === true) {\n const fixable = report.findings.filter((f) => f.autoFixable);\n if (fixable.length > 0) {\n // Resolve execution mode for heal invocations.\n let runtimeMode: RuntimeMode = \"api\";\n try {\n const resolved = resolveExecutionMode(config);\n runtimeMode = resolved.mode;\n } catch {\n warn(\"No execution mode available — LLM-dependent heals will be skipped\");\n // runtimeMode stays \"api\" — heal handlers will fail at budget pre-flight\n // or LLM invocation and return {fixed: false}, which is the correct behavior.\n }\n\n const costTracker = new CostTracker({\n trackFile: config.cost.track_file,\n maxDailyUsd: config.cost.max_daily_usd,\n maxPerIngestUsd: config.cost.max_per_ingest_usd,\n maxPerQueryUsd: config.cost.max_per_query_usd,\n });\n const modelRouter = new ModelRouter(config);\n\n const healCtx: HealContext = {\n config,\n store,\n search,\n provenance,\n costTracker,\n modelRouter,\n runtimeMode,\n };\n\n const maxFixes = config.health.max_fixes_per_run;\n let fixCount = 0;\n\n for (const finding of fixable) {\n if (fixCount >= maxFixes) {\n info(`Reached max_fixes_per_run (${maxFixes}), stopping.`);\n break;\n }\n const result = await healFinding(finding, healCtx);\n if (result) {\n healResults.push(result);\n if (result.fixed) fixCount += 1;\n }\n }\n }\n }\n\n return {\n wikiRoot,\n totalPages: files.length,\n orphanedPages,\n issueCount: report.summary.total,\n missingWikiDir: false,\n healthReport: report,\n healResults,\n };\n}\n\nfunction walkMarkdown(dir: string): string[] {\n const out: string[] = [];\n const walk = (d: string): void => {\n for (const name of readdirSync(d)) {\n const full = join(d, name);\n const s = statSync(full);\n if (s.isDirectory()) walk(full);\n else if (s.isFile() && name.endsWith(\".md\")) out.push(full);\n }\n };\n walk(dir);\n return out;\n}\n","/**\n * Token store for multi-user mode.\n *\n * In single-user mode the server authenticates Bearer tokens against a single\n * `config.server.auth_token` value. In multi-user mode we want several users\n * (or CLI clients) to share one daemon, each with their own distinct token.\n * The TokenStore owns that mapping.\n *\n * Persistence format is a JSON file at `{workspaces_dir}/tokens.json`:\n *\n * {\n * \"version\": 1,\n * \"tokens\": {\n * \"tok_alice_...\": { \"user\": \"alice\", \"created\": \"2026-04-07T...\" },\n * \"tok_bob_...\": { \"user\": \"bob\", \"created\": \"2026-04-07T...\" }\n * }\n * }\n *\n * The store is loaded synchronously on startup so the HTTP server can\n * authenticate without awaiting a disk read on every request. Mutations\n * (add/revoke) are written atomically via the same temp-file-then-rename\n * pattern the wiki store uses.\n */\nimport { chmodSync, copyFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { atomicWriteSync, ensureDirSync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\n\nexport interface TokenInfo {\n user: string;\n created: string;\n}\n\n/** On-disk schema for the token file. */\ninterface TokenFile {\n version: 1;\n tokens: Record<string, TokenInfo>;\n}\n\nexport interface TokenStoreOptions {\n /** Directory where `tokens.json` lives. */\n workspacesDir: string;\n}\n\n/**\n * Authenticated principal returned after a successful token lookup.\n * Currently just a user name; we model it as an object so future fields\n * (scopes, quotas, etc.) can be added without breaking callers.\n */\nexport interface Principal {\n user: string;\n}\n\n/**\n * In-memory cache of token → user mappings, backed by a JSON file.\n */\nexport class TokenStore {\n private readonly file: string;\n private readonly workspacesDir: string;\n private tokens: Map<string, TokenInfo> = new Map();\n\n constructor(opts: TokenStoreOptions) {\n this.workspacesDir = opts.workspacesDir;\n this.file = join(opts.workspacesDir, \"tokens.json\");\n }\n\n /**\n * Load the token file from disk if it exists. Creates an empty store\n * otherwise. Idempotent — safe to call repeatedly.\n */\n load(): void {\n ensureDirSync(this.workspacesDir);\n if (!existsSync(this.file)) {\n this.tokens = new Map();\n return;\n }\n const raw = readFileSync(this.file, \"utf8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n getLogger(\"token-store\").error(\n { path: this.file },\n \"token store file is corrupt — starting with empty store\",\n );\n // Backup corrupt file for forensics\n try {\n copyFileSync(this.file, `${this.file}.corrupt.${Date.now()}`);\n } catch {\n /* best effort */\n }\n this.tokens = new Map();\n return;\n }\n const data = parsed as TokenFile;\n if (!data || typeof data !== \"object\" || !data.tokens) {\n this.tokens = new Map();\n return;\n }\n this.tokens = new Map(Object.entries(data.tokens));\n }\n\n /** Persist the current in-memory state atomically. */\n save(): void {\n const out: TokenFile = {\n version: 1,\n tokens: Object.fromEntries(this.tokens),\n };\n atomicWriteSync(this.file, JSON.stringify(out, null, 2) + \"\\n\");\n chmodSync(this.file, 0o600);\n }\n\n /**\n * Look up a token. Returns the principal on success, or null if the\n * token is unknown.\n *\n * **Timing:** This is an O(1) `Map.get` — it is **not** a constant-time\n * comparison. That is intentional (L-SEC-1). The threat model is a\n * network-level timing side-channel that distinguishes \"valid token\"\n * from \"invalid token\", which an attacker would use to enumerate the\n * token space. Our tokens are `wotw_<64hex>` — 32 bytes (256 bits) of\n * CSPRNG entropy from `node:crypto.randomBytes`. At a generous 10^9\n * guesses per second, a brute-force search of that space would take\n * on the order of 10^58 years; even with a timing oracle that leaks a\n * single bit per attempt the search is still ~10^19 years. No\n * constant-time comparison could meaningfully improve that margin, so\n * we accept the O(1) lookup for simplicity. If the token length or\n * entropy is ever reduced, this tradeoff must be revisited.\n */\n authenticate(token: string): Principal | null {\n if (!token) return null;\n const info = this.tokens.get(token);\n if (!info) return null;\n return { user: info.user };\n }\n\n /**\n * Add a user and return the generated token. Tokens are random 32-byte\n * values hex-encoded. Overwrites any existing entry with the same user\n * name — previous tokens for that user are revoked.\n */\n addUser(user: string): string {\n if (!user || user.trim().length === 0) {\n throw new Error(\"user name must be non-empty\");\n }\n const trimmed = user.trim();\n // Revoke prior tokens for the same user to enforce one active token per user.\n for (const [tok, info] of this.tokens) {\n if (info.user === trimmed) this.tokens.delete(tok);\n }\n const token = `wotw_${randomBytes(32).toString(\"hex\")}`;\n this.tokens.set(token, {\n user: trimmed,\n created: new Date().toISOString(),\n });\n this.save();\n return token;\n }\n\n /** Revoke a token. Returns true if a token was actually removed. */\n revokeToken(token: string): boolean {\n const existed = this.tokens.delete(token);\n if (existed) this.save();\n return existed;\n }\n\n /** Revoke all tokens belonging to a user. Returns count revoked. */\n revokeUser(user: string): number {\n const needle = user.trim();\n let count = 0;\n for (const [tok, info] of this.tokens) {\n if (info.user === needle) {\n this.tokens.delete(tok);\n count++;\n }\n }\n if (count > 0) this.save();\n return count;\n }\n\n /** List all users with their token creation timestamps. */\n listUsers(): Array<{ user: string; created: string }> {\n return Array.from(this.tokens.values()).map((info) => ({\n user: info.user,\n created: info.created,\n }));\n }\n\n /** Number of active tokens. */\n size(): number {\n return this.tokens.size;\n }\n\n /** Clear all tokens (for tests or administrative reset). */\n clear(): void {\n this.tokens.clear();\n this.save();\n }\n}\n","/**\n * Manages `wiki/index.md` — a generated catalog of every wiki page, grouped\n * by category, with link counts and last-updated dates. The file is\n * overwritten in full on every update; hand-edits outside the managed\n * sentinels are preserved at the bottom.\n */\nimport { join } from \"node:path\";\nimport { atomicWrite, readTextOrNullAsync } from \"../utils/fs.js\";\nimport type { WikiCategory, WikiPage } from \"../utils/types.js\";\nimport type { WikiStore } from \"./store.js\";\nimport { CATEGORY_DIRS } from \"./store.js\";\n\nconst SENTINEL_START = \"<!-- wotw:index:start -->\";\nconst SENTINEL_END = \"<!-- wotw:index:end -->\";\n\nconst CATEGORY_ORDER: WikiCategory[] = [\n \"concept\",\n \"entity\",\n \"source\",\n \"comparison\",\n \"synthesis\",\n \"query\",\n];\n\nconst CATEGORY_LABELS: Record<WikiCategory, string> = {\n concept: \"Concepts\",\n entity: \"Entities\",\n source: \"Sources\",\n comparison: \"Comparisons\",\n synthesis: \"Syntheses\",\n query: \"Queries\",\n};\n\nexport interface IndexEntry {\n category: WikiCategory;\n title: string;\n slug: string;\n relativePath: string;\n updated: string;\n confidence: string;\n linkCount: number;\n}\n\nexport class IndexManager {\n private readonly store: WikiStore;\n private readonly indexPath: string;\n\n constructor(store: WikiStore) {\n this.store = store;\n this.indexPath = join(store.wikiRoot, \"wiki\", \"index.md\");\n }\n\n /**\n * Rebuild the index from a list of parsed pages. Preserves any content\n * outside the managed sentinels.\n */\n async rebuild(pages: WikiPage[]): Promise<void> {\n const entries = pages.map((p) => toEntry(this.store, p));\n const generated = renderManagedBlock(entries);\n\n const existing = (await readTextOrNullAsync(this.indexPath)) ?? defaultIndexShell();\n const next = replaceManagedBlock(existing, generated);\n await atomicWrite(this.indexPath, next);\n }\n\n /** Read the wiki index as plain markdown, or null if missing. */\n async read(): Promise<string | null> {\n return readTextOrNullAsync(this.indexPath);\n }\n}\n\n/** Build a table row from a page. */\nfunction toEntry(store: WikiStore, page: WikiPage): IndexEntry {\n const rel = store.relativePath(page.path).replace(/\\\\/g, \"/\");\n return {\n category: page.frontmatter.category,\n title: page.frontmatter.title,\n slug: rel,\n relativePath: rel,\n updated: page.frontmatter.updated,\n confidence: page.frontmatter.confidence,\n linkCount: page.frontmatter.related.length,\n };\n}\n\nfunction renderManagedBlock(entries: IndexEntry[]): string {\n const lines: string[] = [];\n lines.push(SENTINEL_START);\n lines.push(\"# Wiki Index\");\n lines.push(\"\");\n lines.push(`_Auto-generated by watcher-on-the-wall. Last rebuilt: ${new Date().toISOString()}_`);\n lines.push(\"\");\n\n const grouped = new Map<WikiCategory, IndexEntry[]>();\n for (const cat of CATEGORY_ORDER) grouped.set(cat, []);\n for (const e of entries) {\n const bucket = grouped.get(e.category);\n if (bucket) bucket.push(e);\n }\n\n for (const cat of CATEGORY_ORDER) {\n const items = grouped.get(cat) ?? [];\n if (items.length === 0) continue;\n lines.push(`## ${CATEGORY_LABELS[cat]} (${items.length})`);\n lines.push(\"\");\n lines.push(\"| Title | Updated | Confidence | Links |\");\n lines.push(\"|---|---|---|---|\");\n items.sort((a, b) => a.title.localeCompare(b.title));\n for (const it of items) {\n lines.push(\n `| [${escapePipes(it.title)}](${it.relativePath}) | ${it.updated} | ${it.confidence} | ${it.linkCount} |`,\n );\n }\n lines.push(\"\");\n }\n\n if (entries.length === 0) {\n lines.push(\"_No wiki pages yet. Drop files into `raw/` to begin ingestion._\");\n lines.push(\"\");\n }\n\n lines.push(\n `**Totals:** ${entries.length} pages across ${CATEGORY_ORDER.filter((c) => (grouped.get(c) ?? []).length > 0).length} categories.`,\n );\n lines.push(\"\");\n lines.push(SENTINEL_END);\n return lines.join(\"\\n\");\n}\n\nfunction replaceManagedBlock(existing: string, generated: string): string {\n const startIdx = existing.indexOf(SENTINEL_START);\n const endIdx = existing.indexOf(SENTINEL_END);\n if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {\n return `${generated}\\n\\n${existing.trim()}\\n`;\n }\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + SENTINEL_END.length);\n return `${before}${generated}${after}`;\n}\n\nfunction defaultIndexShell(): string {\n return [\n \"# Wiki\",\n \"\",\n \"Welcome to the watcher-on-the-wall wiki. The block below is generated automatically.\",\n \"\",\n SENTINEL_START,\n SENTINEL_END,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction escapePipes(s: string): string {\n return s.replace(/\\|/g, \"\\\\|\");\n}\n\n/** Re-export category constants for callers that render indexes themselves. */\nexport { CATEGORY_DIRS };\n","/**\n * SQLite-backed fact store. Persistent layer for the Pass B fact +\n * synthetic-question index. Lives at `.wotw/facts.db` under the wiki\n * root. The daemon loads everything into a {@link FactIndex} (minisearch\n * BM25) at startup — same pattern as `WikiSearch` over the page corpus.\n *\n * Migrations are gated on `PRAGMA user_version`; this is the\n * version-1 initial schema. Future migrations bump the version + add\n * idempotent ALTER/CREATE statements.\n *\n * Backward compatibility: a daemon with no facts.db on disk opens the\n * DB on first use (creating the file + schema). Pre-existing wikis are\n * unaffected — the fact layer is opt-in via `fact_extraction.enabled`,\n * defaulting to \"auto\" (active only for cost-free runtimes).\n *\n * Concurrency: better-sqlite3 is synchronous + uses a single connection.\n * The daemon's pipeline is single-writer (IngestionQueue is serial),\n * so no in-process locking is required.\n *\n * Pass 008 BYOK: this module touches no API keys. The extractor that\n * fills it (`src/facts/extractor.ts`) reads keys at call-time via the\n * existing provider abstraction.\n */\nimport Database from \"better-sqlite3\";\nimport { createHash } from \"node:crypto\";\nimport { dirname } from \"node:path\";\nimport {\n looksLikeNativeBindingFailure,\n nativeBindingLoadError,\n} from \"../utils/actionable-error.js\";\nimport { ensureDirSync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { Fact, FactQuestion } from \"./types.js\";\n\nconst SCHEMA_VERSION = 1;\n\nconst SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS facts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n wiki_page_id TEXT NOT NULL,\n entity TEXT NOT NULL,\n statement TEXT NOT NULL,\n fact_hash TEXT UNIQUE NOT NULL,\n created_at TEXT NOT NULL,\n superseded_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_facts_wiki_page_id ON facts(wiki_page_id);\n CREATE INDEX IF NOT EXISTS idx_facts_active ON facts(wiki_page_id) WHERE superseded_at IS NULL;\n\n CREATE TABLE IF NOT EXISTS fact_questions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n fact_id INTEGER NOT NULL,\n question_text TEXT NOT NULL,\n question_hash TEXT UNIQUE NOT NULL,\n FOREIGN KEY(fact_id) REFERENCES facts(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_fact_questions_fact_id ON fact_questions(fact_id);\n`;\n\nexport interface FactStoreOptions {\n /** Absolute path to the SQLite file. Created if missing. */\n path: string;\n /** When true, open the DB in-memory (used by tests). */\n inMemory?: boolean;\n}\n\nexport interface InsertFactInput {\n wiki_page_id: string;\n entity: string;\n statement: string;\n}\n\n/**\n * The persistent fact store. All methods are synchronous because\n * better-sqlite3 is sync; the surrounding code awaits at higher layers\n * for consistency with the rest of the daemon's async surface.\n */\nexport class FactStore {\n readonly path: string;\n private readonly db: Database.Database;\n\n constructor(opts: FactStoreOptions) {\n this.path = opts.path;\n if (!opts.inMemory) {\n ensureDirSync(dirname(this.path));\n }\n try {\n this.db = new Database(opts.inMemory ? \":memory:\" : this.path);\n } catch (err) {\n if (looksLikeNativeBindingFailure(err)) {\n throw nativeBindingLoadError(\"better-sqlite3\", err);\n }\n throw err;\n }\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"foreign_keys = ON\");\n this.migrate();\n }\n\n /** Run idempotent migrations up to the current schema version. */\n private migrate(): void {\n const log = getLogger(\"fact-store\");\n const currentVersion = this.db.pragma(\"user_version\", { simple: true }) as number;\n if (currentVersion === SCHEMA_VERSION) return;\n if (currentVersion > SCHEMA_VERSION) {\n throw new Error(\n `facts.db at ${this.path} is at schema version ${currentVersion} (newer than this daemon's ${SCHEMA_VERSION}) — refusing to downgrade`,\n );\n }\n log.info({ from: currentVersion, to: SCHEMA_VERSION }, \"running facts.db migrations\");\n this.db.exec(SCHEMA_SQL);\n this.db.pragma(`user_version = ${SCHEMA_VERSION}`);\n }\n\n /**\n * Insert a fact + return its row id and assigned fact_hash. The hash\n * is computed from the canonical (entity + statement + wiki_page_id +\n * created_at) so repeat extractions land in distinct rows; supersession\n * links the lineage on the prior row.\n */\n insertFact(input: InsertFactInput): { id: number; fact_hash: string; created_at: string } {\n const created_at = new Date().toISOString();\n const fact_hash = factHash(input.entity, input.statement, input.wiki_page_id, created_at);\n const result = this.db\n .prepare(\n `INSERT INTO facts (wiki_page_id, entity, statement, fact_hash, created_at) VALUES (?, ?, ?, ?, ?)`,\n )\n .run(input.wiki_page_id, input.entity, input.statement, fact_hash, created_at);\n return { id: Number(result.lastInsertRowid), fact_hash, created_at };\n }\n\n /**\n * Bulk-insert synthetic questions for a fact. Each gets a content hash\n * computed from (fact_id + question_text). Duplicate questions for the\n * same fact (same hash) are silently skipped via INSERT OR IGNORE.\n */\n insertQuestions(factId: number, questions: string[]): FactQuestion[] {\n const insert = this.db.prepare(\n `INSERT OR IGNORE INTO fact_questions (fact_id, question_text, question_hash) VALUES (?, ?, ?)`,\n );\n const inserted: FactQuestion[] = [];\n const tx = this.db.transaction((qs: string[]) => {\n for (const q of qs) {\n if (typeof q !== \"string\" || q.trim().length === 0) continue;\n const hash = questionHash(factId, q);\n const result = insert.run(factId, q, hash);\n if (Number(result.changes) > 0) {\n inserted.push({\n id: Number(result.lastInsertRowid),\n fact_id: factId,\n question_text: q,\n question_hash: hash,\n });\n }\n }\n });\n tx(questions);\n return inserted;\n }\n\n /**\n * Supersede every active fact for `wikiPageId` (set their\n * `superseded_at`). Returns the list of superseded fact_hashes — the\n * caller writes these into a provenance record's\n * `fact_hashes_superseded` field.\n */\n supersedeByWikiPage(wikiPageId: string, when: string = new Date().toISOString()): string[] {\n const rows = this.db\n .prepare(`SELECT fact_hash FROM facts WHERE wiki_page_id = ? AND superseded_at IS NULL`)\n .all(wikiPageId) as { fact_hash: string }[];\n const hashes = rows.map((r) => r.fact_hash);\n if (hashes.length > 0) {\n this.db\n .prepare(\n `UPDATE facts SET superseded_at = ? WHERE wiki_page_id = ? AND superseded_at IS NULL`,\n )\n .run(when, wikiPageId);\n }\n return hashes;\n }\n\n /** Return every active fact across the corpus. */\n listActive(): Fact[] {\n return this.db\n .prepare(\n `SELECT id, wiki_page_id, entity, statement, fact_hash, created_at, superseded_at FROM facts WHERE superseded_at IS NULL`,\n )\n .all() as Fact[];\n }\n\n /** Return every question linked to facts that are still active. */\n listActiveQuestions(): FactQuestion[] {\n return this.db\n .prepare(\n `SELECT q.id, q.fact_id, q.question_text, q.question_hash\n FROM fact_questions q\n INNER JOIN facts f ON f.id = q.fact_id\n WHERE f.superseded_at IS NULL`,\n )\n .all() as FactQuestion[];\n }\n\n /** Look up a single fact by primary key (returns null if absent). */\n getFact(id: number): Fact | null {\n const row = this.db\n .prepare(\n `SELECT id, wiki_page_id, entity, statement, fact_hash, created_at, superseded_at FROM facts WHERE id = ?`,\n )\n .get(id) as Fact | undefined;\n return row ?? null;\n }\n\n /** All facts (active + superseded) for a given wiki page. */\n listByWikiPage(wikiPageId: string): Fact[] {\n return this.db\n .prepare(\n `SELECT id, wiki_page_id, entity, statement, fact_hash, created_at, superseded_at FROM facts WHERE wiki_page_id = ? ORDER BY created_at`,\n )\n .all(wikiPageId) as Fact[];\n }\n\n /** Total active fact count (cheap, used for \"is the layer populated?\" checks). */\n activeCount(): number {\n const row = this.db\n .prepare(`SELECT COUNT(*) as n FROM facts WHERE superseded_at IS NULL`)\n .get() as { n: number };\n return row.n;\n }\n\n /** Schema version reported by PRAGMA user_version (cheap, used by tests). */\n schemaVersion(): number {\n return this.db.pragma(\"user_version\", { simple: true }) as number;\n }\n\n /** Close the underlying handle. Idempotent. */\n close(): void {\n if (this.db.open) this.db.close();\n }\n}\n\n/** SHA-256 of canonical (entity, statement, wiki_page_id, created_at). */\nexport function factHash(\n entity: string,\n statement: string,\n wikiPageId: string,\n createdAt: string,\n): string {\n return createHash(\"sha256\")\n .update(`${wikiPageId}\u0000${entity}\u0000${statement}\u0000${createdAt}`)\n .digest(\"hex\");\n}\n\n/** SHA-256 of (fact_id, question_text). */\nexport function questionHash(factId: number, question: string): string {\n return createHash(\"sha256\").update(`${factId}\u0000${question}`).digest(\"hex\");\n}\n","/**\n * In-memory BM25 index over the fact + synthetic-question corpus.\n * Two parallel minisearch instances:\n *\n * - `factsEngine` — indexes the entity + statement of every active\n * fact.\n * - `questionsEngine` — indexes the question_text of every synthetic\n * question, tagged with its parent fact_id.\n *\n * At query time both engines run, then results are fused into a single\n * per-fact score using the weighting documented in the Pass B goal:\n * - questions: 0.6\n * - facts: 0.4\n *\n * Question-shape matching wins on weight because the Cambridge ALTA\n * pattern shows it dominates atomic-question quality (a user asking\n * \"what is X?\" hits the synthetic question for X more reliably than\n * keyword overlap on the underlying statement).\n *\n * BM25-only commitment: minisearch is the same BM25 implementation the\n * rest of the daemon uses for wiki pages. No vector code paths.\n */\nimport MiniSearch from \"minisearch\";\nimport type { Fact, FactQuestion } from \"./types.js\";\n\n/** Question-matched contribution weight to the fused score. */\nexport const QUESTION_WEIGHT = 0.6;\n/** Fact-text-matched contribution weight to the fused score. */\nexport const FACT_WEIGHT = 0.4;\n\ninterface FactDoc {\n id: number;\n entity: string;\n statement: string;\n fact_hash: string;\n wiki_page_id: string;\n}\n\ninterface QuestionDoc {\n id: number;\n question_text: string;\n fact_id: number;\n question_hash: string;\n}\n\nexport interface FactSearchHit {\n fact: Fact;\n /** Fused score across the questions + facts indices. */\n score: number;\n /** Whether this hit was matched via the questions index. */\n matched_via_question: boolean;\n /** Whether this hit was matched via the facts (entity/statement) index. */\n matched_via_fact: boolean;\n}\n\n/**\n * Two-engine fused BM25 index. Reuses the same minisearch dep as\n * `WikiSearch`. Empty until {@link rebuild} is called.\n */\nexport class FactIndex {\n private readonly factsEngine: MiniSearch<FactDoc>;\n private readonly questionsEngine: MiniSearch<QuestionDoc>;\n private factsById = new Map<number, Fact>();\n\n constructor() {\n this.factsEngine = new MiniSearch<FactDoc>({\n idField: \"id\",\n fields: [\"entity\", \"statement\"],\n storeFields: [\"fact_hash\", \"wiki_page_id\"],\n searchOptions: {\n boost: { entity: 3 },\n fuzzy: 0.2,\n prefix: true,\n combineWith: \"OR\",\n },\n });\n this.questionsEngine = new MiniSearch<QuestionDoc>({\n idField: \"id\",\n fields: [\"question_text\"],\n storeFields: [\"fact_id\", \"question_hash\"],\n searchOptions: {\n fuzzy: 0.2,\n prefix: true,\n combineWith: \"OR\",\n },\n });\n }\n\n /** Replace the in-memory index from the live persistent state. */\n rebuild(facts: Fact[], questions: FactQuestion[]): void {\n this.factsEngine.removeAll();\n this.questionsEngine.removeAll();\n this.factsById = new Map(facts.map((f) => [f.id, f]));\n if (facts.length > 0) {\n this.factsEngine.addAll(\n facts.map((f) => ({\n id: f.id,\n entity: f.entity,\n statement: f.statement,\n fact_hash: f.fact_hash,\n wiki_page_id: f.wiki_page_id,\n })),\n );\n }\n if (questions.length > 0) {\n this.questionsEngine.addAll(\n questions.map((q) => ({\n id: q.id,\n question_text: q.question_text,\n fact_id: q.fact_id,\n question_hash: q.question_hash,\n })),\n );\n }\n }\n\n /** Add a single fact + its questions to the index without a full rebuild. */\n add(fact: Fact, questions: FactQuestion[] = []): void {\n this.factsById.set(fact.id, fact);\n this.factsEngine.add({\n id: fact.id,\n entity: fact.entity,\n statement: fact.statement,\n fact_hash: fact.fact_hash,\n wiki_page_id: fact.wiki_page_id,\n });\n for (const q of questions) {\n this.questionsEngine.add({\n id: q.id,\n question_text: q.question_text,\n fact_id: q.fact_id,\n question_hash: q.question_hash,\n });\n }\n }\n\n /** Drop a fact (and any questions tagged to it) from the index. */\n remove(factId: number, questionIds: number[] = []): void {\n if (this.factsById.has(factId)) {\n this.factsEngine.remove({ id: factId } as FactDoc);\n this.factsById.delete(factId);\n }\n for (const qid of questionIds) {\n try {\n this.questionsEngine.remove({ id: qid } as QuestionDoc);\n } catch {\n // minisearch throws on remove-missing; ignore.\n }\n }\n }\n\n /**\n * BM25 search across both indices, fuse by weighted sum, return top-k\n * facts. When the query has zero hits across both engines, returns an\n * empty array (caller falls back to page-level retrieval).\n */\n search(query: string, k = 5): FactSearchHit[] {\n if (!query.trim() || this.factsById.size === 0) return [];\n\n const factHits = this.factsEngine.search(query);\n const questionHits = this.questionsEngine.search(query);\n\n // factId → { fact match score, question match score }.\n const merged = new Map<number, { factScore: number; questionScore: number }>();\n for (const h of factHits) {\n const id = h.id as number;\n const slot = merged.get(id) ?? { factScore: 0, questionScore: 0 };\n slot.factScore = Math.max(slot.factScore, h.score);\n merged.set(id, slot);\n }\n for (const h of questionHits) {\n const factId = (h as unknown as { fact_id: number }).fact_id;\n const slot = merged.get(factId) ?? { factScore: 0, questionScore: 0 };\n slot.questionScore = Math.max(slot.questionScore, h.score);\n merged.set(factId, slot);\n }\n\n const fused: FactSearchHit[] = [];\n for (const [id, { factScore, questionScore }] of merged) {\n const fact = this.factsById.get(id);\n if (!fact) continue;\n fused.push({\n fact,\n score: factScore * FACT_WEIGHT + questionScore * QUESTION_WEIGHT,\n matched_via_fact: factScore > 0,\n matched_via_question: questionScore > 0,\n });\n }\n fused.sort((a, b) => b.score - a.score);\n return fused.slice(0, k);\n }\n\n /** Number of active facts in the index. */\n size(): number {\n return this.factsById.size;\n }\n\n /** Total number of synthetic questions in the index. */\n questionCount(): number {\n return this.questionsEngine.documentCount;\n }\n}\n","/**\n * Fact extractor — the LLM-driven side of the Pass B layer.\n *\n * For a given wiki page, this module runs a single LLM call that\n * returns BOTH the atomic facts (Yanhong Li / TTIC factual\n * decomposition) AND the synthetic questions (Cambridge ALTA) for each\n * fact. Combining them in one round trip is the cheapest path: an\n * average page emits ~5-10 facts × 3 questions, and that's still one\n * provider call's worth of tokens.\n *\n * The daemon writes the result to {@link FactStore} and appends a\n * `fact_extracted` provenance record with the new + superseded\n * fact_hashes.\n *\n * Gating (cost-free by default):\n * - Ollama (local inference): zero per-call cost.\n * - Claude Code CLI: subscription-covered (zero metered cost).\n * - API mode (Anthropic / OpenAI / Gemini): opt-in via\n * `fact_extraction.force_enabled` to avoid silently amplifying\n * per-ingest cost.\n *\n * Pass 008 BYOK: extraction goes through the existing\n * `runtimeAwareComplete` wrapper, which honors the daemon's\n * provider-construction-time key read. No keys are logged or persisted\n * by this module.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\n\n/** Hard cap on the body bytes we feed to extraction. Matches query-engine. */\nconst MAX_PAGE_BODY_BYTES = 16 * 1024;\n\nexport interface ExtractedFact {\n entity: string;\n statement: string;\n questions: string[];\n}\n\nexport interface ExtractFactsResult {\n facts: ExtractedFact[];\n costUsd: number;\n inputTokens: number;\n outputTokens: number;\n durationMs: number;\n /** Raw LLM response text — kept for provenance hashing. */\n rawResponse: string;\n /** True when the LLM call ran (false on skip due to gating). */\n ran: boolean;\n /** Human-readable reason when ran=false. */\n skipReason?: string;\n}\n\nexport interface IsExtractionActiveResult {\n active: boolean;\n reason: string;\n}\n\n/**\n * Determine whether fact extraction should run given the daemon's\n * configured `fact_extraction.enabled` + the resolved runtime mode +\n * the configured provider. The reason string is stable and surfaced\n * in the daemon startup banner so the operator can see at a glance\n * why the layer is on or off.\n */\nexport function isExtractionActive(\n config: WotwConfig,\n runtimeMode: RuntimeMode,\n): IsExtractionActiveResult {\n const fx = config.fact_extraction;\n if (fx.enabled === false) {\n return { active: false, reason: \"fact_extraction.enabled=false (explicit)\" };\n }\n if (fx.enabled === true) {\n return { active: true, reason: \"fact_extraction.enabled=true (explicit)\" };\n }\n // enabled === \"auto\"\n if (runtimeMode === \"cli\") {\n return {\n active: true,\n reason: \"auto + runtime=cli (Claude Code CLI is subscription-covered, no metered cost)\",\n };\n }\n if (config.llm.provider === \"ollama\") {\n return {\n active: true,\n reason: \"auto + provider=ollama (local inference, no metered cost)\",\n };\n }\n if (fx.force_enabled) {\n return {\n active: true,\n reason: `auto + force_enabled=true + provider=${config.llm.provider} (operator opt-in to metered extraction)`,\n };\n }\n return {\n active: false,\n reason: `auto + metered provider=${config.llm.provider} (set fact_extraction.force_enabled=true to opt in)`,\n };\n}\n\nexport interface ExtractFactsOptions {\n config: WotwConfig;\n runtimeMode: RuntimeMode;\n /** Wiki-relative page path, only used in the prompt for context. */\n wikiPageId: string;\n /** Markdown body of the page (will be byte-clamped to 16 KB). */\n pageBody: string;\n /** Page title, used to anchor the prompt. */\n title: string;\n costTracker: CostTracker;\n /** Override the model. Defaults to config.models.lint. */\n model?: string;\n}\n\n/**\n * Run a single LLM call that emits atomic facts + N synthetic questions\n * per fact for the supplied page. Returns the parsed result + cost\n * telemetry. When extraction is gated off, returns `{ ran: false, ... }`\n * without making an LLM call.\n */\nexport async function extractFactsFromPage(opts: ExtractFactsOptions): Promise<ExtractFactsResult> {\n const log = getLogger(\"fact-extractor\");\n const active = isExtractionActive(opts.config, opts.runtimeMode);\n if (!active.active) {\n return {\n facts: [],\n costUsd: 0,\n inputTokens: 0,\n outputTokens: 0,\n durationMs: 0,\n rawResponse: \"\",\n ran: false,\n skipReason: active.reason,\n };\n }\n\n const started = Date.now();\n const model =\n opts.model ??\n opts.config.fact_extraction.model ??\n (opts.runtimeMode === \"cli\" ? opts.config.execution.cli_model : opts.config.models.lint);\n const questionsPerFact = clampQuestionsPerFact(opts.config.fact_extraction.questions_per_fact);\n const body = byteClamp(opts.pageBody, MAX_PAGE_BODY_BYTES);\n\n const systemPrompt = buildSystemPrompt(questionsPerFact);\n const userPrompt = buildUserPrompt(opts.title, opts.wikiPageId, body, questionsPerFact);\n\n let result;\n try {\n result = await runtimeAwareComplete(userPrompt, {\n systemPrompt,\n model,\n config: opts.config,\n runtimeMode: opts.runtimeMode,\n maxTokens: 4096,\n });\n } catch (err) {\n log.warn(\n {\n err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\",\n wikiPageId: opts.wikiPageId,\n },\n \"fact extraction LLM call failed\",\n );\n return {\n facts: [],\n costUsd: 0,\n inputTokens: 0,\n outputTokens: 0,\n durationMs: Date.now() - started,\n rawResponse: \"\",\n ran: true,\n skipReason: \"llm_error\",\n };\n }\n\n opts.costTracker.logUsage({\n operation: \"fact_extraction\",\n model,\n costUsd: result.costUsd,\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n });\n\n const facts = parseFactsResponse(result.text);\n return {\n facts,\n costUsd: result.costUsd,\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n durationMs: Date.now() - started,\n rawResponse: result.text,\n ran: true,\n };\n}\n\nfunction clampQuestionsPerFact(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 3;\n return Math.max(1, Math.min(5, Math.floor(value)));\n}\n\nfunction byteClamp(text: string, capBytes: number): string {\n const byteLen = Buffer.byteLength(text, \"utf8\");\n if (byteLen <= capBytes) return text;\n return Buffer.from(text, \"utf8\").subarray(0, capBytes).toString(\"utf8\");\n}\n\nfunction buildSystemPrompt(questionsPerFact: number): string {\n return `You are a fact-extraction assistant for a knowledge wiki.\n\nFor the wiki page provided, return a JSON object of the shape:\n{\n \"facts\": [\n {\n \"entity\": \"<short noun phrase identifying the subject>\",\n \"statement\": \"<single declarative sentence about the entity>\",\n \"questions\": [\"<question 1>\", \"<question 2>\", ...]\n }\n ]\n}\n\nRules:\n- Each fact must be a single atomic claim about exactly one entity.\n- \"entity\" should be a noun phrase of 1-6 words.\n- \"statement\" should be a single declarative sentence; do not include hedges or speculation.\n- Emit ${questionsPerFact} synthetic questions per fact that a user might ask whose answer is that fact.\n- Cover the page comprehensively but do not invent facts not present in the page.\n- Skip rhetorical / aspirational sentences (\"we believe\", \"may\", \"could\").\n- Respond ONLY with the JSON object, no surrounding prose, no markdown fences.`;\n}\n\nfunction buildUserPrompt(\n title: string,\n wikiPageId: string,\n body: string,\n questionsPerFact: number,\n): string {\n return `Wiki page: ${title}\nPath: ${wikiPageId}\n\nBody:\n${body}\n\nReturn the JSON fact decomposition with ${questionsPerFact} questions per fact.`;\n}\n\n/**\n * Parse the LLM response into a list of {@link ExtractedFact}. Tolerates\n * markdown fences (`\\`\\`\\`json ... \\`\\`\\``) and stray prose around the\n * JSON object. Returns an empty array on any parse failure so a single\n * malformed extraction doesn't fail the surrounding ingestion.\n */\nexport function parseFactsResponse(text: string): ExtractedFact[] {\n if (!text || typeof text !== \"string\") return [];\n // Strip code fences if present.\n let s = text.trim();\n const fenced = s.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/);\n if (fenced && fenced[1]) s = fenced[1].trim();\n // Try to locate the first {...} block.\n const objMatch = s.match(/\\{[\\s\\S]*\\}/);\n if (!objMatch) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(objMatch[0]);\n } catch {\n return [];\n }\n if (!parsed || typeof parsed !== \"object\") return [];\n const factsField = (parsed as { facts?: unknown }).facts;\n if (!Array.isArray(factsField)) return [];\n const out: ExtractedFact[] = [];\n for (const raw of factsField) {\n if (!raw || typeof raw !== \"object\") continue;\n const r = raw as Record<string, unknown>;\n const entity = typeof r.entity === \"string\" ? r.entity.trim() : \"\";\n const statement = typeof r.statement === \"string\" ? r.statement.trim() : \"\";\n if (!entity || !statement) continue;\n const questions: string[] = Array.isArray(r.questions)\n ? r.questions\n .filter((q): q is string => typeof q === \"string\")\n .map((q) => q.trim())\n .filter((q) => q.length > 0)\n : [];\n out.push({ entity, statement, questions });\n }\n return out;\n}\n","/**\n * Envelope encryption for workspace DEKs.\n *\n * Scheme: AES-256-GCM. The KEK is a 32-byte symmetric key held in a Fly\n * secret (env var `WOTW_WORKSPACE_KEK`, base64-encoded). Each DEK is\n * 32 random bytes, encrypted under the KEK with a fresh 12-byte nonce,\n * yielding ciphertext + nonce + 16-byte auth tag. Stored as three\n * separate BLOB columns in `workspace_keys` so callers don't need to\n * parse a concatenated layout.\n *\n * Security properties:\n * - AEAD via GCM authenticates the ciphertext: any tampering with the\n * stored bytes is detected at decrypt time (auth tag mismatch throws).\n * - Nonce is per-encryption (random 12 bytes). Reuse risk negligible at\n * the volumes a single daemon will ever produce.\n * - KEK never leaves the daemon process; it's read from env once at\n * startup.\n * - DEK plaintext is held in process memory only; never logged, never\n * serialized to disk, never sent over the wire.\n *\n * KEK rotation: deferred to a future pass (re-encrypt every DEK under\n * the new KEK in a single transaction). DEK rotation is in scope of\n * this pass — see `KeyStore.rotate()`.\n */\n\nimport { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\nimport type { EnvelopeCiphertext } from \"./types.js\";\n\nconst KEK_BYTES = 32;\nconst NONCE_BYTES = 12;\nconst AUTH_TAG_BYTES = 16;\nconst DEK_BYTES = 32;\nconst ALGORITHM = \"aes-256-gcm\" as const;\n\n/**\n * Parse the KEK from its env-var encoding. Accepts base64 (preferred)\n * or hex. Fails loud on wrong length or unparseable encoding so\n * misconfiguration is caught at startup, not at first encrypt.\n *\n * Returns a 32-byte Buffer.\n */\nexport function parseKek(raw: string): Buffer {\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new Error(\"WOTW_WORKSPACE_KEK is empty\");\n }\n // Try base64 first. base64 of 32 bytes is 44 chars (with padding) or 43\n // (without). Hex of 32 bytes is 64 chars. Heuristic: if it's all hex,\n // treat as hex; otherwise try base64.\n let buf: Buffer | null = null;\n if (/^[0-9a-fA-F]+$/.test(trimmed) && trimmed.length === KEK_BYTES * 2) {\n buf = Buffer.from(trimmed, \"hex\");\n } else {\n try {\n const decoded = Buffer.from(trimmed, \"base64\");\n if (decoded.length === KEK_BYTES) buf = decoded;\n } catch {\n // fall through\n }\n }\n if (!buf || buf.length !== KEK_BYTES) {\n throw new Error(\n `WOTW_WORKSPACE_KEK must decode to exactly ${KEK_BYTES} bytes (got base64 or hex of length ${trimmed.length}; decoded ${buf?.length ?? \"n/a\"} bytes)`,\n );\n }\n return buf;\n}\n\n/**\n * Read + parse the KEK from the environment. Throws if the env var is\n * absent or malformed.\n */\nexport function readKekFromEnv(env: NodeJS.ProcessEnv = process.env): Buffer {\n const raw = env.WOTW_WORKSPACE_KEK;\n if (!raw) {\n throw new Error(\"WOTW_WORKSPACE_KEK is not set in environment\");\n }\n return parseKek(raw);\n}\n\n/** Generate a fresh 32-byte DEK via the OS CSPRNG. */\nexport function generateDek(): Buffer {\n return randomBytes(DEK_BYTES);\n}\n\n/**\n * Encrypt a DEK under the KEK using AES-256-GCM with a fresh random\n * nonce. Returns the ciphertext, nonce, and 16-byte auth tag as\n * separate Buffers so the caller can store them in distinct columns.\n */\nexport function wrapDek(dek: Buffer, kek: Buffer): EnvelopeCiphertext {\n if (dek.length !== DEK_BYTES) {\n throw new Error(`DEK must be exactly ${DEK_BYTES} bytes (got ${dek.length})`);\n }\n if (kek.length !== KEK_BYTES) {\n throw new Error(`KEK must be exactly ${KEK_BYTES} bytes (got ${kek.length})`);\n }\n const nonce = randomBytes(NONCE_BYTES);\n const cipher = createCipheriv(ALGORITHM, kek, nonce);\n const ciphertext = Buffer.concat([cipher.update(dek), cipher.final()]);\n const auth_tag = cipher.getAuthTag();\n if (auth_tag.length !== AUTH_TAG_BYTES) {\n throw new Error(`unexpected AES-GCM auth tag length: ${auth_tag.length}`);\n }\n return { ciphertext, nonce, auth_tag };\n}\n\n/**\n * Decrypt an envelope back to the plaintext DEK. The auth_tag must\n * match exactly — any tampering with ciphertext, nonce, or tag throws.\n */\nexport function unwrapDek(env: EnvelopeCiphertext, kek: Buffer): Buffer {\n if (kek.length !== KEK_BYTES) {\n throw new Error(`KEK must be exactly ${KEK_BYTES} bytes (got ${kek.length})`);\n }\n if (env.nonce.length !== NONCE_BYTES) {\n throw new Error(`envelope nonce must be ${NONCE_BYTES} bytes (got ${env.nonce.length})`);\n }\n if (env.auth_tag.length !== AUTH_TAG_BYTES) {\n throw new Error(\n `envelope auth_tag must be ${AUTH_TAG_BYTES} bytes (got ${env.auth_tag.length})`,\n );\n }\n const decipher = createDecipheriv(ALGORITHM, kek, env.nonce);\n decipher.setAuthTag(env.auth_tag);\n const dek = Buffer.concat([decipher.update(env.ciphertext), decipher.final()]);\n if (dek.length !== DEK_BYTES) {\n throw new Error(`unwrapped DEK has wrong length: ${dek.length} (expected ${DEK_BYTES})`);\n }\n return dek;\n}\n","/**\n * SQLite-backed workspace key store. Persistent layer for the G5\n * end-to-end attestation substrate. Lives at `.wotw/keys.db` under\n * the wiki root.\n *\n * Each row is one DEK in its lifecycle. The plaintext DEK is never\n * stored — only the AES-256-GCM ciphertext under the KEK (held in a\n * Fly secret, see `./envelope.ts`). When the daemon needs a DEK for\n * signing or verifying, it unwraps from the row and caches the\n * plaintext in memory.\n *\n * Migrations follow the same `PRAGMA user_version` pattern as\n * `facts.db` from Pass B — see `src/facts/store.ts`. Schema v1 is\n * the initial workspace_keys table.\n *\n * Concurrency: better-sqlite3 is synchronous with a single\n * connection. The daemon is single-writer for chain mutations\n * (IngestionQueue concurrency 1, ProvenanceChain serialized by\n * writeLock); rotation calls share that single writer too.\n *\n * Pass 008 BYOK: KEK is read from env at construction time (via the\n * envelope module) and never logged. Plaintext DEKs returned by\n * `resolveById` / `active` must not be logged or transmitted by\n * callers; see envelope.ts for the threat model.\n */\n\nimport Database from \"better-sqlite3\";\nimport { randomUUID } from \"node:crypto\";\nimport { dirname } from \"node:path\";\nimport {\n looksLikeNativeBindingFailure,\n nativeBindingLoadError,\n} from \"../utils/actionable-error.js\";\nimport { ensureDirSync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport { generateDek, unwrapDek, wrapDek } from \"./envelope.js\";\nimport type { ResolvedWorkspaceKey, WorkspaceKeyRecord, WorkspaceKeyState } from \"./types.js\";\n\nconst SCHEMA_VERSION = 1;\n\nconst SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS workspace_keys (\n key_id TEXT PRIMARY KEY,\n workspace_id TEXT NOT NULL,\n key_state TEXT NOT NULL CHECK (key_state IN ('active','rotating','archived','revoked')),\n encrypted_dek BLOB NOT NULL,\n nonce BLOB NOT NULL,\n auth_tag BLOB NOT NULL,\n created_at TEXT NOT NULL,\n rotated_at TEXT,\n revoked_at TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_workspace_keys_workspace ON workspace_keys(workspace_id);\n CREATE INDEX IF NOT EXISTS idx_workspace_keys_state ON workspace_keys(workspace_id, key_state);\n -- At most one active key per workspace. Enforced at the DB level so a\n -- concurrent rotate() that interleaves wrongly cannot leave two active.\n CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_keys_one_active\n ON workspace_keys(workspace_id)\n WHERE key_state = 'active';\n`;\n\nexport interface KeyStoreOptions {\n path: string;\n /** KEK Buffer, 32 bytes. Required — never derived from env inside this module. */\n kek: Buffer;\n inMemory?: boolean;\n}\n\ntype Row = {\n key_id: string;\n workspace_id: string;\n key_state: WorkspaceKeyState;\n encrypted_dek: Buffer;\n nonce: Buffer;\n auth_tag: Buffer;\n created_at: string;\n rotated_at: string | null;\n revoked_at: string | null;\n};\n\n/**\n * The workspace key store. All public methods are synchronous —\n * better-sqlite3 is sync, and the surrounding daemon awaits at higher\n * layers for async consistency.\n */\nexport class KeyStore {\n readonly path: string;\n private readonly db: Database.Database;\n /**\n * Current KEK reference. Mutable so `rotateKek()` (PASS-019 Part B)\n * can swap it in-place atomically after a successful re-encrypt of\n * every non-revoked row. Never serialized; only held in memory.\n */\n private kek: Buffer;\n /** Cache of decrypted DEKs keyed by key_id. Bounded by the small number of keys per workspace. */\n private readonly dekCache: Map<string, Buffer>;\n\n constructor(opts: KeyStoreOptions) {\n this.path = opts.path;\n this.kek = opts.kek;\n this.dekCache = new Map();\n if (!opts.inMemory) {\n ensureDirSync(dirname(this.path));\n }\n try {\n this.db = new Database(opts.inMemory ? \":memory:\" : this.path);\n } catch (err) {\n if (looksLikeNativeBindingFailure(err)) {\n throw nativeBindingLoadError(\"better-sqlite3\", err);\n }\n throw err;\n }\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"foreign_keys = ON\");\n this.migrate();\n }\n\n private migrate(): void {\n const log = getLogger(\"key-store\");\n const currentVersion = this.db.pragma(\"user_version\", { simple: true }) as number;\n if (currentVersion === SCHEMA_VERSION) return;\n if (currentVersion > SCHEMA_VERSION) {\n throw new Error(\n `keys.db at ${this.path} is at schema version ${currentVersion} (newer than this daemon's ${SCHEMA_VERSION}) — refusing to downgrade`,\n );\n }\n log.info({ from: currentVersion, to: SCHEMA_VERSION }, \"running keys.db migrations\");\n this.db.exec(SCHEMA_SQL);\n this.db.pragma(`user_version = ${SCHEMA_VERSION}`);\n }\n\n /**\n * Provision a brand-new active DEK for the workspace. Fails if an\n * active DEK already exists (caller should `active()` first and\n * skip provisioning if so).\n *\n * Returns the resolved key (plaintext DEK + lineage). The plaintext\n * stays in process memory; never serialize it.\n */\n provision(workspaceId: string, now: string = new Date().toISOString()): ResolvedWorkspaceKey {\n const existing = this.activeRow(workspaceId);\n if (existing) {\n throw new Error(\n `cannot provision: workspace ${workspaceId} already has an active key (${existing.key_id})`,\n );\n }\n const key_id = randomUUID();\n const dek = generateDek();\n const env = wrapDek(dek, this.kek);\n this.db\n .prepare(\n `INSERT INTO workspace_keys (key_id, workspace_id, key_state, encrypted_dek, nonce, auth_tag, created_at)\n VALUES (?, ?, 'active', ?, ?, ?, ?)`,\n )\n .run(key_id, workspaceId, env.ciphertext, env.nonce, env.auth_tag, now);\n this.dekCache.set(key_id, dek);\n return { key_id, workspace_id: workspaceId, key_state: \"active\", dek };\n }\n\n /**\n * Return the workspace's currently active DEK, or null if none.\n * Caches plaintext on first decrypt; subsequent calls are cheap.\n */\n active(workspaceId: string): ResolvedWorkspaceKey | null {\n const row = this.activeRow(workspaceId);\n if (!row) return null;\n return this.toResolved(row);\n }\n\n /**\n * Resolve a DEK by its key_id, across ALL states (active, rotating,\n * archived, revoked). Used by the verifier: it must check HMACs on\n * records signed under any historical key, including revoked ones\n * (revoke is forensic — verify still surfaces the records, the\n * operator decides whether to trust them).\n *\n * Returns null if no row matches. Throws if the row exists but the\n * envelope fails to decrypt (KEK mismatch or tampered ciphertext).\n */\n resolveById(keyId: string): ResolvedWorkspaceKey | null {\n const cached = this.dekCache.get(keyId);\n if (cached) {\n const row = this.db\n .prepare(`SELECT key_id, workspace_id, key_state FROM workspace_keys WHERE key_id = ?`)\n .get(keyId) as\n | { key_id: string; workspace_id: string; key_state: WorkspaceKeyState }\n | undefined;\n if (!row) return null;\n return { ...row, dek: cached };\n }\n const row = this.db.prepare(`SELECT * FROM workspace_keys WHERE key_id = ?`).get(keyId) as\n | Row\n | undefined;\n if (!row) return null;\n return this.toResolved(row);\n }\n\n /**\n * Rotate the workspace's active DEK. Atomically: provisions a new\n * active DEK and transitions the previous active to `rotating`\n * (with `rotated_at = now`). New appends sign under the new DEK;\n * verify still recognizes records signed by the `rotating` DEK\n * during the overlap window.\n *\n * Returns the new active key.\n */\n rotate(\n workspaceId: string,\n now: string = new Date().toISOString(),\n ): { previous: ResolvedWorkspaceKey | null; current: ResolvedWorkspaceKey } {\n return this.db.transaction(() => {\n const prevRow = this.activeRow(workspaceId);\n let previous: ResolvedWorkspaceKey | null = null;\n if (prevRow) {\n this.db\n .prepare(\n `UPDATE workspace_keys SET key_state = 'rotating', rotated_at = ? WHERE key_id = ?`,\n )\n .run(now, prevRow.key_id);\n previous = {\n key_id: prevRow.key_id,\n workspace_id: prevRow.workspace_id,\n key_state: \"rotating\",\n dek: this.unwrapWithCache(prevRow),\n };\n }\n const key_id = randomUUID();\n const dek = generateDek();\n const env = wrapDek(dek, this.kek);\n this.db\n .prepare(\n `INSERT INTO workspace_keys (key_id, workspace_id, key_state, encrypted_dek, nonce, auth_tag, created_at)\n VALUES (?, ?, 'active', ?, ?, ?, ?)`,\n )\n .run(key_id, workspaceId, env.ciphertext, env.nonce, env.auth_tag, now);\n this.dekCache.set(key_id, dek);\n const current: ResolvedWorkspaceKey = {\n key_id,\n workspace_id: workspaceId,\n key_state: \"active\",\n dek,\n };\n return { previous, current };\n })();\n }\n\n /**\n * Archive a `rotating` DEK after its overlap window expires. After\n * archive, the DEK is verify-only (no new appends signed under it,\n * but old records still verify). Idempotent: archiving an already-\n * archived key is a no-op.\n *\n * Returns true if a row transitioned, false if no-op.\n */\n archive(keyId: string): boolean {\n const result = this.db\n .prepare(\n `UPDATE workspace_keys SET key_state = 'archived' WHERE key_id = ? AND key_state = 'rotating'`,\n )\n .run(keyId);\n return Number(result.changes) > 0;\n }\n\n /**\n * Archive every `rotating` DEK whose `rotated_at` is older than the\n * overlap window. Bulk version of `archive()` for the auto-archive\n * cron (PASS-019 Part C). Idempotent.\n *\n * @param workspaceId the workspace to scan\n * @param overlapMs overlap window in milliseconds. Rows whose\n * `rotated_at` is older than `now - overlapMs`\n * transition to `archived`. Pass the configured\n * `WOTW_DEK_OVERLAP_HOURS * 3600 * 1000` here.\n * @param now injected for testability (ISO string). Defaults\n * to the current time.\n * @returns the list of key_ids that transitioned\n */\n archiveOverlapped(\n workspaceId: string,\n overlapMs: number,\n now: string = new Date().toISOString(),\n ): string[] {\n // Compute the cutoff timestamp. Anything rotated_at older than this\n // is past the overlap window and should be archived. Doing the date\n // arithmetic in JS rather than SQLite so we control the time source\n // for tests (`now` is injectable).\n const cutoff = new Date(new Date(now).getTime() - overlapMs).toISOString();\n return this.db.transaction(() => {\n const candidates = this.db\n .prepare(\n `SELECT key_id FROM workspace_keys\n WHERE workspace_id = ?\n AND key_state = 'rotating'\n AND rotated_at IS NOT NULL\n AND rotated_at <= ?`,\n )\n .all(workspaceId, cutoff) as { key_id: string }[];\n if (candidates.length === 0) return [];\n const ids = candidates.map((r) => r.key_id);\n const placeholders = ids.map(() => \"?\").join(\",\");\n this.db\n .prepare(\n `UPDATE workspace_keys SET key_state = 'archived'\n WHERE key_id IN (${placeholders}) AND key_state = 'rotating'`,\n )\n .run(...ids);\n return ids;\n })();\n }\n\n /**\n * Re-encrypt every non-revoked DEK in the store under a new KEK\n * (PASS-019 Part B). Single SQLite transaction — partial failure\n * rolls back, the store's effective KEK stays unchanged. On\n * success, `this.kek` is updated to `newKek` so future\n * provision/rotate calls use the new KEK.\n *\n * Revoked DEKs are skipped: they're forensic; if a row's encrypted\n * material can't unwrap under the old KEK (corruption, prior\n * tampering), the rotation fails LOUD rather than silently dropping\n * the row.\n *\n * Idempotent in semantics: re-running with the same `newKek` produces\n * new ciphertexts (fresh nonces) but the underlying DEK plaintext is\n * unchanged, so verify continues to work either way.\n *\n * Operator runbook: see `docs/policies/kek-rotation.md`. The Fly\n * secret swap is OUT of the daemon's hands — the operator sets\n * `WOTW_WORKSPACE_KEK_NEW` alongside the existing\n * `WOTW_WORKSPACE_KEK`, runs this op, then swaps the Fly secret and\n * restarts the daemon. KEK plaintext is never logged or printed.\n *\n * @returns count of DEK rows re-encrypted (active + rotating + archived)\n * @throws if any row fails to unwrap under the old KEK; transaction\n * rolls back and `this.kek` is unchanged\n */\n rotateKek(newKek: Buffer): { rotated: number } {\n if (newKek.length !== 32) {\n throw new Error(`new KEK must be exactly 32 bytes (got ${newKek.length})`);\n }\n const result = this.db.transaction(() => {\n // Read every non-revoked DEK. Revoked keys are deliberately left\n // under the old KEK — they're terminal forensic records and\n // re-encrypting them would mask any KEK compromise that motivated\n // the revocation.\n const rows = this.db\n .prepare(\n `SELECT key_id, encrypted_dek, nonce, auth_tag FROM workspace_keys\n WHERE key_state != 'revoked'`,\n )\n .all() as {\n key_id: string;\n encrypted_dek: Buffer;\n nonce: Buffer;\n auth_tag: Buffer;\n }[];\n const update = this.db.prepare(\n `UPDATE workspace_keys\n SET encrypted_dek = ?, nonce = ?, auth_tag = ?\n WHERE key_id = ?`,\n );\n for (const r of rows) {\n // Decrypt under the CURRENT (old) KEK. If this throws, the\n // transaction rolls back and `this.kek` is unchanged.\n const dek = unwrapDek(\n { ciphertext: r.encrypted_dek, nonce: r.nonce, auth_tag: r.auth_tag },\n this.kek,\n );\n // Re-wrap under the new KEK with a fresh nonce.\n const wrapped = wrapDek(dek, newKek);\n update.run(wrapped.ciphertext, wrapped.nonce, wrapped.auth_tag, r.key_id);\n }\n return { rotated: rows.length };\n })();\n // Commit-then-swap. If commit failed we wouldn't reach here.\n // Clear the cache because the underlying ciphertext changed — any\n // stale cached DEK is still correct (plaintext unchanged) but the\n // cache invariant (cache miss → re-decrypt under current KEK)\n // requires the KEK reference to match what the rows are encrypted\n // under. Re-populating lazily on next resolveById is cheap.\n this.dekCache.clear();\n // Swap in the new KEK reference last — anything before this that\n // throws leaves the store consistent with the old KEK.\n this.kek = newKek;\n return result;\n }\n\n /**\n * Revoke a DEK from any state (active, rotating, archived).\n * `revoked` is terminal — records signed under this DEK still\n * verify cryptographically (the math doesn't care about state),\n * but operators see `revoked` and decide whether to trust those\n * records. Compromise response.\n *\n * Returns true if a row transitioned, false if no-op.\n */\n revoke(keyId: string, now: string = new Date().toISOString()): boolean {\n const result = this.db\n .prepare(\n `UPDATE workspace_keys SET key_state = 'revoked', revoked_at = ? WHERE key_id = ? AND key_state != 'revoked'`,\n )\n .run(now, keyId);\n return Number(result.changes) > 0;\n }\n\n /**\n * Return every row for the workspace, ordered by created_at ascending.\n * Includes encrypted_dek + nonce + auth_tag for forensic inspection;\n * decryption happens lazily via `resolveById`.\n */\n listAll(workspaceId: string): WorkspaceKeyRecord[] {\n return this.db\n .prepare(\n `SELECT key_id, workspace_id, key_state, encrypted_dek, nonce, auth_tag, created_at, rotated_at, revoked_at\n FROM workspace_keys WHERE workspace_id = ? ORDER BY created_at ASC`,\n )\n .all(workspaceId) as WorkspaceKeyRecord[];\n }\n\n /** Count keys for a workspace by state. */\n countByState(workspaceId: string): Record<WorkspaceKeyState, number> {\n const rows = this.db\n .prepare(\n `SELECT key_state, COUNT(*) as n FROM workspace_keys WHERE workspace_id = ? GROUP BY key_state`,\n )\n .all(workspaceId) as { key_state: WorkspaceKeyState; n: number }[];\n const out: Record<WorkspaceKeyState, number> = {\n active: 0,\n rotating: 0,\n archived: 0,\n revoked: 0,\n };\n for (const r of rows) out[r.key_state] = r.n;\n return out;\n }\n\n /** Schema version (cheap, used by tests + diagnostics). */\n schemaVersion(): number {\n return this.db.pragma(\"user_version\", { simple: true }) as number;\n }\n\n /** Close the underlying handle. Idempotent. */\n close(): void {\n if (this.db.open) this.db.close();\n this.dekCache.clear();\n }\n\n private activeRow(workspaceId: string): Row | undefined {\n return this.db\n .prepare(\n `SELECT * FROM workspace_keys WHERE workspace_id = ? AND key_state = 'active' LIMIT 1`,\n )\n .get(workspaceId) as Row | undefined;\n }\n\n private toResolved(row: Row): ResolvedWorkspaceKey {\n return {\n key_id: row.key_id,\n workspace_id: row.workspace_id,\n key_state: row.key_state,\n dek: this.unwrapWithCache(row),\n };\n }\n\n private unwrapWithCache(row: Row): Buffer {\n const cached = this.dekCache.get(row.key_id);\n if (cached) return cached;\n const dek = unwrapDek(\n { ciphertext: row.encrypted_dek, nonce: row.nonce, auth_tag: row.auth_tag },\n this.kek,\n );\n this.dekCache.set(row.key_id, dek);\n return dek;\n }\n}\n","/**\n * Daemon main loop. Initializes subsystems, wires up signal handlers, and keeps\n * the event loop alive until a graceful shutdown is requested.\n *\n * In Phase 1 the daemon owns the PID file, logger, and signal handlers. Phases 2-4\n * progressively attach watcher, ingestion, MCP server, and provenance subsystems\n * by calling {@link Daemon.attachSubsystem}.\n */\nimport { loadConfig, resolveConfigPaths } from \"./config.js\";\nimport { acquireStartLock, checkDaemonAlive, removePidFile, writePidFile } from \"./lifecycle.js\";\nimport { getLogger, initLogger } from \"../utils/logger.js\";\nimport type { WotwConfig } from \"../utils/types.js\";\nimport {\n daemonAlreadyRunningError,\n looksLikePermissionDenied,\n wikiDirPermissionError,\n} from \"../utils/actionable-error.js\";\nimport { ensureDirSync } from \"../utils/fs.js\";\n\nfunction ensureDirSyncOrActionable(path: string): void {\n try {\n ensureDirSync(path);\n } catch (err) {\n if (looksLikePermissionDenied(err)) {\n throw wikiDirPermissionError(path, err);\n }\n throw err;\n }\n}\nimport { VERSION } from \"../utils/version.js\";\nimport {\n resolveExecutionMode,\n type ResolvedExecutionMode,\n ExecutionModeError,\n} from \"../ingestion/execution-mode.js\";\n\n/** A daemon subsystem that can start and stop cleanly. */\nexport interface DaemonSubsystem {\n name: string;\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\nexport interface DaemonOptions {\n configPath: string | null;\n workingDir: string;\n}\n\n/**\n * The Daemon class holds runtime state and coordinates subsystem lifecycle.\n */\nexport class Daemon {\n private readonly subsystems: DaemonSubsystem[] = [];\n private shuttingDown = false;\n private readonly opts: DaemonOptions;\n private config: WotwConfig | null = null;\n private executionMode: ResolvedExecutionMode | null = null;\n private releaseLock: (() => Promise<void>) | null = null;\n\n constructor(opts: DaemonOptions) {\n this.opts = opts;\n }\n\n /** Resolve config and return it. */\n async init(): Promise<WotwConfig> {\n const loaded = await loadConfig(this.opts.workingDir);\n this.config = resolveConfigPaths(loaded.config, this.opts.workingDir);\n\n // Initialize logger against the resolved log file. Foreground runs set\n // WOTW_LOG_STDOUT=1 so logs stream (pretty) to the inherited terminal\n // instead of disappearing into the log file — otherwise `wotw start\n // --foreground` looks silent (PASS-023 dogfood finding #18).\n const logToStdout =\n process.env.WOTW_LOG_STDOUT === \"1\" || process.env.WOTW_LOG_STDOUT === \"true\";\n initLogger(this.config.daemon.log_level, logToStdout ? undefined : this.config.daemon.log_file);\n const log = getLogger(\"daemon\");\n log.info(\n {\n pid: process.pid,\n cwd: this.opts.workingDir,\n configPath: loaded.path,\n },\n \"daemon initializing\",\n );\n\n ensureDirSyncOrActionable(this.config.wiki_root);\n ensureDirSyncOrActionable(this.config.raw_path);\n\n // Resolve execution mode BEFORE starting any subsystem. If neither a\n // claude CLI binary nor an API key is available, refuse to start with a\n // precise error message — downstream code would crash in confusing ways\n // without this guard.\n try {\n this.executionMode = resolveExecutionMode(this.config);\n } catch (err) {\n if (err instanceof ExecutionModeError) {\n log.fatal({ code: err.code }, err.message);\n } else {\n log.fatal({ err }, \"failed to resolve execution mode\");\n }\n throw err;\n }\n // Log the resolved mode prominently so the user always knows which\n // runtime is active and what it costs.\n log.info(\n {\n mode: this.executionMode.mode,\n configured: this.executionMode.configuredMode,\n cliPath: this.executionMode.cliPath,\n apiKeyEnv: this.executionMode.apiKeyEnv,\n model: this.executionMode.effectiveModelHint,\n },\n this.executionMode.description,\n );\n\n return this.config;\n }\n\n /** Return the resolved execution mode, or null if init() hasn't run yet. */\n getExecutionMode(): ResolvedExecutionMode | null {\n return this.executionMode;\n }\n\n /** Attach a subsystem for start/stop management. */\n attachSubsystem(sub: DaemonSubsystem): void {\n this.subsystems.push(sub);\n }\n\n /**\n * Main run loop. Acquires the start lock, writes the PID file, starts all\n * subsystems, installs signal handlers, and blocks until shutdown.\n */\n async run(): Promise<void> {\n if (!this.config) throw new Error(\"Daemon.init() must be called before run()\");\n const log = getLogger(\"daemon\");\n\n // Guard against double-start\n const alive = checkDaemonAlive(this.config.daemon.pid_file);\n if (alive.alive) {\n log.error({ pid: alive.pid }, \"another daemon instance is already running\");\n throw daemonAlreadyRunningError(this.config.daemon.pid_file);\n }\n\n // Acquire lock to prevent simultaneous starts\n try {\n this.releaseLock = await acquireStartLock(this.config.daemon.lock_file);\n } catch (err) {\n log.error({ err }, \"failed to acquire start lock\");\n throw daemonAlreadyRunningError(this.config.daemon.lock_file, err);\n }\n\n // Write PID file\n writePidFile(this.config.daemon.pid_file, {\n pid: process.pid,\n started_at: new Date().toISOString(),\n version: VERSION,\n });\n log.info({ pidFile: this.config.daemon.pid_file }, \"PID file written\");\n\n // Install signal handlers BEFORE starting subsystems so any crash during\n // startup is handled cleanly.\n this.installSignalHandlers();\n\n // Start subsystems in order\n for (const sub of this.subsystems) {\n try {\n log.info({ subsystem: sub.name }, \"starting subsystem\");\n await sub.start();\n } catch (err) {\n log.error({ err, subsystem: sub.name }, \"subsystem failed to start\");\n await this.shutdown(1);\n return;\n }\n }\n\n log.info({ subsystems: this.subsystems.map((s) => s.name) }, \"daemon running\");\n\n // Block until shutdown. The check interval intentionally does NOT unref,\n // so it keeps the event loop alive even when no subsystems are attached.\n // Once this.shuttingDown flips true we clear it and resolve.\n await new Promise<void>((resolve) => {\n const check = setInterval(() => {\n if (this.shuttingDown) {\n clearInterval(check);\n resolve();\n }\n }, 250);\n });\n }\n\n /** Install SIGTERM / SIGINT handlers for graceful shutdown. */\n private installSignalHandlers(): void {\n const handle = (signal: NodeJS.Signals): void => {\n const log = getLogger(\"daemon\");\n log.info({ signal }, \"received shutdown signal\");\n void this.shutdown(0);\n };\n process.on(\"SIGTERM\", handle);\n process.on(\"SIGINT\", handle);\n process.on(\"uncaughtException\", (err) => {\n const log = getLogger(\"daemon\");\n log.fatal({ err }, \"uncaught exception\");\n void this.shutdown(1);\n });\n process.on(\"unhandledRejection\", (reason) => {\n const log = getLogger(\"daemon\");\n log.fatal(\n { reason: reason instanceof Error ? reason.message : String(reason) },\n \"unhandled rejection — shutting down\",\n );\n void this.shutdown(1);\n });\n }\n\n /** Stop all subsystems, release the lock, remove the PID file, and exit. */\n async shutdown(exitCode: number): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n const log = getLogger(\"daemon\");\n log.info(\"daemon shutting down\");\n\n // Stop subsystems in reverse order\n for (const sub of [...this.subsystems].reverse()) {\n try {\n await sub.stop();\n log.info({ subsystem: sub.name }, \"subsystem stopped\");\n } catch (err) {\n log.error({ err, subsystem: sub.name }, \"subsystem stop failed\");\n }\n }\n\n // Remove PID file\n if (this.config) {\n removePidFile(this.config.daemon.pid_file);\n }\n\n // Release lock\n if (this.releaseLock) {\n try {\n await this.releaseLock();\n } catch {\n /* ignore */\n }\n }\n\n log.info(\"daemon shutdown complete\");\n // Give pino a tick to flush\n await new Promise((r) => setTimeout(r, 50));\n process.exit(exitCode);\n }\n}\n","/**\n * Periodic background lint. Runs the same structural sweep as\n * `wotw lint` on a timer so the operator gets a heads-up when orphaned\n * pages accumulate or future sub-lints start reporting issues.\n *\n * Implements {@link DaemonSubsystem}. The interval is unref'd so it\n * never keeps the event loop alive on its own — the daemon's internal\n * keep-alive is what actually holds the process open. Respects\n * `config.lint.schedule_enabled`: when false, `start()` is a cheap\n * no-op and the scheduler logs one INFO line explaining itself.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport type { WotwConfig } from \"../utils/types.js\";\nimport { runLintPass, type LintResult } from \"../cli/commands/lint.js\";\nimport type { DaemonSubsystem } from \"./index.js\";\n\nexport interface LintSchedulerOptions {\n config: WotwConfig;\n /**\n * Override the lint runner — only used by tests so they can assert\n * the scheduler is calling through on each interval without touching\n * the filesystem.\n */\n runner?: (config: WotwConfig, opts?: { fix?: boolean; yes?: boolean }) => Promise<LintResult>;\n}\n\n/** One hour in milliseconds — unit used by the interval math. */\nconst MS_PER_HOUR = 60 * 60 * 1000;\n\nexport class LintScheduler implements DaemonSubsystem {\n readonly name = \"lint-scheduler\";\n private readonly opts: LintSchedulerOptions;\n private timer: NodeJS.Timeout | null = null;\n /** Last computed result — exposed for tests and future status output. */\n private lastResult: LintResult | null = null;\n\n constructor(opts: LintSchedulerOptions) {\n this.opts = opts;\n }\n\n async start(): Promise<void> {\n const log = getLogger(\"lint-scheduler\");\n const { schedule_enabled: enabled, interval_hours: intervalHours } = this.opts.config.lint;\n if (!enabled) {\n log.info(\"lint scheduler disabled (set lint.schedule_enabled=true to enable)\");\n return;\n }\n const intervalMs = Math.max(1, Math.round(intervalHours * MS_PER_HOUR));\n log.info({ intervalHours, intervalMs }, \"lint scheduler starting\");\n // Review item 31: pre-fix runOnce was fire-and-forget; if one tick's\n // runOnce was still in flight when the next interval fired, two\n // concurrent runs would race on writes, search-rebuild, provenance,\n // git-commit, and cost-tracker. Gate interval ticks with an\n // in-flight flag so a tardy run skips one cycle instead of racing.\n // The startup tick is not gated (it's the first run and has nothing\n // to race with) so observers can see the initial state immediately.\n let inFlight = false;\n const startupPromise = this.runOnce()\n .catch(() => undefined)\n .finally(() => {\n // Mark the startup run as no-longer-blocking — it took its turn.\n });\n void startupPromise;\n const timer = setInterval(() => {\n if (inFlight) {\n log.warn({}, \"lint scheduler skipping tick — previous runOnce still in flight\");\n return;\n }\n inFlight = true;\n void this.runOnce().finally(() => {\n inFlight = false;\n });\n }, intervalMs);\n // Do NOT keep the event loop alive for the scheduler — the daemon's\n // own check-interval is the keep-alive. Without unref() a stopped\n // daemon would hang waiting for the next lint tick.\n timer.unref();\n this.timer = timer;\n }\n\n async stop(): Promise<void> {\n const log = getLogger(\"lint-scheduler\");\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n log.info(\"lint scheduler stopped\");\n }\n }\n\n /** Last result produced by the scheduler — null until the first run. */\n getLastResult(): LintResult | null {\n return this.lastResult;\n }\n\n /**\n * Run a single lint pass and log the result. Exposed for tests and for\n * callers that want to force a pass outside the interval cadence.\n */\n async runOnce(): Promise<LintResult | null> {\n const log = getLogger(\"lint-scheduler\");\n const runner = this.opts.runner ?? runLintPass;\n const autoFix = this.opts.config.lint.auto_fix === true;\n try {\n const result = await runner(this.opts.config, autoFix ? { fix: true, yes: true } : undefined);\n this.lastResult = result;\n if (result.missingWikiDir) {\n log.warn(\n { wikiRoot: result.wikiRoot },\n \"lint scheduler: wiki directory missing — skipping sweep\",\n );\n } else if (result.issueCount > 0) {\n log.warn(\n {\n totalPages: result.totalPages,\n orphanedPages: result.orphanedPages,\n issueCount: result.issueCount,\n },\n \"lint scheduler: issues found\",\n );\n } else {\n log.info({ totalPages: result.totalPages }, \"lint scheduler: clean sweep — no issues\");\n }\n // After lint, check zero-hit rate and run vocabulary enrichment if needed.\n if (this.opts.config.health.enrichment_enabled) {\n try {\n const { computeZeroHitRate } = await import(\"../server/query-metrics.js\");\n const metrics = computeZeroHitRate(this.opts.config.health.query_log_file);\n if (metrics.zero_hit_rate > this.opts.config.health.zero_hit_threshold) {\n log.info(\n { rate: (metrics.zero_hit_rate * 100).toFixed(0) },\n \"zero-hit rate exceeds threshold — vocabulary enrichment would run\",\n );\n // Enrichment requires full context (store, search, etc.) that the scheduler\n // doesn't have. In daemon mode, enrichment runs via `wotw lint --fix` which\n // has the full heal context. Log for observability here.\n }\n } catch {\n // Non-fatal — metrics computation may fail if log file doesn't exist yet.\n }\n }\n\n return result;\n } catch (err) {\n log.error({ err }, \"lint scheduler: sweep failed\");\n return null;\n }\n }\n}\n","/**\n * Auto-archive cron for workspace DEKs past their rotation overlap\n * window (PASS-019 Part C).\n *\n * After `KeyStore.rotate()` transitions the previous active DEK to\n * `rotating`, the daemon needs to eventually transition it to\n * `archived` so it stops appearing in operator-facing\n * `state='rotating'` queries that suggest a rotation is still in\n * progress. This scheduler does it on a configurable interval —\n * default hourly tick, default 24-hour overlap window.\n *\n * Implements {@link DaemonSubsystem}. The interval is unref'd; the\n * daemon's own keepalive holds the process. Pattern mirrors\n * `LintScheduler` in this same directory.\n *\n * Operator override: `WOTW_DEK_OVERLAP_HOURS` env var. The same CLI\n * subcommand (`wotw workspace archive-overlapped`) can force-archive\n * outside the cron cadence.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport type { KeyStore } from \"../keys/store.js\";\nimport type { DaemonSubsystem } from \"./index.js\";\n\n/** Default overlap window. Operators can override via WOTW_DEK_OVERLAP_HOURS. */\nconst DEFAULT_OVERLAP_HOURS = 24;\n/** How often the cron ticks. Operators don't override this — tick cadence is independent of overlap window. */\nconst TICK_INTERVAL_HOURS = 1;\nconst MS_PER_HOUR = 60 * 60 * 1000;\n\nexport interface DekArchiveSchedulerOptions {\n keyStore: KeyStore;\n workspaceId: string;\n /**\n * Overlap window in hours. Records signed by a `rotating` DEK still\n * verify; the only effect of archiving is the lifecycle-state label.\n * Defaults to `WOTW_DEK_OVERLAP_HOURS` env or 24h.\n */\n overlapHours?: number;\n /**\n * Tick cadence in hours. Default 1h. Exposed for tests so they can\n * trigger ticks at a faster cadence without waiting for real time.\n */\n tickIntervalHours?: number;\n /**\n * Optional time-source override. Tests use `vi.useFakeTimers()` +\n * inject `() => new Date(Date.now()).toISOString()` to control the\n * reference clock without monkey-patching globals.\n */\n now?: () => string;\n}\n\nexport class DekArchiveScheduler implements DaemonSubsystem {\n readonly name = \"dek-archive-scheduler\";\n private readonly opts: DekArchiveSchedulerOptions;\n private timer: NodeJS.Timeout | null = null;\n /** Last cron result, for tests + future status output. */\n private lastResult: { archived: number; archivedAt: string } | null = null;\n\n constructor(opts: DekArchiveSchedulerOptions) {\n this.opts = opts;\n }\n\n async start(): Promise<void> {\n const log = getLogger(\"dek-archive-scheduler\");\n const overlapHours = this.resolveOverlapHours();\n const tickHours = this.opts.tickIntervalHours ?? TICK_INTERVAL_HOURS;\n const tickMs = Math.max(1, Math.round(tickHours * MS_PER_HOUR));\n log.info(\n { overlapHours, tickHours, workspaceId: this.opts.workspaceId },\n \"DEK auto-archive scheduler starting\",\n );\n // Run once at startup so any rotating DEKs left over from a previous\n // daemon process get a chance to archive immediately. Then on the\n // scheduled cadence.\n let inFlight = false;\n this.runOnce().catch(() => undefined);\n const timer = setInterval(() => {\n if (inFlight) {\n log.warn({}, \"DEK auto-archive scheduler skipping tick — previous runOnce still in flight\");\n return;\n }\n inFlight = true;\n this.runOnce().finally(() => {\n inFlight = false;\n });\n }, tickMs);\n timer.unref();\n this.timer = timer;\n }\n\n async stop(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n getLogger(\"dek-archive-scheduler\").info(\"DEK auto-archive scheduler stopped\");\n }\n }\n\n /**\n * Run one archive sweep. Exposed for tests + the manual CLI trigger.\n * Returns the count of DEKs transitioned.\n */\n async runOnce(): Promise<number> {\n const log = getLogger(\"dek-archive-scheduler\");\n const overlapHours = this.resolveOverlapHours();\n const overlapMs = overlapHours * MS_PER_HOUR;\n const now = this.opts.now ? this.opts.now() : new Date().toISOString();\n try {\n const archived = this.opts.keyStore.archiveOverlapped(this.opts.workspaceId, overlapMs, now);\n this.lastResult = { archived: archived.length, archivedAt: now };\n if (archived.length > 0) {\n log.info(\n {\n count: archived.length,\n overlapHours,\n workspaceId: this.opts.workspaceId,\n },\n \"DEK auto-archive: rotating DEKs archived\",\n );\n } else {\n log.debug({ overlapHours }, \"DEK auto-archive: no rotating DEKs past overlap\");\n }\n return archived.length;\n } catch (err) {\n log.error(\n { err: err instanceof Error ? err.message : String(err) },\n \"DEK auto-archive sweep failed\",\n );\n return 0;\n }\n }\n\n /** Last sweep result — null until the first tick. */\n getLastResult(): { archived: number; archivedAt: string } | null {\n return this.lastResult;\n }\n\n private resolveOverlapHours(): number {\n if (this.opts.overlapHours !== undefined && this.opts.overlapHours > 0) {\n return this.opts.overlapHours;\n }\n const envValue = process.env.WOTW_DEK_OVERLAP_HOURS;\n if (envValue) {\n const n = Number(envValue);\n if (Number.isFinite(n) && n > 0) return n;\n }\n return DEFAULT_OVERLAP_HOURS;\n }\n}\n","/**\n * Content sanitization: strip credentials and PII patterns from text before LLM ingestion.\n *\n * This is a best-effort redaction layer. Users can extend the patterns list via\n * configuration. The goal is to keep secrets out of logs, prompts, and wiki pages.\n */\n\nexport interface RedactionRule {\n name: string;\n pattern: RegExp;\n replacement: string;\n}\n\n/**\n * Default redaction rules. Ordered by likely-to-match first for efficiency.\n */\nexport const DEFAULT_REDACTIONS: readonly RedactionRule[] = [\n {\n name: \"aws-access-key\",\n pattern: /\\bAKIA[0-9A-Z]{16}\\b/g,\n replacement: \"[REDACTED:AWS_ACCESS_KEY]\",\n },\n {\n name: \"aws-secret-key\",\n pattern: /\\b[A-Za-z0-9/+=]{40}\\b(?=.*(?:secret|aws))/gi,\n replacement: \"[REDACTED:AWS_SECRET_KEY]\",\n },\n {\n name: \"github-token\",\n // Review item 2: also catch GitHub fine-grained personal access\n // tokens (`github_pat_*`, 82+ chars per docs).\n pattern: /\\bgh[pousr]_[A-Za-z0-9]{36,255}\\b|\\bgithub_pat_[A-Za-z0-9_]{50,}\\b/g,\n replacement: \"[REDACTED:GITHUB_TOKEN]\",\n },\n {\n name: \"anthropic-api-key\",\n // Anthropic keys span ~95-115 chars after `sk-ant-`. The 80,120\n // window stays generous enough to catch both legacy and current\n // formats including api03- prefix.\n pattern: /\\bsk-ant-[A-Za-z0-9-_]{80,120}\\b/g,\n replacement: \"[REDACTED:ANTHROPIC_API_KEY]\",\n },\n {\n name: \"openai-api-key\",\n // Review item 2: original `\\bsk-[A-Za-z0-9]{20,}\\b` missed modern\n // formats with `-` and `_` in the body — `sk-proj-*`,\n // `sk-svcacct-*`, `sk-admin-*` all use `-` separators after the\n // prefix and longer character set. Updated character class.\n pattern: /\\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,200}\\b|\\bsk-[A-Za-z0-9]{20,200}\\b/g,\n replacement: \"[REDACTED:OPENAI_API_KEY]\",\n },\n {\n name: \"gemini-api-key\",\n // Review item 2: Google AI Studio API keys are `AIza` + 35 chars.\n // No rule existed pre-fix.\n pattern: /\\bAIza[A-Za-z0-9_-]{35}\\b/g,\n replacement: \"[REDACTED:GEMINI_API_KEY]\",\n },\n {\n name: \"wotw-daemon-token\",\n // Review item 2: daemon tokens emitted by `wotw user add` are\n // `wotw_` + base64url chars. Pre-fix these went unredacted.\n pattern: /\\bwotw_[A-Za-z0-9_-]{30,200}\\b/g,\n replacement: \"[REDACTED:WOTW_TOKEN]\",\n },\n {\n name: \"private-key-block\",\n pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,\n replacement: \"[REDACTED:PRIVATE_KEY_BLOCK]\",\n },\n {\n name: \"jwt\",\n pattern: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n replacement: \"[REDACTED:JWT]\",\n },\n {\n // L-SEC-3: This pattern is deliberately scoped to full `scheme://`\n // URIs with a `user:password@` userinfo component. The `\\w+:\\/\\/`\n // prefix is load-bearing — without it the pattern would also match\n // bare `user@host` email addresses and `mailto:user@host` URIs,\n // both of which carry no password and must not be over-redacted.\n // Verified by unit tests in test/unit/sanitize.test.ts.\n name: \"password-in-url\",\n pattern: /(\\w+:\\/\\/[^:/\\s]+:)[^@\\s]+(@)/g,\n replacement: \"$1[REDACTED]$2\",\n },\n {\n name: \"credit-card\",\n pattern: /\\b(?:\\d[ -]*?){13,16}\\b/g,\n replacement: \"[REDACTED:PAN]\",\n },\n {\n name: \"us-ssn\",\n pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n replacement: \"[REDACTED:SSN]\",\n },\n];\n\n/**\n * Redact sensitive content from a text blob using the supplied rules (or defaults).\n */\nexport function sanitize(\n input: string,\n rules: readonly RedactionRule[] = DEFAULT_REDACTIONS,\n): string {\n let out = input;\n for (const rule of rules) {\n out = out.replace(rule.pattern, rule.replacement);\n }\n return out;\n}\n\n/**\n * Redact and return the list of rule names that triggered.\n */\nexport function sanitizeWithReport(\n input: string,\n rules: readonly RedactionRule[] = DEFAULT_REDACTIONS,\n): { output: string; triggered: string[] } {\n const triggered: string[] = [];\n let out = input;\n for (const rule of rules) {\n if (rule.pattern.test(out)) {\n triggered.push(rule.name);\n // Reset lastIndex for global regexes before replacing\n rule.pattern.lastIndex = 0;\n out = out.replace(rule.pattern, rule.replacement);\n }\n }\n return { output: out, triggered };\n}\n","/**\n * Build prompts handed to the ingestion LLM. The ingestion pipeline uses the\n * Claude Agent SDK to let the model do its own reading/writing of wiki files,\n * but the *initial* user turn still needs a carefully crafted brief that\n * tells the model:\n *\n * 1. What files were just dropped and where they live\n * 2. The wiki layout and CLAUDE.md conventions\n * 3. What's expected on success (one or more markdown files under\n * `wiki/<category>/`), and what to avoid\n *\n * The builder returns a structured object so the llm-invoker can attach it\n * to a session and the cost-tracker can hash the prompt for provenance.\n */\nimport { readFileSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readTextOrNullAsync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport { sanitize } from \"../utils/sanitize.js\";\nimport type { WotwConfig } from \"../utils/types.js\";\nimport { parsePage } from \"../wiki/page.js\";\n\nexport interface IngestionPrompt {\n /** The full user-turn body. */\n text: string;\n /** Sanitized excerpts of each source file in the order they were read. */\n excerpts: { path: string; excerpt: string; bytes: number; truncated: boolean }[];\n /** The system prompt (currently our CLAUDE.md instructions). */\n system: string;\n}\n\nconst MAX_EXCERPT_BYTES = 32 * 1024; // 32KB per file in the prompt\nconst CLAUDE_MD_MAX_BYTES = 64 * 1024;\n\nexport interface ExistingPageManifestEntry {\n path: string;\n title: string;\n category: string;\n tags?: string[];\n status?: string | null;\n}\n\nexport interface BuildIngestionPromptOptions {\n config: WotwConfig;\n files: string[];\n claudeMdOverride?: string;\n /** Optional override for testing — otherwise read from disk. */\n readFile?: (path: string) => string;\n /**\n * Review item 17: slim manifest of existing wiki pages so the model\n * can dedupe / merge / supersede / match conventions. Caller loads\n * via WikiStore.listAll() and projects to the shape above. When the\n * manifest exceeds {@link EXISTING_PAGES_FULL_LIST_LIMIT}, the top\n * {@link EXISTING_PAGES_PROMPT_CAP} by token-overlap with incoming\n * sources are kept (X1-C1 scope-bound).\n */\n existingPages?: ExistingPageManifestEntry[];\n}\n\n/** Cap for the manifest section in the prompt (per X1-C1). */\nexport const EXISTING_PAGES_PROMPT_CAP = 50;\n/** Wiki size above which we pick the most-relevant pages instead of full list. */\nexport const EXISTING_PAGES_FULL_LIST_LIMIT = 200;\n\n/**\n * Build the ingestion prompt for a batch of files.\n */\nexport async function buildIngestionPrompt(\n opts: BuildIngestionPromptOptions,\n): Promise<IngestionPrompt> {\n const read = opts.readFile ?? ((p: string) => readFileSync(p, \"utf8\"));\n\n const excerpts: IngestionPrompt[\"excerpts\"] = [];\n for (const file of opts.files) {\n try {\n const raw = read(file);\n // Review item 18: byte-correct truncation. Pre-fix used string\n // length (UTF-16 code units), which mis-measured multilingual\n // content (CJK = 1 code unit but 3-4 bytes; emoji = surrogate pair).\n // Slice on a UTF-8 byte boundary by buffering first.\n const rawBytes = Buffer.byteLength(raw, \"utf8\");\n const truncated = rawBytes > MAX_EXCERPT_BYTES;\n let body: string;\n if (truncated) {\n const buf = Buffer.from(raw, \"utf8\").subarray(0, MAX_EXCERPT_BYTES);\n body = `${buf.toString(\"utf8\")}\\n\\n...[truncated]`;\n // Review item 18: emit a structured log when truncation fires so\n // the operator can see context loss instead of silently losing it.\n getLogger(\"prompt-builder\").warn(\n { path: file, rawBytes, capBytes: MAX_EXCERPT_BYTES },\n \"source file truncated for prompt — model sees only the first MAX_EXCERPT_BYTES\",\n );\n } else {\n body = raw;\n }\n excerpts.push({\n path: file,\n excerpt: sanitize(body),\n bytes: rawBytes,\n truncated,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n getLogger(\"prompt-builder\").warn({ path: file, err: msg }, \"skipping unreadable source file\");\n continue;\n }\n }\n\n // Gather rejection feedback from previously rejected candidates.\n const rejections = loadRejectionFeedback(opts.config);\n\n // Review item 17: select existing-page manifest to surface to the model.\n const manifest = selectRelevantPages(opts.existingPages ?? [], excerpts);\n\n const system = opts.claudeMdOverride ?? (await loadClaudeMd(opts.config));\n const text = renderUserTurn(opts.config, excerpts, rejections, manifest);\n\n return { text, excerpts, system };\n}\n\n/**\n * Pick the slice of the wiki's existing pages to surface to the model.\n * If the wiki is small enough, dump the whole list. Otherwise rank by\n * token-overlap with incoming source excerpts (cheap word-set jaccard)\n * and keep the top {@link EXISTING_PAGES_PROMPT_CAP}.\n */\nfunction selectRelevantPages(\n manifest: ExistingPageManifestEntry[],\n excerpts: IngestionPrompt[\"excerpts\"],\n): ExistingPageManifestEntry[] {\n if (manifest.length === 0) return [];\n if (manifest.length <= EXISTING_PAGES_FULL_LIST_LIMIT) return manifest;\n\n const sourceTokens = new Set<string>();\n for (const e of excerpts) {\n for (const tok of tokenize(e.excerpt)) sourceTokens.add(tok);\n }\n const scored = manifest.map((p) => {\n const pageTokens = new Set([\n ...tokenize(p.title),\n ...(p.tags ?? []).flatMap((t) => tokenize(t)),\n ]);\n let overlap = 0;\n for (const t of pageTokens) {\n if (sourceTokens.has(t)) overlap += 1;\n }\n return { p, score: overlap };\n });\n scored.sort((a, b) => b.score - a.score);\n return scored.slice(0, EXISTING_PAGES_PROMPT_CAP).map((s) => s.p);\n}\n\nfunction tokenize(s: string): string[] {\n return s\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((t) => t.length >= 3);\n}\n\nasync function loadClaudeMd(cfg: WotwConfig): Promise<string> {\n const path = join(cfg.wiki_root, \"CLAUDE.md\");\n const contents = await readTextOrNullAsync(path);\n if (!contents) return DEFAULT_SYSTEM;\n // Review item 19: byte-correct truncation + structured log when the\n // operator's main lever against context-loss is silently capped.\n const rawBytes = Buffer.byteLength(contents, \"utf8\");\n if (rawBytes <= CLAUDE_MD_MAX_BYTES) return contents;\n const buf = Buffer.from(contents, \"utf8\").subarray(0, CLAUDE_MD_MAX_BYTES);\n getLogger(\"prompt-builder\").warn(\n { path, rawBytes, capBytes: CLAUDE_MD_MAX_BYTES },\n \"CLAUDE.md truncated for prompt — model sees only the first CLAUDE_MD_MAX_BYTES\",\n );\n return buf.toString(\"utf8\");\n}\n\nconst DEFAULT_SYSTEM = `You are the watcher-on-the-wall ingestion agent.\nYour job is to read source files dropped into the raw/ directory and\nproduce interlinked wiki pages under wiki/<category>/.\n\nCategories: concept, entity, source, comparison, synthesis, query.\n\nEach wiki page MUST begin with YAML frontmatter:\n title: string\n category: concept|entity|source|comparison|synthesis|query\n created: YYYY-MM-DD\n updated: YYYY-MM-DD\n sources: [list of raw/ paths]\n related: [list of wiki/ slugs]\n tags: [strings]\n confidence: high|medium|low\n\nFor every wiki page you write, also include these optional frontmatter fields:\n - domain: a broad knowledge domain category (e.g., ops, security, architecture, research, finance, engineering)\n - scope: the project or organizational context this knowledge belongs to (e.g., the project name, team name, or \"general\")\n - key_terms: an array of 5-15 keywords and phrases that this page should be findable by, including synonyms and alternative phrasings not used in the body text\n\nRules:\n - Always produce at least one source-category page per raw file.\n - Always link related concepts bidirectionally.\n - Never modify files in raw/.\n - Never emit placeholder TODOs — write what you know.`;\n\ninterface RejectionFeedback {\n title: string;\n reason: string;\n}\n\n/**\n * Scan the rejected candidates directory for rejection reasons.\n * These feed back into the LLM prompt so the model learns from past rejections.\n */\nfunction loadRejectionFeedback(config: WotwConfig): RejectionFeedback[] {\n const rejectedDir = join(config.wiki_root, \"candidates\", \"rejected\");\n let files: string[];\n try {\n files = readdirSync(rejectedDir).filter((f) => f.endsWith(\".md\"));\n } catch {\n return [];\n }\n const feedback: RejectionFeedback[] = [];\n for (const file of files) {\n try {\n const raw = readFileSync(join(rejectedDir, file), \"utf8\");\n const page = parsePage(join(rejectedDir, file), raw);\n if (page.frontmatter.rejection_note) {\n feedback.push({\n title: page.frontmatter.title,\n reason: page.frontmatter.rejection_note,\n });\n }\n } catch {\n // Skip unreadable rejected pages.\n }\n }\n return feedback;\n}\n\nfunction renderUserTurn(\n cfg: WotwConfig,\n excerpts: IngestionPrompt[\"excerpts\"],\n rejections: RejectionFeedback[] = [],\n existingPages: ExistingPageManifestEntry[] = [],\n): string {\n const lines: string[] = [];\n lines.push(\"# Ingestion batch\");\n lines.push(\"\");\n lines.push(`Wiki root: ${cfg.wiki_root}`);\n lines.push(`Raw path: ${cfg.raw_path}`);\n lines.push(\"\");\n lines.push(`You have ${excerpts.length} source file(s) to ingest. For each file,`);\n lines.push(\"produce or update a `source` page under `wiki/sources/`, and create or\");\n lines.push(\"link any `concept` / `entity` pages the material references.\");\n lines.push(\"\");\n lines.push(\"## Source files\");\n for (const e of excerpts) {\n lines.push(\"\");\n lines.push(`### ${e.path}`);\n lines.push(`- bytes: ${e.bytes}${e.truncated ? \" (truncated)\" : \"\"}`);\n lines.push(\"\");\n lines.push(\"```\");\n lines.push(e.excerpt);\n lines.push(\"```\");\n }\n lines.push(\"\");\n // Review item 17: surface existing wiki pages so the model can dedupe,\n // merge, supersede, and match category conventions.\n if (existingPages.length > 0) {\n lines.push(\"## Existing wiki pages\");\n lines.push(\"\");\n lines.push(\n `The wiki already contains the following pages (${existingPages.length} shown). ` +\n \"Prefer updating an existing page over creating a new one with overlapping \" +\n \"content; cross-link via `related:` rather than duplicating; use \" +\n \"`status: superseded_by:` to chain successors.\",\n );\n lines.push(\"\");\n for (const p of existingPages) {\n const tagSuffix =\n p.tags && p.tags.length > 0 ? ` — tags: ${p.tags.slice(0, 6).join(\", \")}` : \"\";\n const statusSuffix =\n p.status && p.status !== \"active\" && p.status !== null ? ` [${p.status}]` : \"\";\n lines.push(`- \\`${p.path}\\` (${p.category}) — ${p.title}${tagSuffix}${statusSuffix}`);\n }\n lines.push(\"\");\n }\n\n // Rejection feedback from previously rejected candidates.\n if (rejections.length > 0) {\n lines.push(\"## Previous rejections\");\n lines.push(\"\");\n lines.push(\"The following pages were rejected by the user. Learn from their feedback:\");\n lines.push(\"\");\n for (const r of rejections) {\n lines.push(`- **${r.title}**: ${r.reason}`);\n }\n lines.push(\"\");\n }\n\n lines.push(\"## Expected output\");\n lines.push(\"\");\n lines.push(\"Return ONLY valid JSON with this shape, no other text:\");\n lines.push(\"\");\n lines.push(\"```json\");\n lines.push(\"{\");\n lines.push(' \"edits\": [');\n lines.push(\" {\");\n lines.push(' \"path\": \"wiki/<category>/<slug>.md\",');\n lines.push(' \"content\": \"---\\\\nfull YAML frontmatter and markdown body\\\\n---\\\\n...\"');\n lines.push(\" }\");\n lines.push(\" ]\");\n lines.push(\"}\");\n lines.push(\"```\");\n lines.push(\"\");\n lines.push(\"Rules:\");\n lines.push(\"- Emit one edit per wiki page you want to create or update.\");\n lines.push(\"- The `content` field is the COMPLETE file content including frontmatter.\");\n lines.push(\n \"- Every new page must include full YAML frontmatter (title, category, created, updated, sources, related, tags, confidence).\",\n );\n lines.push(\"- Use `[[wiki-links]]` when referring to other pages.\");\n lines.push(\n \"- Cite source files in the `sources:` frontmatter list (relative paths under `raw/`).\",\n );\n lines.push(\"- Always produce at least one source-category page per raw file.\");\n lines.push(\"- Do NOT include any text outside the JSON object.\");\n return lines.join(\"\\n\");\n}\n","/**\n * Multi-tenant job scheduler with round-robin fairness, per-tenant\n * concurrency caps, and a kill switch. Only active when `hosted.enabled`\n * is true — the single-user p-queue path is unchanged.\n */\nimport { getLogger } from \"../utils/logger.js\";\n\nexport interface TenantJob {\n tenantId: string;\n batchId: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- callers return heterogeneous result types\n execute: () => Promise<any>;\n}\n\nexport class TenantScheduler {\n private readonly globalConcurrency: number;\n private readonly getConcurrencyCap: (tenantId: string) => number;\n private readonly isPaused: (tenantId: string) => boolean;\n\n /** Per-tenant FIFO job queues. */\n private readonly subqueues = new Map<string, TenantJob[]>();\n /** Ordered list of tenant IDs for round-robin iteration. */\n private tenantOrder: string[] = [];\n /** Index into tenantOrder for round-robin fairness. */\n private rrIndex = 0;\n\n /** Number of jobs currently executing per tenant. */\n private readonly activeByTenant = new Map<string, number>();\n /** Total jobs currently executing across all tenants. */\n private globalActive = 0;\n\n /** Set to true when stop() is called — no new jobs accepted. */\n private stopped = false;\n /** Resolvers waiting for drain(). */\n private drainResolvers: Array<() => void> = [];\n /** Currently scheduled processing tick (prevents duplicate scheduling). */\n private tickScheduled = false;\n\n constructor(opts: {\n globalConcurrency: number;\n getConcurrencyCap: (tenantId: string) => number;\n isPaused: (tenantId: string) => boolean;\n }) {\n this.globalConcurrency = opts.globalConcurrency;\n this.getConcurrencyCap = opts.getConcurrencyCap;\n this.isPaused = opts.isPaused;\n }\n\n /**\n * Enqueue a job. It will be scheduled when capacity is available and the\n * tenant is not paused.\n */\n enqueue(job: TenantJob): void {\n if (this.stopped) {\n const log = getLogger(\"tenant-scheduler\");\n log.warn(\n { tenantId: job.tenantId, batchId: job.batchId },\n \"scheduler stopped — rejecting job\",\n );\n return;\n }\n if (!this.subqueues.has(job.tenantId)) {\n this.subqueues.set(job.tenantId, []);\n this.tenantOrder.push(job.tenantId);\n }\n this.subqueues.get(job.tenantId)!.push(job);\n this.scheduleTick();\n }\n\n /** Number of pending (not yet executing) jobs for a tenant. */\n getQueueDepth(tenantId: string): number {\n return this.subqueues.get(tenantId)?.length ?? 0;\n }\n\n /** Total pending jobs across all tenants. */\n getTotalQueueDepth(): number {\n let total = 0;\n for (const q of this.subqueues.values()) total += q.length;\n return total;\n }\n\n /** Number of currently executing jobs for a tenant. */\n getActiveJobs(tenantId: string): number {\n return this.activeByTenant.get(tenantId) ?? 0;\n }\n\n /** Total active jobs across all tenants. */\n getTotalActiveJobs(): number {\n return this.globalActive;\n }\n\n /** Wait for all in-flight jobs to complete. */\n async drain(): Promise<void> {\n if (this.globalActive === 0 && this.getTotalQueueDepth() === 0) return;\n return new Promise<void>((resolve) => {\n this.drainResolvers.push(resolve);\n });\n }\n\n /** Stop accepting new jobs. In-flight jobs are allowed to complete. */\n stop(): void {\n this.stopped = true;\n }\n\n /**\n * Toggle the paused state for a tenant at runtime. Paused tenants' jobs\n * stay in the queue (not dropped). Unpausing triggers a processing tick.\n */\n setPaused(tenantId: string, paused: boolean): void {\n // The paused state is delegated to the callback — this method exists\n // so the caller can trigger re-evaluation after changing the state.\n // We intentionally don't store paused state here; the isPaused callback\n // is the single source of truth. Log the intent and reschedule.\n const log = getLogger(\"tenant-scheduler\");\n log.info({ tenantId, paused }, paused ? \"tenant paused\" : \"tenant unpaused\");\n if (!paused) {\n this.scheduleTick();\n }\n }\n\n /** Per-tenant timestamps of last completed job. */\n private readonly lastCompletedAt = new Map<string, Date>();\n /** Per-tenant rolling latency accumulators. */\n private readonly latencySum = new Map<string, number>();\n private readonly latencyCount = new Map<string, number>();\n\n /**\n * Returns a snapshot of queue state across all tenants.\n */\n getStatus(): {\n tenants: Array<{\n tenantId: string;\n queueDepth: number;\n activeJobs: number;\n paused: boolean;\n lastCompletedAt: Date | null;\n avgLatencyMs: number | null;\n }>;\n globalActiveJobs: number;\n globalQueueDepth: number;\n } {\n const tenants = this.tenantOrder.map((tenantId) => {\n const sum = this.latencySum.get(tenantId) ?? 0;\n const count = this.latencyCount.get(tenantId) ?? 0;\n return {\n tenantId,\n queueDepth: this.getQueueDepth(tenantId),\n activeJobs: this.getActiveJobs(tenantId),\n paused: this.isPaused(tenantId),\n lastCompletedAt: this.lastCompletedAt.get(tenantId) ?? null,\n avgLatencyMs: count > 0 ? Math.round(sum / count) : null,\n };\n });\n return {\n tenants,\n globalActiveJobs: this.globalActive,\n globalQueueDepth: this.getTotalQueueDepth(),\n };\n }\n\n // ---------------------------------------------------------------------------\n // Internal scheduling\n // ---------------------------------------------------------------------------\n\n private scheduleTick(): void {\n if (this.tickScheduled) return;\n this.tickScheduled = true;\n // Use queueMicrotask for immediate scheduling without setTimeout(0) delay\n queueMicrotask(() => {\n this.tickScheduled = false;\n this.processTick();\n });\n }\n\n /**\n * Round-robin processing loop. Iterates through tenants starting from\n * rrIndex so no tenant is systematically starved.\n */\n private processTick(): void {\n if (this.tenantOrder.length === 0) return;\n\n let launched = false;\n const n = this.tenantOrder.length;\n\n for (let i = 0; i < n; i++) {\n if (this.globalActive >= this.globalConcurrency) break;\n\n const idx = (this.rrIndex + i) % n;\n const tenantId = this.tenantOrder[idx]!;\n\n if (this.isPaused(tenantId)) continue;\n\n const queue = this.subqueues.get(tenantId);\n if (!queue || queue.length === 0) continue;\n\n const tenantActive = this.activeByTenant.get(tenantId) ?? 0;\n const cap = this.getConcurrencyCap(tenantId);\n if (tenantActive >= cap) continue;\n\n // Dequeue and execute\n const job = queue.shift()!;\n this.activeByTenant.set(tenantId, tenantActive + 1);\n this.globalActive++;\n launched = true;\n\n this.executeJob(job);\n }\n\n // Advance round-robin pointer so the next tick starts with a different tenant\n if (launched) {\n this.rrIndex = (this.rrIndex + 1) % n;\n }\n\n // Clean up empty tenant entries\n this.cleanupEmptyTenants();\n\n // Check if drained\n this.checkDrained();\n }\n\n private executeJob(job: TenantJob): void {\n const log = getLogger(\"tenant-scheduler\");\n const startMs = Date.now();\n job\n .execute()\n .catch((err: unknown) => {\n log.error({ err, tenantId: job.tenantId, batchId: job.batchId }, \"tenant job failed\");\n })\n .finally(() => {\n const prev = this.activeByTenant.get(job.tenantId) ?? 1;\n this.activeByTenant.set(job.tenantId, prev - 1);\n this.globalActive--;\n\n // Track completion time and latency\n this.lastCompletedAt.set(job.tenantId, new Date());\n const elapsed = Date.now() - startMs;\n this.latencySum.set(job.tenantId, (this.latencySum.get(job.tenantId) ?? 0) + elapsed);\n this.latencyCount.set(job.tenantId, (this.latencyCount.get(job.tenantId) ?? 0) + 1);\n\n this.scheduleTick();\n });\n }\n\n private cleanupEmptyTenants(): void {\n this.tenantOrder = this.tenantOrder.filter((tid) => {\n const queue = this.subqueues.get(tid);\n const active = this.activeByTenant.get(tid) ?? 0;\n if ((!queue || queue.length === 0) && active === 0) {\n this.subqueues.delete(tid);\n this.activeByTenant.delete(tid);\n return false;\n }\n return true;\n });\n // Keep rrIndex in bounds\n if (this.tenantOrder.length > 0) {\n this.rrIndex = this.rrIndex % this.tenantOrder.length;\n } else {\n this.rrIndex = 0;\n }\n }\n\n private checkDrained(): void {\n if (\n this.globalActive === 0 &&\n this.getTotalQueueDepth() === 0 &&\n this.drainResolvers.length > 0\n ) {\n for (const resolve of this.drainResolvers) resolve();\n this.drainResolvers = [];\n }\n }\n}\n","/**\n * Ingestion queue. p-queue with concurrency:1 ensures batches are processed\n * strictly in order. Each enqueued batch goes through:\n *\n * 1. Budget check (cost-tracker)\n * 2. Prompt construction (prompt-builder)\n * 3. Agent invocation (llm-invoker)\n * 4. Wiki reconciliation (wiki-writer)\n * 5. Bidirectional link repair (cross-reference)\n * 6. Index rebuild + search reindex\n * 7. Cost logging\n * 8. Git commit\n *\n * The queue is a {@link DaemonSubsystem}. start() is a no-op today — we\n * don't open network connections until a batch arrives. stop() drains any\n * in-flight work with a timeout so shutdown is responsive.\n */\nimport { relative, resolve } from \"node:path\";\nimport PQueue from \"p-queue\";\nimport { cliAuthError, looksLikeCliAuthFailure } from \"../utils/actionable-error.js\";\nimport { errMsg } from \"../utils/errors.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport type { DaemonSubsystem } from \"../daemon/index.js\";\nimport type { IndexManager } from \"../wiki/index-manager.js\";\nimport { repairBidirectionalLinks } from \"../wiki/cross-reference.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport type { WatcherBatch } from \"../watcher/index.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport { sha256File, sha256Files, sha256Hex } from \"../provenance/hash.js\";\nimport type { CostTracker } from \"./cost-tracker.js\";\nimport type { DeadLetterQueue } from \"./dead-letter.js\";\nimport { commitWikiChanges } from \"./git-committer.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport { parseDaemonEditsResponse, resolveEditPath } from \"../llm/edits.js\";\nimport { atomicWrite } from \"../utils/fs.js\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { ModelRouter } from \"./model-router.js\";\nimport { buildIngestionPrompt, type ExistingPageManifestEntry } from \"./prompt-builder.js\";\nimport type { FactStore } from \"../facts/store.js\";\nimport type { FactIndex } from \"../facts/index-manager.js\";\nimport { extractFactsFromPage, isExtractionActive } from \"../facts/extractor.js\";\nimport { TenantScheduler } from \"./tenant-scheduler.js\";\nimport { loadAllPages, reconcileWrittenPages } from \"./wiki-writer.js\";\n\nexport interface IngestionQueueOptions {\n config: WotwConfig;\n store: WikiStore;\n indexManager: IndexManager;\n search: WikiSearch;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n /** Optional provenance chain — if provided, each batch appends a record. */\n provenance?: ProvenanceChain | null;\n /**\n * Resolved runtime mode. Defaults to \"api\" so legacy callers and test rigs\n * keep working without change. When set to \"cli\" the queue spawns the\n * `claude` binary for every batch and logs cost=0 (subscription-covered).\n */\n runtimeMode?: RuntimeMode;\n /**\n * Optional dead-letter sink. When provided, catch blocks that previously\n * dropped a failed batch with only a log line now persist the failure to\n * a JSONL ledger so operators can inspect it and replay later.\n */\n deadLetter?: DeadLetterQueue | null;\n /**\n * Pass B fact-extraction sidecar. When both factStore + factIndex are\n * provided AND `isExtractionActive(config, runtimeMode)` returns true,\n * the queue runs fact extraction after each successful page write and\n * appends a `fact_extracted` provenance record. Best-effort: extraction\n * failure does NOT fail the ingestion.\n */\n factStore?: FactStore | null;\n factIndex?: FactIndex | null;\n}\n\nexport interface IngestionOutcome {\n batchId: string;\n skipped: boolean;\n skipReason?: string;\n pagesWritten: number;\n costUsd: number;\n gitSha: string | null;\n durationMs: number;\n}\n\nexport class IngestionQueue implements DaemonSubsystem {\n readonly name = \"ingestion\";\n private readonly opts: IngestionQueueOptions;\n private readonly queue: PQueue;\n /** Tenant-aware scheduler used when `hosted.enabled: true`. */\n private readonly tenantScheduler: TenantScheduler | null;\n /** Last enqueued outcome — exposed for tests. */\n private lastOutcome: IngestionOutcome | null = null;\n\n constructor(opts: IngestionQueueOptions) {\n this.opts = opts;\n this.queue = new PQueue({ concurrency: 1 });\n\n // In hosted mode, construct a TenantScheduler that manages per-tenant\n // subqueues with round-robin fairness and concurrency caps. The p-queue\n // stays as a fallback for single-user mode.\n const hosted = opts.config.hosted;\n if (hosted.enabled && hosted.tenant_id) {\n const pausedState = new Map<string, boolean>();\n if (hosted.paused) pausedState.set(hosted.tenant_id, true);\n this.tenantScheduler = new TenantScheduler({\n globalConcurrency: hosted.concurrency_cap,\n getConcurrencyCap: () => hosted.concurrency_cap,\n isPaused: (tid) => pausedState.get(tid) ?? false,\n });\n // Expose setPaused so the admin API can toggle it at runtime\n (this as { _pausedState?: Map<string, boolean> })._pausedState = pausedState;\n } else {\n this.tenantScheduler = null;\n }\n }\n\n async start(): Promise<void> {\n const log = getLogger(\"ingestion\");\n if (this.tenantScheduler) {\n const hosted = this.opts.config.hosted;\n log.info(\n {\n tenantId: hosted.tenant_id,\n concurrencyCap: hosted.concurrency_cap,\n paused: hosted.paused,\n },\n \"ingestion queue ready (hosted mode)\",\n );\n if (hosted.paused) {\n log.warn(\n { tenantId: hosted.tenant_id },\n \"tenant is paused — ingestion jobs will be held until unpaused\",\n );\n }\n } else {\n log.info(\"ingestion queue ready\");\n }\n // Eagerly rebuild search + index so queries are immediately useful\n // even before the first batch lands.\n await this.opts.store.ensureLayout();\n const pages = await loadAllPages(this.opts.store);\n this.opts.search.rebuild(pages);\n await this.opts.indexManager.rebuild(pages);\n log.info({ pages: pages.length }, \"initial wiki scan complete\");\n }\n\n async stop(): Promise<void> {\n const log = getLogger(\"ingestion\");\n if (this.tenantScheduler) {\n log.info(\"stopping tenant scheduler\");\n this.tenantScheduler.stop();\n const drainTimeout = 5_000;\n await Promise.race([\n this.tenantScheduler.drain(),\n new Promise<void>((r) => setTimeout(r, drainTimeout)),\n ]);\n } else {\n log.info(\n { pending: this.queue.size, active: this.queue.pending },\n \"draining ingestion queue\",\n );\n this.queue.pause();\n const drainTimeout = 5_000;\n await Promise.race([\n this.queue.onIdle(),\n new Promise<void>((r) => setTimeout(r, drainTimeout)),\n ]);\n this.queue.clear();\n }\n }\n\n /** Enqueue a batch. Returns a promise that resolves with the outcome. */\n enqueue(batch: WatcherBatch): Promise<IngestionOutcome> {\n const processWithCatch = async (): Promise<IngestionOutcome> => {\n try {\n return await this.process(batch);\n } catch (err) {\n const log = getLogger(\"ingestion\");\n log.error({ err, batchId: batch.id }, \"batch processing threw\");\n if (this.opts.deadLetter) {\n await this.opts.deadLetter.record(batch, err, \"add\");\n }\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: true,\n skipReason: `process error: ${errMsg(err)}`,\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: 0,\n };\n this.lastOutcome = outcome;\n return outcome;\n }\n };\n\n // In hosted mode, route through the tenant scheduler for fair scheduling.\n // In single-user mode, use the existing p-queue.\n if (this.tenantScheduler && this.opts.config.hosted.tenant_id) {\n return new Promise<IngestionOutcome>((resolve) => {\n this.tenantScheduler!.enqueue({\n tenantId: this.opts.config.hosted.tenant_id!,\n batchId: batch.id,\n execute: async () => {\n const result = await processWithCatch();\n resolve(result);\n return result;\n },\n });\n });\n }\n\n return this.queue.add(processWithCatch) as Promise<IngestionOutcome>;\n }\n\n /** Number of pending batches waiting on the queue. */\n pendingCount(): number {\n if (this.tenantScheduler) return this.tenantScheduler.getTotalQueueDepth();\n return this.queue.size;\n }\n\n /** Expose the last outcome (for debugging / tests). */\n getLastOutcome(): IngestionOutcome | null {\n return this.lastOutcome;\n }\n\n private async process(batch: WatcherBatch): Promise<IngestionOutcome> {\n const log = getLogger(\"ingestion\");\n const started = Date.now();\n const runtimeMode: RuntimeMode = this.opts.runtimeMode ?? \"api\";\n // In CLI mode, the daemon uses a single Sonnet model for every operation\n // (the subscription covers it). In API mode, the model-router picks\n // Haiku for ingestion to keep costs down.\n const model =\n runtimeMode === \"cli\"\n ? this.opts.config.execution.cli_model\n : this.opts.modelRouter.modelFor(\"ingest\");\n\n log.info(\n {\n batchId: batch.id,\n fileCount: batch.paths.length,\n deletes: batch.deletedPaths.length,\n model,\n runtimeMode,\n },\n \"processing batch\",\n );\n\n // Deletion-only batch: skip the entire LLM pipeline and archive the\n // affected wiki pages directly. The archive path costs nothing to run\n // and records an \"archive\" provenance entry per batch.\n if (batch.paths.length === 0 && batch.deletedPaths.length > 0) {\n const archived = await this.archiveDeletedSources(batch.deletedPaths, batch);\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: false,\n pagesWritten: archived,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n log.info({ ...outcome, archived }, \"delete batch complete\");\n this.lastOutcome = outcome;\n return outcome;\n }\n\n // 1. Build prompt\n // Review item 17: surface the existing-wiki manifest so the model\n // can dedupe, merge, supersede, and match conventions. We project\n // existing pages down to a slim shape (path + title + category +\n // tags + status) — the prompt-builder ranks + caps when the wiki\n // grows past 200 pages.\n const existingPages: ExistingPageManifestEntry[] = (await loadAllPages(this.opts.store)).map(\n (p) => ({\n path: relative(this.opts.config.wiki_root, p.path) || p.path,\n title: p.frontmatter.title,\n category: p.frontmatter.category,\n tags: p.frontmatter.tags,\n status: p.frontmatter.status ?? null,\n }),\n );\n const prompt = await buildIngestionPrompt({\n config: this.opts.config,\n files: batch.paths,\n existingPages,\n });\n\n // 1a. Hash source files NOW (M-PIPE-1). Computing source_hashes at\n // provenance-append time opens a race window: a concurrent manual edit\n // to a raw/ file between now and the chain append would drift the\n // recorded hash from the state the agent actually consumed. We hash\n // as soon as we know the batch paths and hold the result through to\n // provenance append.\n const sourceFiles = [...batch.paths];\n const sourceHashes: string[] = [];\n for (const p of sourceFiles) {\n const h = await sha256File(p);\n sourceHashes.push(h ?? \"missing\");\n }\n\n // 2. Pre-flight budget check. CLI mode is subscription-covered — skip\n // the budget check entirely. API mode estimates 12k input + 4k output\n // worst-case until we know the real cost.\n // Review item 22: use checkOperationBudget() so the per-ingest USD\n // cap fires too (not just daily-exceeds). Pre-fix only daily was\n // checked; checkOperationBudget had zero callers anywhere in the\n // codebase. Now both per-ingest and daily caps are enforced here.\n const preflight =\n runtimeMode === \"cli\" ? 0 : this.opts.modelRouter.computeCost(model, 12_000, 4_000);\n if (runtimeMode !== \"cli\") {\n const budgetErr = this.opts.costTracker.checkOperationBudget(\"ingest\", preflight);\n if (budgetErr) {\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: true,\n skipReason: budgetErr,\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n log.warn({ batchId: batch.id, budgetErr }, \"skipping batch — budget cap\");\n this.lastOutcome = outcome;\n return outcome;\n }\n }\n\n // 3. Single-pass LLM call. Phase 6: the daemon dispatches via\n // runtimeAwareComplete, parses the model's JSON edits response, and\n // performs the file writes itself. The legacy multi-turn agent loop\n // (Read/Write/Edit tools, maxTurns 50) is gone — the prompt-builder\n // instructs the model to emit { edits: [{ path, content }] } and the\n // daemon validates + writes each edit.\n //\n // Behavioral comparison vs the SDK baseline on 5+ representative\n // fixtures is the Phase 6 hard gate (deferred until live model fixture\n // run — see MULTI-LLM-PHASE-006.md).\n let invokeResult: {\n finalText: string;\n totalCostUsd: number;\n inputTokens: number;\n outputTokens: number;\n durationMs: number;\n writtenPaths: string[];\n sessionId: string | null;\n };\n try {\n // maxTokens kept under the Anthropic SDK's 10-min request threshold\n // (above which the SDK requires streaming mode). 8192 tokens ≈ 5-10\n // typical wiki pages per batch. Future phase can add streaming for\n // large multi-file outputs.\n const completeResult = await runtimeAwareComplete(prompt.text, {\n systemPrompt: prompt.system,\n model,\n maxTokens: 8192,\n config: this.opts.config,\n runtimeMode,\n });\n\n // Parse the model's JSON edits response. Defensive: parse failure\n // results in zero writtenPaths and the downstream \"agent produced\n // zero pages\" guard fires.\n const parsed = parseDaemonEditsResponse(completeResult.text);\n const writtenPaths: string[] = [];\n const rawPath = resolve(this.opts.config.raw_path);\n if (parsed) {\n for (const edit of parsed.edits) {\n const absPath = resolveEditPath(this.opts.config.wiki_root, edit.path);\n if (!absPath) {\n log.warn(\n { path: edit.path, batchId: batch.id },\n \"ingestion edit rejected — path resolves outside wiki_root\",\n );\n continue;\n }\n // Forbid writes to the raw/ directory; model must not modify\n // source files.\n if (absPath === rawPath || absPath.startsWith(`${rawPath}/`)) {\n log.warn(\n { path: edit.path, batchId: batch.id },\n \"ingestion edit rejected — model attempted to write inside raw/\",\n );\n continue;\n }\n try {\n await mkdir(dirname(absPath), { recursive: true });\n await atomicWrite(absPath, edit.content);\n writtenPaths.push(absPath);\n } catch (writeErr) {\n log.warn(\n { err: writeErr, path: edit.path, batchId: batch.id },\n \"ingestion edit failed to write — skipping\",\n );\n }\n }\n } else if (completeResult.text.trim().length > 0) {\n // PASS-023 dogfood finding #21: a Claude Code CLI auth failure (401)\n // surfaces here as the agent's stdout rather than an HTTP status.\n // Detect it and emit a loud, actionable message instead of the\n // generic \"not valid JSON edits\" → silent skip.\n if (looksLikeCliAuthFailure(completeResult.text)) {\n const authErr = cliAuthError();\n log.error({ batchId: batch.id, code: authErr.code }, authErr.message);\n if (this.opts.deadLetter) {\n await this.opts.deadLetter.record(batch, authErr, \"add\");\n }\n } else {\n log.warn(\n {\n batchId: batch.id,\n textLen: completeResult.text.length,\n sample: completeResult.text.slice(0, 200),\n tailSample: completeResult.text.slice(-200),\n },\n \"ingestion response was not valid JSON edits\",\n );\n }\n }\n\n invokeResult = {\n finalText: completeResult.text,\n totalCostUsd: completeResult.costUsd,\n inputTokens: completeResult.inputTokens,\n outputTokens: completeResult.outputTokens,\n durationMs: completeResult.durationMs,\n writtenPaths,\n sessionId: null,\n };\n } catch (err) {\n log.error({ err, batchId: batch.id }, \"ingestion agent invocation failed\");\n if (this.opts.deadLetter) {\n await this.opts.deadLetter.record(batch, err, \"add\");\n }\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: true,\n skipReason: `agent error: ${errMsg(err)}`,\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n this.lastOutcome = outcome;\n return outcome;\n }\n\n // 4. Reconcile written pages\n const { pages: newPages, skipped: skippedWrites } = await reconcileWrittenPages(\n this.opts.store,\n invokeResult.writtenPaths,\n { staging: this.opts.config.ingestion.staging },\n );\n if (skippedWrites.length > 0) {\n log.warn({ skipped: skippedWrites, batchId: batch.id }, \"some written paths skipped\");\n }\n\n // Guard: if the agent produced zero pages, skip all downstream work.\n if (newPages.length === 0 && skippedWrites.length === 0) {\n log.warn({ batchId: batch.id }, \"agent produced zero pages — marking batch as skipped\");\n // Review item 24: empty-pages skip used to bypass costTracker.logUsage\n // entirely. The LLM was invoked, real dollars were spent, but the\n // daily cap never saw it — so subsequent batches could blow past\n // the daily budget without warning. Log the actual cost here too.\n if (runtimeMode !== \"cli\" && invokeResult.totalCostUsd > 0) {\n this.opts.costTracker.logUsage({\n operation: \"ingest\",\n model,\n costUsd: invokeResult.totalCostUsd,\n inputTokens: invokeResult.inputTokens,\n outputTokens: invokeResult.outputTokens,\n batchId: batch.id,\n });\n }\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: true,\n skipReason: \"agent produced no wiki pages\",\n pagesWritten: 0,\n costUsd: invokeResult.totalCostUsd,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n this.lastOutcome = outcome;\n return outcome;\n }\n\n // 5. Repair bidirectional links across the whole wiki (cheap: in-memory)\n const allPages = await loadAllPages(this.opts.store);\n const mutated = repairBidirectionalLinks(this.opts.store, allPages);\n for (const p of mutated) {\n await this.opts.store.writePage(p);\n }\n\n // 6. Rebuild index + search\n const finalPages = mutated.length > 0 ? await loadAllPages(this.opts.store) : allPages;\n await this.opts.indexManager.rebuild(finalPages);\n this.opts.search.rebuild(finalPages);\n\n // 6a. Hash every written wiki file NOW (M-PIPE-1). Computing these\n // lazily at provenance-append time opens a race window with concurrent\n // compounding passes or manual edits between the last write and the\n // append. Capture the committed state here, while no other writer can\n // touch these paths, and thread the precomputed hashes through to the\n // provenance record.\n const writtenAbsPaths = [\n ...newPages.map((p) => p.path),\n ...mutated.map((p) => p.path),\n `${this.opts.store.wikiDir}/index.md`,\n ];\n const uniqueWrittenAbs = [...new Set(writtenAbsPaths)];\n const wikiFileHashesByAbs = await sha256Files(uniqueWrittenAbs);\n const wikiRoot = this.opts.config.wiki_root;\n const wikiFileHashes: Record<string, string> = {};\n for (const abs of uniqueWrittenAbs) {\n const h = wikiFileHashesByAbs[abs];\n if (h) wikiFileHashes[relative(wikiRoot, abs) || abs] = h;\n }\n\n // 7. Log cost\n this.opts.costTracker.logUsage({\n operation: \"ingest\",\n model,\n costUsd: invokeResult.totalCostUsd,\n inputTokens: invokeResult.inputTokens,\n outputTokens: invokeResult.outputTokens,\n batchId: batch.id,\n });\n\n // 8. Append provenance record (before commit so the chain file is\n // included in the same git commit as the wiki pages it describes).\n // Source and wiki-file hashes were captured eagerly at steps 1a / 6a\n // to close the M-PIPE-1 race window.\n if (this.opts.provenance) {\n try {\n await this.recordProvenance({\n batch,\n model,\n promptText: `${prompt.system}\\n\\n---\\n\\n${prompt.text}`,\n // Review item 20 (X1-C2): hash the system-prompt template\n // separately so audit-replay is possible without the rendered\n // text. sourceHashes already captures source bytes; this\n // closes the third leg (system prompt fingerprint that's\n // independent of sanitize-rule drift).\n systemPromptHash: sha256Hex(prompt.system),\n responseText: invokeResult.finalText,\n sourceFiles,\n sourceHashes,\n wikiFileHashes,\n costUsd: invokeResult.totalCostUsd,\n inputTokens: invokeResult.inputTokens,\n outputTokens: invokeResult.outputTokens,\n durationMs: Date.now() - started,\n });\n } catch (err) {\n log.error({ err, batchId: batch.id }, \"failed to append provenance record\");\n // Continue — provenance failure should not roll back ingestion.\n }\n }\n\n // 8b. Pass B — fact extraction sidecar. Best-effort: a single call\n // per written page emits atomic facts + synthetic questions, the\n // SQLite store keeps the persistent record, FactIndex picks them\n // up for query_facts retrieval. Failures are logged + swallowed\n // (fact layer is additive; ingestion stays green either way).\n if (this.opts.factStore && this.opts.factIndex) {\n const active = isExtractionActive(this.opts.config, runtimeMode);\n if (active.active) {\n await this.runFactExtraction(newPages, runtimeMode, model, batch.id);\n } else {\n log.debug({ reason: active.reason }, \"fact extraction inactive — skipping\");\n }\n }\n\n // 9. Commit\n const changedPaths = [\n ...newPages.map((p) => p.path),\n ...mutated.map((p) => p.path),\n // Always include the generated index and provenance chain file\n // (even if it didn't change; git-add is idempotent).\n `${this.opts.store.wikiDir}/index.md`,\n ...(this.opts.provenance ? [this.opts.provenance.path] : []),\n ];\n const uniquePaths = [...new Set(changedPaths)];\n const commit = await commitWikiChanges({\n wikiRoot: this.opts.config.wiki_root,\n paths: uniquePaths,\n operationId: batch.id,\n operation: \"ingest\",\n metadata: {\n files: batch.paths.length,\n pages_written: newPages.length,\n cost_usd: invokeResult.totalCostUsd.toFixed(6),\n model,\n },\n });\n\n // 10. Process any deletions in this same batch. Deletes run AFTER\n // adds so an add+delete window (e.g. rename) still produces a\n // coherent final state. See Feature 2: deletion handling.\n if (batch.deletedPaths.length > 0) {\n await this.archiveDeletedSources(batch.deletedPaths, batch);\n }\n\n const outcome: IngestionOutcome = {\n batchId: batch.id,\n skipped: false,\n pagesWritten: newPages.length,\n costUsd: invokeResult.totalCostUsd,\n gitSha: commit.sha,\n durationMs: Date.now() - started,\n };\n log.info(outcome, \"batch complete\");\n this.lastOutcome = outcome;\n return outcome;\n }\n\n /**\n * Pass B sidecar: for each newly written page, run the LLM\n * fact-extractor, supersede any prior active facts for the same page,\n * insert the new facts + synthetic questions into the SQLite store,\n * keep the in-memory {@link FactIndex} in sync, and emit a\n * `fact_extracted` provenance record. Best-effort — any single-page\n * extraction failure logs + continues to the next page.\n */\n private async runFactExtraction(\n newPages: Awaited<ReturnType<typeof reconcileWrittenPages>>[\"pages\"],\n runtimeMode: RuntimeMode,\n model: string,\n batchId: string,\n ): Promise<void> {\n const log = getLogger(\"ingestion\");\n const store = this.opts.factStore;\n const index = this.opts.factIndex;\n if (!store || !index) return;\n const wikiRoot = this.opts.config.wiki_root;\n\n for (const page of newPages) {\n const relPath = relative(wikiRoot, page.path) || page.path;\n let extraction;\n try {\n extraction = await extractFactsFromPage({\n config: this.opts.config,\n runtimeMode,\n wikiPageId: relPath,\n pageBody: page.body,\n title: page.frontmatter.title,\n costTracker: this.opts.costTracker,\n });\n } catch (err) {\n log.warn(\n {\n err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\",\n page: relPath,\n batchId,\n },\n \"fact extraction threw — skipping page\",\n );\n continue;\n }\n if (!extraction.ran) {\n log.debug({ reason: extraction.skipReason, page: relPath }, \"fact extraction skipped\");\n continue;\n }\n if (extraction.facts.length === 0) {\n log.debug({ page: relPath }, \"fact extraction produced zero facts\");\n continue;\n }\n\n // Supersede any active facts for this page (re-ingest case), then\n // insert the new ones inside the same SQLite transaction effect.\n const superseded = store.supersedeByWikiPage(relPath);\n const added: string[] = [];\n for (const f of extraction.facts) {\n try {\n const { id, fact_hash } = store.insertFact({\n wiki_page_id: relPath,\n entity: f.entity,\n statement: f.statement,\n });\n const questions = store.insertQuestions(id, f.questions);\n const fact = store.getFact(id);\n if (fact) index.add(fact, questions);\n added.push(fact_hash);\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\", page: relPath },\n \"fact insert failed — continuing\",\n );\n }\n }\n\n // Emit a fact_extracted provenance record so an external auditor\n // can correlate which facts entered the index at which point in\n // the chain. Best-effort: failures don't roll back the SQLite\n // inserts (the facts themselves are authoritative).\n if (this.opts.provenance && (added.length > 0 || superseded.length > 0)) {\n try {\n await this.opts.provenance.append({\n type: \"fact_extracted\",\n source_files: [relPath],\n source_hashes: [sha256Hex(page.body)],\n prompt_hash: sha256Hex(extraction.rawResponse.slice(0, 4096)),\n model_id: model,\n response_hash: sha256Hex(extraction.rawResponse),\n wiki_files_written: [],\n wiki_file_hashes_after: {},\n fact_hashes_added: added,\n fact_hashes_superseded: superseded,\n metadata: {\n batch_id: batchId,\n facts_added: added.length,\n facts_superseded: superseded.length,\n cost_usd: Number(extraction.costUsd.toFixed(6)),\n duration_ms: extraction.durationMs,\n },\n });\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\", page: relPath },\n \"fact_extracted provenance append failed\",\n );\n }\n }\n }\n }\n\n /**\n * Archive wiki pages whose source files were deleted. For every deleted\n * raw file we consult the provenance chain for records that touched it,\n * collect the union of wiki pages those records wrote, mark each page\n * with frontmatter `status: orphaned` / `orphaned_at` / `orphaned_source`,\n * rewrite it, and append a single `\"archive\"` provenance record\n * summarising the operation. Wiki files are NEVER deleted — orphaning\n * is reversible and preserves history.\n *\n * Returns the number of wiki pages that were marked orphaned by this\n * call. Deletions with no matching provenance records (e.g. a raw file\n * deleted before any batch consumed it) still produce a lightweight\n * archive record so the chain shows the deletion was observed.\n */\n private async archiveDeletedSources(\n deletedAbsPaths: string[],\n batch: WatcherBatch,\n ): Promise<number> {\n const log = getLogger(\"ingestion\");\n const wikiRoot = this.opts.config.wiki_root;\n const toRel = (abs: string): string => relative(wikiRoot, abs) || abs;\n\n // 1. Collect the affected wiki pages via provenance lookup.\n const affected = new Map<string, Set<string>>(); // abs wiki path → rel source paths\n if (this.opts.provenance) {\n for (const deleted of deletedAbsPaths) {\n const relDeleted = toRel(deleted);\n // recordsFor matches both source_files and wiki_files_written.\n // We only want records whose SOURCE was this file.\n const records = await this.opts.provenance.recordsFor(relDeleted);\n for (const rec of records) {\n if (!rec.source_files.includes(relDeleted)) continue;\n for (const relWikiPath of rec.wiki_files_written) {\n const absWiki = resolve(wikiRoot, relWikiPath);\n if (!affected.has(absWiki)) affected.set(absWiki, new Set());\n affected.get(absWiki)!.add(relDeleted);\n }\n }\n }\n }\n\n // 2. Mark each affected wiki page as orphaned.\n const nowIso = new Date().toISOString();\n const rewrittenPages: string[] = [];\n for (const [absWiki, sources] of affected) {\n const page = await this.opts.store.readPage(absWiki);\n if (!page) continue; // Page was already deleted or never existed.\n const existingSources = new Set(page.frontmatter.orphaned_source ?? []);\n for (const s of sources) existingSources.add(s);\n page.frontmatter.status = \"orphaned\";\n if (!page.frontmatter.orphaned_at) {\n page.frontmatter.orphaned_at = nowIso;\n }\n page.frontmatter.orphaned_source = [...existingSources].sort();\n page.frontmatter.updated = nowIso.slice(0, 10);\n await this.opts.store.writePage(page);\n rewrittenPages.push(absWiki);\n }\n\n // 3. Rebuild index + search so orphaned status is reflected everywhere.\n if (rewrittenPages.length > 0) {\n const allPages = await loadAllPages(this.opts.store);\n await this.opts.indexManager.rebuild(allPages);\n this.opts.search.rebuild(allPages);\n }\n\n // 4. Append a provenance \"archive\" record. Even when no pages were\n // rewritten (orphan from a never-ingested source) we still log the\n // archive so the chain records the deletion event.\n if (this.opts.provenance) {\n try {\n const hashesByAbs = await sha256Files(rewrittenPages);\n const wikiFileHashes: Record<string, string> = {};\n for (const abs of rewrittenPages) {\n const h = hashesByAbs[abs];\n if (h) wikiFileHashes[toRel(abs)] = h;\n }\n const deletedRel = deletedAbsPaths.map(toRel);\n await this.opts.provenance.append({\n type: \"archive\",\n source_files: deletedRel,\n // Sources are gone — their prior hashes live in earlier records.\n source_hashes: deletedRel.map(() => \"deleted\"),\n prompt_hash: sha256Hex(\"archive\"),\n model_id: \"none\",\n response_hash: sha256Hex(\"archive\"),\n wiki_files_written: Object.keys(wikiFileHashes),\n wiki_file_hashes_after: wikiFileHashes,\n metadata: {\n batch_id: batch.id,\n deleted_sources: deletedRel.length,\n orphaned_pages: rewrittenPages.length,\n },\n });\n } catch (err) {\n log.error({ err, batchId: batch.id }, \"failed to append archive provenance record\");\n }\n }\n\n // 5. Commit the archive as a single git commit so the deletion shows\n // up in history alongside the index refresh.\n try {\n const paths = [\n ...rewrittenPages,\n `${this.opts.store.wikiDir}/index.md`,\n ...(this.opts.provenance ? [this.opts.provenance.path] : []),\n ];\n await commitWikiChanges({\n wikiRoot: this.opts.config.wiki_root,\n paths: [...new Set(paths)],\n operationId: batch.id,\n operation: \"archive\",\n metadata: {\n deleted: deletedAbsPaths.length,\n orphaned_pages: rewrittenPages.length,\n },\n });\n } catch (err) {\n log.warn({ err, batchId: batch.id }, \"archive commit failed (non-fatal)\");\n }\n\n log.info(\n {\n batchId: batch.id,\n deleted: deletedAbsPaths.length,\n orphanedPages: rewrittenPages.length,\n },\n \"archive complete\",\n );\n return rewrittenPages.length;\n }\n\n /**\n * Append a provenance record describing a completed ingestion batch.\n * Source and wiki-file hashes are precomputed by the caller at the\n * point of read/write (M-PIPE-1) and threaded in here. Paths are stored\n * as wiki-relative strings so the chain is portable across machines\n * with different wiki_root values.\n */\n private async recordProvenance(args: {\n batch: WatcherBatch;\n model: string;\n promptText: string;\n /** Review item 20 (X1-C2): independent hash of the system prompt. */\n systemPromptHash?: string;\n responseText: string;\n sourceFiles: string[];\n sourceHashes: string[];\n wikiFileHashes: Record<string, string>;\n costUsd: number;\n inputTokens: number;\n outputTokens: number;\n durationMs: number;\n }): Promise<void> {\n if (!this.opts.provenance) return;\n const wikiRoot = this.opts.config.wiki_root;\n // Use path.relative for portable, platform-correct relative paths\n // (L-CODE-1). The previous substring-replace approach broke on\n // Windows separators and on paths where `wikiRoot` appeared mid-path.\n const toRel = (abs: string): string => relative(wikiRoot, abs) || abs;\n\n await this.opts.provenance.append({\n type: \"ingest\",\n source_files: args.sourceFiles.map(toRel),\n source_hashes: args.sourceHashes,\n prompt_hash: sha256Hex(args.promptText),\n model_id: args.model,\n response_hash: sha256Hex(args.responseText),\n wiki_files_written: Object.keys(args.wikiFileHashes),\n wiki_file_hashes_after: args.wikiFileHashes,\n metadata: {\n batch_id: args.batch.id,\n cost_usd: Number(args.costUsd.toFixed(6)),\n input_tokens: args.inputTokens,\n output_tokens: args.outputTokens,\n duration_ms: args.durationMs,\n // Review item 20: stable hash of the system prompt independent\n // of sanitize-rule drift. With this + source_hashes (already\n // present), an auditor can verify \"this source bytes + this\n // system prompt template → that response_hash\" without storing\n // the rendered post-sanitize prompt text.\n ...(args.systemPromptHash ? { system_prompt_hash: args.systemPromptHash } : {}),\n },\n });\n }\n}\n","/**\n * Adaptive debounce batcher. Collects events until a quiet period is seen,\n * then flushes them as a batch. When events keep streaming in (a \"burst\")\n * the debounce window grows geometrically up to a ceiling. This lets us\n * batch huge drops without firing mid-copy, while still responding quickly\n * to single-file saves.\n *\n * Deletions (`pushDelete`) flow through the same timer and size caps as\n * adds — the flush handler receives both sets so the ingestion queue can\n * process adds first and deletions after (Feature 2: deletion handling).\n *\n * Integration contract:\n * const batcher = new DebounceBatcher(opts, (adds, deletes) => onFlush(adds, deletes));\n * batcher.push(path); // an added / changed file\n * batcher.pushDelete(path); // a removed file\n * await batcher.flushNow(); // force-flush (tests / shutdown)\n * batcher.stop(); // cancel any pending timer\n */\nexport interface DebounceOptions {\n initialMs: number;\n maxMs: number;\n growthFactor: number;\n burstThreshold: number;\n maxBatchSize: number;\n}\n\n/**\n * Flush handler signature. `deletes` is always supplied (may be empty)\n * so the handler can implement a single \"adds then deletes\" path.\n */\nexport type FlushHandler = (paths: string[], deletes: string[]) => void | Promise<void>;\n\nexport class DebounceBatcher {\n private readonly opts: DebounceOptions;\n private readonly onFlush: FlushHandler;\n private pending = new Set<string>();\n private pendingDeletes = new Set<string>();\n private timer: NodeJS.Timeout | null = null;\n private currentWait: number;\n private eventsSinceLastFlush = 0;\n private stopped = false;\n\n constructor(opts: DebounceOptions, onFlush: FlushHandler) {\n this.opts = opts;\n this.onFlush = onFlush;\n this.currentWait = opts.initialMs;\n }\n\n /** Add a path to the pending set and (re)arm the timer. */\n push(path: string): void {\n if (this.stopped) return;\n // If the path was previously marked for deletion in this same window,\n // the add supersedes the delete (file was re-created).\n this.pendingDeletes.delete(path);\n this.pending.add(path);\n this.registerEvent();\n }\n\n /**\n * Mark a path as deleted. Flows through the same timer/burst/size\n * machinery as {@link push} so an add+delete combo lands in the same\n * flush. If the path was previously pending an add in this window, the\n * delete supersedes it (file was added then removed before flush).\n */\n pushDelete(path: string): void {\n if (this.stopped) return;\n this.pending.delete(path);\n this.pendingDeletes.add(path);\n this.registerEvent();\n }\n\n /** Flush immediately, cancelling any pending timer. */\n async flushNow(): Promise<void> {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n if (this.pending.size === 0 && this.pendingDeletes.size === 0) return;\n const paths = [...this.pending].sort();\n const deletes = [...this.pendingDeletes].sort();\n this.pending.clear();\n this.pendingDeletes.clear();\n this.eventsSinceLastFlush = 0;\n this.currentWait = this.opts.initialMs; // reset back to baseline\n await this.onFlush(paths, deletes);\n }\n\n /** Permanently stop the batcher. Cancels any pending flush. */\n stop(): void {\n this.stopped = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.pending.clear();\n this.pendingDeletes.clear();\n }\n\n /** Total pending event count (adds + deletes). */\n size(): number {\n return this.pending.size + this.pendingDeletes.size;\n }\n\n /** Current debounce wait window in milliseconds. */\n currentWindow(): number {\n return this.currentWait;\n }\n\n private registerEvent(): void {\n this.eventsSinceLastFlush += 1;\n\n if (this.eventsSinceLastFlush >= this.opts.burstThreshold) {\n // burst detected → grow the wait window\n this.currentWait = Math.min(\n this.opts.maxMs,\n Math.round(this.currentWait * this.opts.growthFactor),\n );\n }\n\n // Hard cap on combined add+delete batch size.\n if (this.pending.size + this.pendingDeletes.size >= this.opts.maxBatchSize) {\n void this.flushNow();\n return;\n }\n\n this.arm();\n }\n\n private arm(): void {\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flushNow();\n }, this.currentWait);\n // intentional: do NOT unref — we want the flush to keep the event loop alive\n }\n}\n","/**\n * Classify chokidar events into high-level ingestion intents.\n *\n * - A newly-created file that has never been seen before → \"new\"\n * - A modified file whose hash changed → \"update\"\n * - An unchanged file (saved but identical bytes) → \"noop\"\n * - A deleted file → \"removed\"\n *\n * The classifier is backed by an in-memory map of absolute path → sha256.\n * Hashes are computed lazily; callers pass in file contents.\n */\nimport { sha256 } from \"../provenance/hash.js\";\n\nexport type ClassificationIntent = \"new\" | \"update\" | \"noop\" | \"removed\";\n\nexport interface Classification {\n intent: ClassificationIntent;\n path: string;\n previousHash: string | null;\n currentHash: string | null;\n}\n\nexport class EventClassifier {\n private readonly hashes = new Map<string, string>();\n\n /** Seed the classifier from an initial scan. */\n seed(path: string, contents: string | Buffer): void {\n this.hashes.set(path, sha256(contents));\n }\n\n /** Forget everything. */\n reset(): void {\n this.hashes.clear();\n }\n\n /**\n * Classify an add / change event. Returns \"noop\" if the file contents\n * are byte-identical to the previously recorded hash.\n */\n classifyAddOrChange(path: string, contents: string | Buffer): Classification {\n const currentHash = sha256(contents);\n const previousHash = this.hashes.get(path) ?? null;\n this.hashes.set(path, currentHash);\n if (previousHash === null) {\n return { intent: \"new\", path, previousHash, currentHash };\n }\n if (previousHash === currentHash) {\n return { intent: \"noop\", path, previousHash, currentHash };\n }\n return { intent: \"update\", path, previousHash, currentHash };\n }\n\n /** Classify a removal event. */\n classifyRemove(path: string): Classification {\n const previousHash = this.hashes.get(path) ?? null;\n this.hashes.delete(path);\n return { intent: \"removed\", path, previousHash, currentHash: null };\n }\n\n /** Number of files currently tracked. */\n size(): number {\n return this.hashes.size;\n }\n}\n","/**\n * Ignore patterns for the file watcher. We mix defaults (dotfiles, OS junk,\n * temp files) with user-supplied globs from config.\n */\nimport { basename } from \"node:path\";\n\nexport const DEFAULT_IGNORES: readonly string[] = [\n \"**/.DS_Store\",\n \"**/Thumbs.db\",\n \"**/.*\", // all dotfiles\n \"**/*.swp\",\n \"**/*.swo\",\n \"**/*~\",\n \"**/*.tmp\",\n \"**/*.part\",\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/.wotw*.tmp\", // our own atomic-write staging files\n];\n\n/**\n * Return the merged list of ignore globs: defaults first, then user patterns.\n * Duplicates are removed preserving the first occurrence.\n */\nexport function resolveIgnores(userPatterns: readonly string[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const p of [...DEFAULT_IGNORES, ...userPatterns]) {\n if (!seen.has(p)) {\n seen.add(p);\n out.push(p);\n }\n }\n return out;\n}\n\n/**\n * Lightweight pre-filter applied inside event handlers — chokidar's glob\n * matcher handles most cases, but we still want to drop transient files\n * like \".foo.md.swp\" that race in before the glob fires.\n */\nexport function shouldIgnoreBasename(name: string): boolean {\n const base = basename(name);\n if (base.startsWith(\".\")) return true;\n if (base.endsWith(\".tmp\") || base.endsWith(\".part\") || base.endsWith(\"~\")) return true;\n if (base.endsWith(\".swp\") || base.endsWith(\".swo\")) return true;\n return false;\n}\n","/**\n * File watcher subsystem. Wraps chokidar with our adaptive debounce and\n * event classifier, then emits batches as ingestion intents.\n *\n * The watcher is a {@link DaemonSubsystem}: start() begins watching the\n * configured raw directory, stop() tears down chokidar and cancels the\n * pending debounce timer.\n */\nimport { readFileSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport type { DaemonSubsystem } from \"../daemon/index.js\";\nimport { DebounceBatcher } from \"./debounce.js\";\nimport { EventClassifier, type ClassificationIntent } from \"./event-classifier.js\";\nimport { resolveIgnores, shouldIgnoreBasename } from \"./ignore-patterns.js\";\n\n/**\n * In CLI mode the daemon spawns the `claude` binary for every batch, which\n * is more expensive (process spawn + bundled CLI overhead) and is rate-\n * limited by the user's subscription rather than the API. We absorb a bit\n * more latency to coalesce more files into each spawn — empirically a 1.5×\n * multiplier on the debounce window halves the spawn rate without making\n * interactive editing feel laggy.\n */\nconst CLI_DEBOUNCE_MULTIPLIER = 1.5;\n\n/**\n * A batch handed off to the ingestion queue. Paths are absolute and sorted.\n * `reasons` maps path → intent so the ingestion layer can decide whether\n * to create a new page vs update an existing one.\n *\n * `deletedPaths` carries any files removed from the raw tree during the\n * same debounce window as the adds. The ingestion queue processes adds\n * first and deletes second, so a net add+delete within one window still\n * produces a coherent wiki state.\n */\nexport interface WatcherBatch {\n id: string;\n createdAt: string;\n paths: string[];\n reasons: Record<string, ClassificationIntent>;\n deletedPaths: string[];\n}\n\n/**\n * Review item 25: optional handler return value. When the consumer\n * (IngestionQueue) knows a batch was skipped for a reason that should\n * NOT mark the files as processed (daily-budget-exceeded, per-ingest\n * cap), it returns `{ retainForRetry: true }` so the watcher re-tries\n * those paths on the next reconciliation pass. When the handler returns\n * undefined/void, the legacy \"mark processed\" semantics apply.\n */\nexport interface BatchHandlerResult {\n retainForRetry?: boolean;\n}\n\nexport type BatchHandler = (\n batch: WatcherBatch,\n) => Promise<BatchHandlerResult | void> | BatchHandlerResult | void;\n\nexport interface WatcherOptions {\n config: WotwConfig;\n onBatch: BatchHandler;\n /**\n * Resolved runtime mode. When `\"cli\"`, the debounce window is widened by\n * {@link CLI_DEBOUNCE_MULTIPLIER} to coalesce more files per `claude`\n * spawn. Defaults to `\"api\"` so existing tests are unaffected.\n */\n runtimeMode?: RuntimeMode;\n /** Called when a file exceeds the retry limit. */\n onDropped?: (path: string, reason: string) => void;\n}\n\n/**\n * Chokidar-backed watcher with adaptive debouncing. Emits classified batches\n * via the `onBatch` handler.\n */\nexport class FileWatcher implements DaemonSubsystem {\n readonly name = \"watcher\";\n private readonly opts: WatcherOptions;\n private readonly classifier = new EventClassifier();\n private readonly batcher: DebounceBatcher;\n private readonly retryCount = new Map<string, number>();\n private readonly processedPaths = new Set<string>();\n private reconciliationTimer: ReturnType<typeof setInterval> | null = null;\n private watcher: FSWatcher | null = null;\n private ready = false;\n private degraded = false;\n\n constructor(opts: WatcherOptions) {\n this.opts = opts;\n const cliMode = opts.runtimeMode === \"cli\";\n const initialMs = cliMode\n ? Math.round(opts.config.watcher.debounce_initial_ms * CLI_DEBOUNCE_MULTIPLIER)\n : opts.config.watcher.debounce_initial_ms;\n const maxMs = cliMode\n ? Math.round(opts.config.watcher.debounce_max_ms * CLI_DEBOUNCE_MULTIPLIER)\n : opts.config.watcher.debounce_max_ms;\n this.batcher = new DebounceBatcher(\n {\n initialMs,\n maxMs,\n growthFactor: opts.config.watcher.debounce_growth_factor,\n burstThreshold: opts.config.watcher.burst_threshold,\n maxBatchSize: opts.config.watcher.max_batch_size,\n },\n (paths, deletes) => this.emitBatch(paths, deletes),\n );\n }\n\n async start(): Promise<void> {\n const log = getLogger(\"watcher\");\n const rawPath = this.opts.config.raw_path;\n const ignores = resolveIgnores(this.opts.config.watcher.ignore_patterns);\n\n log.info({ rawPath, ignores }, \"starting watcher\");\n\n this.watcher = chokidar.watch(rawPath, {\n ignored: ignores,\n ignoreInitial: false, // pick up files present on startup\n persistent: true,\n awaitWriteFinish: {\n stabilityThreshold: 200,\n pollInterval: 50,\n },\n atomic: true,\n });\n\n this.watcher.on(\"add\", (path) => this.handleAddOrChange(path, \"add\"));\n this.watcher.on(\"change\", (path) => this.handleAddOrChange(path, \"change\"));\n this.watcher.on(\"unlink\", (path) => this.handleUnlink(path));\n this.watcher.on(\"error\", (err) => {\n log.error({ err }, \"watcher error\");\n this.degraded = true;\n });\n this.watcher.on(\"ready\", () => {\n this.ready = true;\n log.info({ tracked: this.classifier.size() }, \"watcher ready\");\n });\n }\n\n async stop(): Promise<void> {\n const log = getLogger(\"watcher\");\n log.info(\"stopping watcher\");\n if (this.reconciliationTimer) {\n clearInterval(this.reconciliationTimer);\n this.reconciliationTimer = null;\n }\n this.batcher.stop();\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n }\n\n /** Pending batch size — exposed for status output. */\n pendingCount(): number {\n return this.batcher.size();\n }\n\n /** True once chokidar's initial scan has completed. */\n isReady(): boolean {\n return this.ready;\n }\n\n /** True if the watcher encountered an error and may be missing events. */\n isDegraded(): boolean {\n return this.degraded;\n }\n\n private handleAddOrChange(path: string, kind: \"add\" | \"change\"): void {\n if (shouldIgnoreBasename(path)) return;\n let contents: string;\n try {\n contents = readFileSync(path, \"utf8\");\n } catch (err) {\n getLogger(\"watcher\").warn({ err, path, kind }, \"failed to read file for classification\");\n return;\n }\n const cls = this.classifier.classifyAddOrChange(path, contents);\n if (cls.intent === \"noop\") return;\n this.batcher.push(path);\n }\n\n private handleUnlink(path: string): void {\n if (shouldIgnoreBasename(path)) return;\n this.classifier.classifyRemove(path);\n getLogger(\"watcher\").info({ path }, \"file removed — queuing for archive\");\n // Route the deletion through the same debouncer as adds. The\n // ingestion queue will archive the affected wiki pages (mark them\n // orphaned) during the next flush. See Feature 2.\n this.batcher.pushDelete(path);\n }\n\n private async emitBatch(paths: string[], deletes: string[]): Promise<void> {\n const log = getLogger(\"watcher\");\n const reasons: Record<string, ClassificationIntent> = {};\n // We classified each path as it arrived; for the batch handoff, mark\n // them as \"new\" or \"update\" based on whether we've seen the hash before.\n // (The classifier already holds the latest hash map.)\n for (const p of paths) {\n reasons[p] = \"update\"; // benign default; real intent is used by ingestion\n }\n const batch: WatcherBatch = {\n id: `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n createdAt: new Date().toISOString(),\n paths,\n reasons,\n deletedPaths: deletes,\n };\n log.info({ batchId: batch.id, count: paths.length, deletes: deletes.length }, \"flushing batch\");\n try {\n const handlerResult = await this.opts.onBatch(batch);\n // Review item 25: don't mark processed when the handler signals\n // \"retain for retry\" (daily-budget-exceeded, per-ingest cap).\n // Without this gate budget-skipped files were silently lost.\n if (!handlerResult || handlerResult.retainForRetry !== true) {\n for (const p of batch.paths) this.processedPaths.add(p);\n } else {\n log.info(\n { batchId: batch.id, count: batch.paths.length },\n \"batch retained for retry — not marking files processed\",\n );\n }\n } catch (err: unknown) {\n const errMessage = err instanceof Error ? err.message : String(err);\n log.error({ err: errMessage, batchId: batch.id }, \"batch handler failed — re-queuing files\");\n for (const p of batch.paths) {\n const retries = (this.retryCount.get(p) ?? 0) + 1;\n if (retries > 3) {\n log.error({ path: p, retries }, \"file exceeded retry limit — sending to DLQ\");\n this.retryCount.delete(p);\n this.opts.onDropped?.(p, errMessage);\n } else {\n this.retryCount.set(p, retries);\n this.batcher.push(p);\n }\n }\n }\n }\n\n /**\n * Start periodic reconciliation to catch files missed by the watcher.\n * Scans the raw directory and re-queues any files not already processed.\n */\n startReconciliation(intervalMs: number): void {\n if (intervalMs <= 0) return;\n const log = getLogger(\"watcher\");\n this.reconciliationTimer = setInterval(() => {\n try {\n const rawPath = this.opts.config.raw_path;\n const files = this.walkRawFiles(rawPath);\n let requeued = 0;\n for (const f of files) {\n if (!this.processedPaths.has(f)) {\n this.batcher.push(f);\n requeued++;\n }\n }\n if (requeued > 0) {\n log.info({ requeued }, \"reconciliation found unprocessed files\");\n }\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"reconciliation scan failed\",\n );\n }\n }, intervalMs);\n this.reconciliationTimer.unref();\n }\n\n private walkRawFiles(dir: string): string[] {\n const out: string[] = [];\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const e of entries) {\n if (e.name.startsWith(\".\")) continue;\n const full = join(dir, e.name);\n if (e.isDirectory()) {\n out.push(...this.walkRawFiles(full));\n } else if (e.isFile()) {\n out.push(full);\n }\n }\n } catch {\n /* skip unreadable dirs */\n }\n return out;\n }\n}\n\nexport { DebounceBatcher } from \"./debounce.js\";\nexport { EventClassifier, type ClassificationIntent } from \"./event-classifier.js\";\nexport { DEFAULT_IGNORES, resolveIgnores, shouldIgnoreBasename } from \"./ignore-patterns.js\";\n","/**\n * SSRF-hardened fetch for downloading user-uploaded content from signed\n * URLs (Supabase Storage, S3 presigned, etc.). Layered defenses per the\n * REVIEW-LAYER-1-DAEMON.md X4-C-1 recommendation: IP-level rejection\n * FIRST, content-length cap SECOND, streaming-to-disk with byte-count\n * enforcement THIRD.\n *\n * Closes review items 49 (SSRF) + 52 (no AbortSignal timeout) + 53 (raw\n * err.message echo) + 58 (no content-type validation).\n */\nimport { lookup } from \"node:dns/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport { Readable } from \"node:stream\";\nimport { isIP } from \"node:net\";\n\nexport interface SafeFetchOptions {\n /** Absolute filesystem path to write the downloaded body to. */\n targetPath: string;\n /** Wall-clock cap in milliseconds. Default 30_000. */\n timeoutMs?: number;\n /** Byte cap. Default 32 MiB. */\n maxBytes?: number;\n /** Optional case-insensitive Content-Type prefix allowlist. */\n contentTypeAllowlist?: string[];\n /** Optional hostname substring allowlist. */\n hostnameAllowlist?: string[];\n}\n\nexport interface SafeFetchResult {\n bytes: number;\n contentType: string | null;\n}\n\nexport type SafeFetchErrorCode =\n | \"INVALID_URL\"\n | \"INVALID_SCHEME\"\n | \"HOSTNAME_NOT_ALLOWED\"\n | \"PRIVATE_IP_BLOCKED\"\n | \"UPSTREAM_NON_2XX\"\n | \"CONTENT_LENGTH_TOO_LARGE\"\n | \"CONTENT_LENGTH_DURING_STREAM\"\n | \"CONTENT_TYPE_NOT_ALLOWED\"\n | \"FETCH_TIMEOUT\"\n | \"FETCH_FAILED\"\n | \"WRITE_FAILED\";\n\nexport class SafeFetchError extends Error {\n readonly code: SafeFetchErrorCode;\n constructor(code: SafeFetchErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = \"SafeFetchError\";\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_MAX_BYTES = 32 * 1024 * 1024;\n\n/**\n * Returns true when the IPv4 / IPv6 string belongs to a range that an\n * external public download should never reach. Exported for unit testing.\n */\nexport function isBlockedIp(ip: string): boolean {\n const family = isIP(ip);\n if (family === 4) return isBlockedIpv4(ip);\n if (family === 6) return isBlockedIpv6(ip);\n return true;\n}\n\nfunction isBlockedIpv4(ip: string): boolean {\n const parts = ip.split(\".\").map((p) => Number(p));\n if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n) || n < 0 || n > 255)) {\n return true;\n }\n const [a, b, c] = parts as [number, number, number, number];\n if (a === 0) return true;\n if (a === 10) return true;\n if (a === 127) return true;\n if (a === 169 && b === 254) return true;\n if (a === 172 && b >= 16 && b <= 31) return true;\n if (a === 192 && b === 168) return true;\n if (a === 192 && b === 0 && c === 0) return true;\n if (a === 100 && b >= 64 && b <= 127) return true;\n if (a >= 224) return true;\n return false;\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const lower = ip.toLowerCase();\n if (lower === \"::\" || lower === \"::1\") return true;\n if (lower.startsWith(\"fe80:\") || lower.startsWith(\"fe80::\")) return true;\n if (lower.startsWith(\"fc\") || lower.startsWith(\"fd\")) return true;\n const v4MapMatch = lower.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (v4MapMatch && v4MapMatch[1]) return isBlockedIpv4(v4MapMatch[1]);\n if (lower.startsWith(\"ff\")) return true;\n return false;\n}\n\n/**\n * Download a remote URL to a local file with layered SSRF defenses.\n *\n * Order of checks (each must pass before the next runs):\n * 1. URL parse + scheme=https + optional hostname allowlist\n * 2. DNS lookup; reject if any resolved address is private/loopback/etc.\n * 3. HTTP GET with AbortSignal timeout and redirect:\"error\"\n * 4. Response must be 2xx\n * 5. Optional Content-Type allowlist check\n * 6. Content-Length header (if present) must be <= maxBytes\n * 7. Stream body to disk; abort if running total exceeds maxBytes\n *\n * On any failure the (partial) target file is removed.\n */\nexport async function safeFetchToFile(\n url: string,\n opts: SafeFetchOptions,\n): Promise<SafeFetchResult> {\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;\n\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new SafeFetchError(\"INVALID_URL\", \"URL parse failed\");\n }\n if (parsed.protocol !== \"https:\") {\n throw new SafeFetchError(\"INVALID_SCHEME\", \"scheme must be https\");\n }\n if (opts.hostnameAllowlist && opts.hostnameAllowlist.length > 0) {\n const hostLower = parsed.hostname.toLowerCase();\n const allowed = opts.hostnameAllowlist.some((suffix) =>\n hostLower.includes(suffix.toLowerCase()),\n );\n if (!allowed) {\n throw new SafeFetchError(\"HOSTNAME_NOT_ALLOWED\", \"hostname not in allowlist\");\n }\n }\n\n let addrs: { address: string }[];\n try {\n addrs = await lookup(parsed.hostname, { all: true });\n } catch {\n throw new SafeFetchError(\"FETCH_FAILED\", \"dns lookup failed\");\n }\n if (addrs.length === 0) {\n throw new SafeFetchError(\"FETCH_FAILED\", \"dns returned no addresses\");\n }\n for (const a of addrs) {\n if (isBlockedIp(a.address)) {\n throw new SafeFetchError(\"PRIVATE_IP_BLOCKED\", \"host resolves to a blocked address\");\n }\n }\n\n const controller = new AbortController();\n const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetch(url, { signal: controller.signal, redirect: \"error\" });\n } catch {\n clearTimeout(timeoutHandle);\n if (controller.signal.aborted) {\n throw new SafeFetchError(\"FETCH_TIMEOUT\", \"fetch timed out\");\n }\n throw new SafeFetchError(\"FETCH_FAILED\", \"fetch failed\");\n }\n\n if (!res.ok) {\n clearTimeout(timeoutHandle);\n throw new SafeFetchError(\"UPSTREAM_NON_2XX\", `upstream returned ${res.status}`);\n }\n\n const contentType = res.headers.get(\"content-type\");\n if (opts.contentTypeAllowlist && opts.contentTypeAllowlist.length > 0) {\n const lower = (contentType ?? \"\").toLowerCase();\n const allowed = opts.contentTypeAllowlist.some((prefix) =>\n lower.startsWith(prefix.toLowerCase()),\n );\n if (!allowed) {\n clearTimeout(timeoutHandle);\n throw new SafeFetchError(\"CONTENT_TYPE_NOT_ALLOWED\", \"content-type not in allowlist\");\n }\n }\n\n const declaredLength = res.headers.get(\"content-length\");\n if (declaredLength !== null) {\n const n = Number(declaredLength);\n if (!Number.isFinite(n) || n > maxBytes) {\n clearTimeout(timeoutHandle);\n throw new SafeFetchError(\"CONTENT_LENGTH_TOO_LARGE\", \"content-length exceeds cap\");\n }\n }\n\n if (!res.body) {\n clearTimeout(timeoutHandle);\n throw new SafeFetchError(\"FETCH_FAILED\", \"response had no body\");\n }\n\n let bytesWritten = 0;\n const sink = createWriteStream(opts.targetPath);\n const sinkErrors: Error[] = [];\n sink.on(\"error\", (e) => sinkErrors.push(e));\n\n try {\n const source = Readable.fromWeb(res.body as never);\n for await (const chunk of source) {\n const buf = chunk as Buffer;\n bytesWritten += buf.length;\n if (bytesWritten > maxBytes) {\n controller.abort();\n sink.destroy();\n throw new SafeFetchError(\n \"CONTENT_LENGTH_DURING_STREAM\",\n \"body exceeded max bytes during stream\",\n );\n }\n if (!sink.write(buf)) {\n await new Promise<void>((resolve) => sink.once(\"drain\", () => resolve()));\n }\n }\n await new Promise<void>((resolve, reject) => {\n sink.end((err: unknown) => {\n if (err) {\n const e = err instanceof Error ? err : new Error(\"sink write failed\");\n reject(e);\n } else if (sinkErrors.length > 0) {\n const first = sinkErrors[0];\n reject(first instanceof Error ? first : new Error(\"sink write failed\"));\n } else {\n resolve();\n }\n });\n });\n clearTimeout(timeoutHandle);\n return { bytes: bytesWritten, contentType };\n } catch (err) {\n clearTimeout(timeoutHandle);\n sink.destroy();\n await unlink(opts.targetPath).catch(() => undefined);\n if (err instanceof SafeFetchError) throw err;\n if (controller.signal.aborted) {\n throw new SafeFetchError(\"FETCH_TIMEOUT\", \"fetch timed out during stream\");\n }\n throw new SafeFetchError(\"WRITE_FAILED\", \"write failed during stream\");\n }\n}\n","/**\n * Query expansion — expand a user query into keyword variants via LLM\n * so BM25 can find conceptually related pages that use different vocabulary.\n *\n * Gate: `config.query.expand`. Falls back to original query on any failure.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport type { ModelRouter } from \"../ingestion/model-router.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\n\nexport interface QueryExpansionOptions {\n config: WotwConfig;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n runtimeMode: RuntimeMode;\n}\n\nexport interface QueryExpansionResult {\n /** The expanded query string (original + variants, OR-combined). */\n expandedQuery: string;\n /** The individual expansion terms returned by the LLM. */\n expansionTerms: string[];\n /** Whether expansion actually ran (false if disabled, budget exceeded, or failed). */\n expanded: boolean;\n /** Cost of the expansion LLM call. */\n costUsd: number;\n}\n\n/**\n * Expand a query into keyword variants via a small LLM call.\n * Returns the original query unchanged if expansion is disabled, budget\n * exceeded, or the LLM call fails.\n */\nexport async function expandQuery(\n originalQuery: string,\n opts: QueryExpansionOptions,\n): Promise<QueryExpansionResult> {\n const log = getLogger(\"query-expansion\");\n const notExpanded: QueryExpansionResult = {\n expandedQuery: originalQuery,\n expansionTerms: [],\n expanded: false,\n costUsd: 0,\n };\n\n if (!opts.config.query.expand) {\n return notExpanded;\n }\n\n const model =\n opts.runtimeMode === \"cli\"\n ? opts.config.execution.cli_model\n : opts.modelRouter.modelFor(\"query\");\n\n // Budget pre-flight — expansion is a small call (~100 in, ~50 out).\n const estimated = opts.runtimeMode === \"cli\" ? 0 : opts.modelRouter.computeCost(model, 200, 100);\n if (opts.runtimeMode !== \"cli\" && opts.costTracker.wouldExceedDaily(estimated)) {\n log.debug(\"skipping query expansion — daily budget exceeded\");\n return notExpanded;\n }\n\n try {\n const result = await runtimeAwareComplete(\n `Expand this search query into keyword variants. Return a JSON array of 5-10 alternative search terms that someone might use to describe the same concept. Include synonyms, related technical terms, and common phrasings. Query: \"${originalQuery}\". Respond ONLY with a JSON array of strings, no other text.`,\n {\n systemPrompt:\n \"You are a search query expansion assistant. Return ONLY a JSON array of strings, no other text.\",\n model,\n config: opts.config,\n runtimeMode: opts.runtimeMode,\n },\n );\n\n opts.costTracker.logUsage({\n operation: \"query\",\n model,\n costUsd: result.costUsd,\n inputTokens: result.inputTokens,\n outputTokens: result.outputTokens,\n });\n\n // Parse the response as a JSON array of strings.\n const text = result.text.trim();\n // Try to extract a JSON array from the response (it might have markdown fences).\n const jsonMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (!jsonMatch) {\n log.debug({ text }, \"query expansion returned non-JSON — falling back\");\n return { ...notExpanded, costUsd: result.costUsd };\n }\n\n const parsed: unknown = JSON.parse(jsonMatch[0]);\n if (!Array.isArray(parsed)) {\n log.debug(\"query expansion returned non-array — falling back\");\n return { ...notExpanded, costUsd: result.costUsd };\n }\n\n const terms = parsed.filter((t): t is string => typeof t === \"string\" && t.trim().length > 0);\n if (terms.length === 0) {\n return { ...notExpanded, costUsd: result.costUsd };\n }\n\n // Combine original query + expansion terms via space (minisearch uses OR by default).\n const expandedQuery = [originalQuery, ...terms].join(\" \");\n log.debug({ originalQuery, terms }, \"query expanded\");\n\n return {\n expandedQuery,\n expansionTerms: terms,\n expanded: true,\n costUsd: result.costUsd,\n };\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"query expansion failed — falling back to original query\",\n );\n return notExpanded;\n }\n}\n","/**\n * Query engine. Answers natural-language questions against the wiki by:\n *\n * 1. Running a full-text search over the wiki pages (minisearch)\n * 2. Assembling the top-k hits into a grounded context\n * 3. Invoking the query model via the Claude Agent SDK to write a\n * structured answer with inline citations\n *\n * The engine is used by both the `wotw query` CLI command and the MCP\n * `query` tool. It enforces the per-query budget cap and logs cost.\n */\nimport { errMsg } from \"../utils/errors.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport type { ModelRouter } from \"../ingestion/model-router.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport { type SearchHit } from \"../wiki/search.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport { sha256Hex } from \"../provenance/hash.js\";\nimport { expandQuery } from \"./query-expansion.js\";\nimport { recordQueryOutcome } from \"./query-metrics.js\";\n\n/** Maximum bytes of page body to inline per hit (clamps prompt size). */\nconst MAX_PAGE_BODY_BYTES = 16 * 1024;\n\nexport interface QueryEngineOptions {\n config: WotwConfig;\n store: WikiStore;\n search: WikiSearch;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n /** Optional provenance chain — if provided, each query appends a record. */\n provenance?: ProvenanceChain | null;\n /**\n * Resolved runtime mode. Defaults to \"api\" so legacy callers and test rigs\n * keep working without change. When set to \"cli\" the engine spawns the\n * `claude` binary for every query and logs cost=0 (subscription-covered).\n */\n runtimeMode?: RuntimeMode;\n}\n\nexport interface QueryResult {\n question: string;\n answer: string;\n sources: { path: string; title: string; score: number }[];\n costUsd: number;\n durationMs: number;\n model: string;\n skipped: boolean;\n skipReason?: string;\n}\n\nconst DEFAULT_K = 8;\n\nexport class QueryEngine {\n private readonly opts: QueryEngineOptions;\n\n constructor(opts: QueryEngineOptions) {\n this.opts = opts;\n }\n\n /**\n * Answer a natural-language question using wiki pages as context.\n * `k` controls the number of top hits to include as grounding context.\n */\n async answer(question: string, k: number = DEFAULT_K): Promise<QueryResult> {\n const log = getLogger(\"query\");\n const started = Date.now();\n const runtimeMode: RuntimeMode = this.opts.runtimeMode ?? \"api\";\n // CLI mode uses a single Sonnet model for everything (subscription-\n // covered). API mode lets the model-router pick a query-tier model.\n const model =\n runtimeMode === \"cli\"\n ? this.opts.config.execution.cli_model\n : this.opts.modelRouter.modelFor(\"query\");\n\n // Budget pre-flight — skipped in CLI mode (subscription-covered).\n // Review item 36: pre-fix estimate was 8K input + 2K output; the\n // actual single-pass prompt assembles 8 full pages at 16KB each\n // (= 128KB input, ~32K tokens) — 4× the old estimate. Update to\n // the realistic upper bound + invoke checkOperationBudget so the\n // per-query cap fires too, not just daily-exceeds.\n const estimated =\n runtimeMode === \"cli\" ? 0 : this.opts.modelRouter.computeCost(model, 32_000, 4_000);\n if (runtimeMode !== \"cli\") {\n const budgetErr = this.opts.costTracker.checkOperationBudget(\"query\", estimated);\n if (budgetErr) {\n return {\n question,\n answer: \"\",\n sources: [],\n costUsd: 0,\n durationMs: Date.now() - started,\n model,\n skipped: true,\n skipReason: budgetErr,\n };\n }\n }\n\n // Query expansion — expand the query into keyword variants via LLM.\n let searchQuery = question;\n let expansionCost = 0;\n try {\n const expansion = await expandQuery(question, {\n config: this.opts.config,\n costTracker: this.opts.costTracker,\n modelRouter: this.opts.modelRouter,\n runtimeMode,\n });\n if (expansion.expanded) {\n searchQuery = expansion.expandedQuery;\n expansionCost = expansion.costUsd;\n log.debug(\n { terms: expansion.expansionTerms.length },\n \"query expanded with keyword variants\",\n );\n }\n } catch {\n // Expansion failure is non-fatal — fall back to original query.\n }\n\n // Search index health pre-flight: if the wiki has pages but the\n // search index is empty, something went wrong during rebuild.\n if (this.opts.search.size() === 0 && this.opts.store.count() > 0) {\n recordQueryOutcome(this.opts.config.health.query_log_file, question, 0);\n return {\n question,\n answer: \"\",\n sources: [],\n costUsd: 0,\n durationMs: Date.now() - started,\n model,\n skipped: true,\n skipReason: \"search index is empty but wiki has pages — rebuild required\",\n };\n }\n\n // Retrieve top-k hits\n const hits = this.opts.search.search(searchQuery, k);\n log.info({ question, hits: hits.length, runtimeMode }, \"query received\");\n\n // Zero-hit grounding guard: refuse to answer from general knowledge.\n // The wiki's value is grounded answers backed by source material.\n if (hits.length === 0) {\n recordQueryOutcome(this.opts.config.health.query_log_file, question, 0);\n return {\n question,\n answer:\n \"No relevant wiki pages found for this query. Try ingesting source material on this topic first.\",\n sources: [],\n costUsd: 0,\n durationMs: Date.now() - started,\n model,\n skipped: false,\n };\n }\n\n // Pre-assemble full page bodies for each hit. This eliminates the\n // need for the model to use the Read tool to fetch page contents\n // mid-conversation — the single-pass completion sees everything it\n // needs up front. Per-page body is clamped to MAX_PAGE_BODY_BYTES\n // (16KB) to bound prompt size; pages exceeding the cap are truncated\n // with a \"[truncated]\" marker.\n const hitsWithBody = await Promise.all(\n hits.map(async (h) => {\n // SearchHit.path is the absolute path stored by toDoc() in\n // wiki/search.ts (`id: page.path`). Pass it through to readPage.\n let body = \"\";\n let truncated = false;\n try {\n const page = await this.opts.store.readPage(h.path);\n if (page) {\n if (page.body.length > MAX_PAGE_BODY_BYTES) {\n body = page.body.slice(0, MAX_PAGE_BODY_BYTES);\n truncated = true;\n } else {\n body = page.body;\n }\n } else {\n // readPage returned null — file deleted or unreadable. Fall\n // back to the snippet so the LLM still has some grounding.\n body = h.snippet;\n }\n } catch {\n body = h.snippet;\n }\n return { hit: h, body, truncated };\n }),\n );\n\n const systemPrompt = buildQuerySystemPrompt();\n const userPrompt = buildQueryUserPrompt(question, hitsWithBody);\n\n // Single-pass completion: the prompt now contains full page bodies,\n // so no in-call tool use is required. The wrapper dispatches API\n // mode → AnthropicProvider.completeWithUsage, CLI mode → subprocess.\n let answer = \"\";\n let costUsd = 0;\n try {\n const result = await runtimeAwareComplete(userPrompt, {\n systemPrompt,\n model,\n config: this.opts.config,\n runtimeMode,\n });\n answer = result.text;\n costUsd = result.costUsd + expansionCost;\n // Review item 34: empty / whitespace LLM response must NOT be\n // returned as a successful answer. Treat as skip with explicit\n // reason so the caller surfaces \"I don't know\" instead of \"\".\n if (answer.trim().length === 0) {\n this.opts.costTracker.logUsage({ operation: \"query\", model, costUsd });\n return {\n question,\n answer: \"\",\n sources: hits.map(mapSourceMeta),\n costUsd,\n durationMs: Date.now() - started,\n model,\n skipped: true,\n skipReason: \"LLM returned empty response\",\n };\n }\n } catch (err) {\n log.error({ err, question }, \"query agent failed\");\n return {\n question,\n answer: \"\",\n sources: hits.map(mapSourceMeta),\n costUsd: 0,\n durationMs: Date.now() - started,\n model,\n skipped: true,\n skipReason: `query error: ${errMsg(err)}`,\n };\n }\n\n this.opts.costTracker.logUsage({\n operation: \"query\",\n model,\n costUsd,\n });\n\n // Record a provenance event so queries are auditable alongside\n // ingestions. Queries don't write files — wiki_files_written is empty\n // and wiki_file_hashes_after stays empty.\n if (this.opts.provenance) {\n try {\n await this.opts.provenance.append({\n type: \"query\",\n source_files: hits.map((h) => h.path),\n source_hashes: hits.map((h) => sha256Hex(h.snippet ?? \"\")),\n prompt_hash: sha256Hex(`${systemPrompt}\\n\\n${userPrompt}`),\n model_id: model,\n response_hash: sha256Hex(answer),\n wiki_files_written: [],\n wiki_file_hashes_after: {},\n metadata: {\n question_hash: sha256Hex(question),\n cost_usd: Number(costUsd.toFixed(6)),\n hit_count: hits.length,\n duration_ms: Date.now() - started,\n },\n });\n } catch (err) {\n log.error({ err }, \"failed to append query provenance\");\n }\n }\n\n // Log query outcome for zero-hit monitoring.\n recordQueryOutcome(this.opts.config.health.query_log_file, question, hits.length);\n\n return {\n question,\n answer,\n sources: hits.map(mapSourceMeta),\n costUsd,\n durationMs: Date.now() - started,\n model,\n skipped: false,\n };\n }\n}\n\nfunction mapSourceMeta(hit: SearchHit): { path: string; title: string; score: number } {\n return { path: hit.path, title: hit.title, score: hit.score };\n}\n\nfunction buildQuerySystemPrompt(): string {\n return `You are the watcher-on-the-wall query agent.\nAnswer user questions using only the wiki pages provided as context.\nCite sources inline using [title](relative/path.md) markdown links.\nIf the wiki does not contain the answer, say so honestly — do not invent facts.\nKeep answers grounded, concise, and citation-heavy.`;\n}\n\ninterface HitWithBody {\n hit: SearchHit;\n body: string;\n truncated: boolean;\n}\n\nfunction buildQueryUserPrompt(question: string, hits: HitWithBody[]): string {\n const lines: string[] = [];\n lines.push(`# Question`);\n lines.push(\"\");\n lines.push(question);\n lines.push(\"\");\n lines.push(`# Retrieved wiki pages (${hits.length})`);\n lines.push(\"\");\n if (hits.length === 0) {\n lines.push(\"_No matching pages found._\");\n } else {\n for (const { hit: h, body, truncated } of hits) {\n lines.push(`## ${h.title} (score ${h.score.toFixed(3)})`);\n lines.push(`path: ${h.path}`);\n lines.push(\"\");\n lines.push(body);\n if (truncated) {\n lines.push(\"\");\n lines.push(\"_[page body truncated]_\");\n }\n lines.push(\"\");\n }\n }\n lines.push(\"---\");\n lines.push(\"Write an answer in markdown. Cite sources inline as `[title](path)`.\");\n return lines.join(\"\\n\");\n}\n","/**\n * Text-truncation utilities for context-efficient retrieval.\n *\n * The progressive-retrieval and narrow-query MCP tools (Feature Pass 005-007)\n * ship the smallest viable payload first and expand only on signal from the\n * client LLM. To do that without burning the caller's budget on a hard\n * mid-word cut, every payload boundary cuts on a *sentence boundary* (or, as\n * a fallback, a word boundary). Mid-token cuts are not allowed.\n *\n * The token budget is expressed in tokens, but truncation itself works in\n * characters — we convert at the boundary using a 4-chars-per-token heuristic\n * that matches the daemon's existing CLI-mode approximation (see\n * `src/ingestion/cli-invoker.ts:206`). Providers that need exact counts go\n * through `src/server/token-estimator.ts` and validate at the response edge.\n *\n * Design notes:\n * - We don't depend on a specific tokenizer here. Truncation produces\n * \"safe\" payloads bounded by char-equivalent budgets; the token-estimator\n * re-counts on the way out for precise reporting.\n * - Sentence-boundary search looks back up to LOOKBACK_RATIO of the budget\n * so the cut lands on a punctuation/whitespace, not mid-word.\n * - Markdown outline extraction strips YAML frontmatter and returns the\n * header skeleton so the client LLM can pick which section to expand.\n */\n\n/** Sentence-terminating punctuation. Excludes ellipses (handled separately). */\nconst SENTENCE_TERMINATORS = new Set([\".\", \"!\", \"?\"]);\n\n/**\n * Fraction of the budget the lookback window may scan for a sentence\n * boundary. Tuned to 30% so the algorithm can reach a terminator that\n * sits just before the hard char cap without dropping the entire last\n * sentence.\n */\nconst LOOKBACK_RATIO = 0.3;\n\n/** Characters per token under the 4-char heuristic. */\nexport const CHARS_PER_TOKEN = 4;\n\n/** Convert a token budget into a character budget. */\nexport function tokensToChars(tokens: number): number {\n return Math.max(0, Math.floor(tokens * CHARS_PER_TOKEN));\n}\n\n/** Convert a character count into an approximate token count. */\nexport function charsToTokens(chars: number): number {\n return Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Truncate `text` to fit within `maxTokens` (4-chars-per-token heuristic),\n * preferring a cut on a sentence boundary. Falls back to a word boundary if\n * no sentence boundary is found within the lookback window. Returns the\n * original text unchanged when it already fits.\n */\nexport function truncateToTokenBudget(text: string, maxTokens: number): string {\n if (maxTokens <= 0 || text.length === 0) return \"\";\n const maxChars = tokensToChars(maxTokens);\n if (text.length <= maxChars) return text;\n\n const slice = text.slice(0, maxChars);\n const lookBack = Math.max(0, Math.floor(maxChars * LOOKBACK_RATIO));\n const minIdx = Math.max(0, maxChars - lookBack);\n\n // First pass: sentence boundary (punct followed by whitespace or eof).\n for (let i = slice.length - 1; i >= minIdx; i--) {\n const c = slice[i];\n if (c === undefined) continue;\n if (SENTENCE_TERMINATORS.has(c)) {\n const next = text[i + 1];\n if (next === undefined || /\\s/.test(next)) {\n return slice.slice(0, i + 1);\n }\n }\n }\n\n // Second pass: word boundary (whitespace).\n for (let i = slice.length - 1; i >= minIdx; i--) {\n const c = slice[i];\n if (c !== undefined && /\\s/.test(c)) {\n return slice.slice(0, i).trimEnd();\n }\n }\n\n // No boundary found — hard cut at maxChars.\n return slice;\n}\n\n/** One header in a markdown outline. */\nexport interface OutlineEntry {\n level: 1 | 2 | 3 | 4 | 5 | 6;\n title: string;\n /** Slug derived from the header (lowercase, hyphenated). */\n slug: string;\n /** Character offset in the body where this section starts. */\n offset: number;\n}\n\n/**\n * Strip a YAML frontmatter block (`---\\n...\\n---\\n`) from the head of a\n * markdown document. Returns `{ frontmatter, body }`. If no frontmatter is\n * present, frontmatter is the empty string and body is the original input.\n */\nexport function splitFrontmatter(markdown: string): { frontmatter: string; body: string } {\n if (!markdown.startsWith(\"---\")) return { frontmatter: \"\", body: markdown };\n const rest = markdown.slice(3);\n // Match a closing --- at the start of a line.\n const closeIdx = rest.indexOf(\"\\n---\");\n if (closeIdx === -1) return { frontmatter: \"\", body: markdown };\n const afterClose = closeIdx + 4; // skip \"\\n---\"\n // Skip the rest of the closing line.\n let bodyStart = afterClose;\n while (bodyStart < rest.length && rest[bodyStart] !== \"\\n\") bodyStart++;\n if (bodyStart < rest.length && rest[bodyStart] === \"\\n\") bodyStart++;\n return {\n frontmatter: rest.slice(0, closeIdx),\n body: rest.slice(bodyStart),\n };\n}\n\n/** Render an outline entry as a single bullet line for inclusion in prompts. */\nexport function renderOutlineEntry(entry: OutlineEntry): string {\n const indent = \" \".repeat(Math.max(0, entry.level - 1));\n return `${indent}- ${entry.title}`;\n}\n\n/**\n * Extract the header outline from a markdown body. Skips fenced code blocks\n * (a \"## \" inside a ```...``` fence is not a header). Frontmatter is\n * dropped before scanning.\n */\nexport function extractOutline(markdown: string): OutlineEntry[] {\n const { body } = splitFrontmatter(markdown);\n const out: OutlineEntry[] = [];\n let inFence = false;\n let offset = 0;\n for (const line of body.split(\"\\n\")) {\n const lineLen = line.length + 1; // +1 for the newline we split on\n if (line.startsWith(\"```\")) {\n inFence = !inFence;\n offset += lineLen;\n continue;\n }\n if (!inFence) {\n const m = /^(#{1,6})\\s+(.+?)\\s*$/.exec(line);\n if (m && m[1] && m[2]) {\n const level = m[1].length as 1 | 2 | 3 | 4 | 5 | 6;\n const title = m[2].trim();\n out.push({ level, title, slug: slugify(title), offset });\n }\n }\n offset += lineLen;\n }\n return out;\n}\n\n/**\n * Slugify a header title (lowercase, hyphen-separated, alphanumeric only).\n * Used to generate stable section identifiers for later structured fetches.\n */\nexport function slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s-]+/gu, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** One section's lede paragraph alongside its header info. */\nexport interface SectionLede {\n header: OutlineEntry | null;\n /** First non-empty paragraph after the header. Trimmed. */\n lede: string;\n}\n\n/**\n * Extract the outline plus the first paragraph after each header. Used by\n * tier-2 progressive expansion: \"show me each section, just the lede.\" Pages\n * with no headers fall back to a single SectionLede whose header is null and\n * whose lede is the page's first paragraph.\n */\nexport function extractSectionLedes(markdown: string): SectionLede[] {\n const { body } = splitFrontmatter(markdown);\n const headers = extractOutline(markdown);\n if (headers.length === 0) {\n const lede = firstParagraph(body);\n return lede ? [{ header: null, lede }] : [];\n }\n const sections: SectionLede[] = [];\n for (let i = 0; i < headers.length; i++) {\n const h = headers[i]!;\n const nextOffset = i + 1 < headers.length ? headers[i + 1]!.offset : body.length;\n // Find where the header line ends (its newline) — sections start after that.\n const headerLineEnd = body.indexOf(\"\\n\", h.offset);\n const sectionStart = headerLineEnd === -1 ? h.offset : headerLineEnd + 1;\n const sectionBody = body.slice(sectionStart, nextOffset);\n const lede = firstParagraph(sectionBody);\n if (lede) sections.push({ header: h, lede });\n }\n return sections;\n}\n\n/**\n * Find the first non-empty paragraph in `text`. Paragraphs are separated by\n * blank lines. Returns the empty string if no paragraph is found.\n */\nexport function firstParagraph(text: string): string {\n const lines = text.split(\"\\n\");\n const current: string[] = [];\n for (const line of lines) {\n if (line.trim() === \"\") {\n if (current.length > 0) return current.join(\"\\n\").trim();\n continue;\n }\n current.push(line);\n }\n return current.join(\"\\n\").trim();\n}\n\n/**\n * Split a block of text into sentences. Conservative regex — matches on\n * `[.!?]` followed by whitespace or end-of-text. Doesn't try to be clever\n * about abbreviations (Mr., e.g.) because the consumers below tolerate\n * over-splitting. Trims results.\n */\nexport function sentenceSplit(text: string): string[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n // Match runs ending in . ! or ? followed by whitespace or end.\n const matches = trimmed.match(/[^.!?]+[.!?]+(?=\\s|$)|[^.!?]+$/g);\n if (!matches) return [trimmed];\n return matches.map((m) => m.trim()).filter((s) => s.length > 0);\n}\n\n/**\n * Return sentences from `text` that contain *all* of the supplied anchor\n * substrings (case-insensitive), up to `maxTokens` worth of total text.\n * Stops at the first sentence that would exceed the budget.\n */\nexport function extractSentencesContainingAll(\n text: string,\n anchors: string[],\n maxTokens: number,\n): string[] {\n if (anchors.length === 0) return [];\n const lowered = anchors.map((a) => a.toLowerCase());\n const budget = tokensToChars(maxTokens);\n const out: string[] = [];\n let used = 0;\n for (const sentence of sentenceSplit(text)) {\n const lc = sentence.toLowerCase();\n if (lowered.every((a) => lc.includes(a))) {\n if (used + sentence.length > budget) break;\n out.push(sentence);\n used += sentence.length + 1; // +1 for the space we'd join on\n }\n }\n return out;\n}\n","/**\n * Token estimator. Returns token counts for a given text + provider so the\n * `estimate_query_cost` MCP tool (Feature Pass 006) can report retrieval\n * payload size to the client LLM *before* the client commits to a query.\n *\n * Default method is a 4-chars-per-token heuristic that matches the daemon's\n * existing CLI-mode approximation (`src/ingestion/cli-invoker.ts:206`). This\n * is fast, deterministic, and good to ~10-15% on English prose. Power users\n * who need exact counts pass `precise: true` to invoke the provider-native\n * tokenizer:\n *\n * - Anthropic: `client.messages.countTokens()` (free, network call).\n * - Gemini: `model.countTokens()` (free, network call).\n * - OpenAI: tiktoken would be required (not installed). Falls back.\n * - Ollama: no client-side tokenizer. Falls back.\n *\n * BYOK invariants (Pass 008): the daemon reads the API key from its\n * environment at provider-construction time; we do not log the key, persist\n * it, or echo it in error messages. Network failures fall back to the\n * heuristic so a transient DNS hiccup doesn't break `estimate_query_cost`.\n *\n * Confidence + method are surfaced in the response so the client LLM can\n * reason about whether to trust the estimate (e.g., a 1000-token approximate\n * estimate is fine for a \"do I have budget?\" check; an exact count matters\n * if the client is right at its context-window limit).\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { GoogleGenerativeAI } from \"@google/generative-ai\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { LlmProviderName } from \"../utils/types.js\";\nimport { CHARS_PER_TOKEN, charsToTokens } from \"./truncate.js\";\n\nexport type TokenEstimateConfidence = \"exact\" | \"approximate\";\n\nexport type TokenEstimateMethod =\n | \"anthropic-count-tokens\"\n | \"gemini-count-tokens\"\n | \"openai-tiktoken\"\n | \"ollama-llama-cpp\"\n | \"4-char-heuristic\"\n | \"fallback-heuristic\";\n\nexport interface TokenEstimate {\n /** Token count for the supplied text. */\n tokens: number;\n /** Whether the count is from a provider-native tokenizer or a heuristic. */\n confidence: TokenEstimateConfidence;\n /** Which tokenizer or heuristic produced the count. */\n method: TokenEstimateMethod;\n /** Provider the estimate is sized for. */\n provider: LlmProviderName | null;\n /** Model identifier the estimate is sized for. */\n model: string | null;\n}\n\nexport interface EstimateTokensOptions {\n /** Provider the estimate should target. Defaults to no specific provider. */\n provider?: LlmProviderName;\n /** Model identifier. Required for Anthropic/Gemini precise counts. */\n model?: string;\n /** When true, attempt the provider-native tokenizer. */\n precise?: boolean;\n /**\n * Optional Anthropic SDK client. When omitted, a fresh client is\n * constructed using ANTHROPIC_API_KEY (or the env var named in\n * `apiKeyEnv`). Exposed for tests.\n */\n anthropicClient?: Anthropic;\n /**\n * Optional Gemini SDK client. When omitted, a fresh client is constructed\n * using GOOGLE_API_KEY (or the env var named in `apiKeyEnv`).\n */\n geminiClient?: GoogleGenerativeAI;\n /** Env var to read the provider API key from. */\n apiKeyEnv?: string;\n}\n\n/**\n * 4-chars-per-token heuristic. Deterministic, network-free, and good to\n * ~10-15% on English prose. The result is rounded up so the estimate is\n * never silently under the true count.\n */\nexport function heuristicTokens(text: string): number {\n return charsToTokens(text.length);\n}\n\n/**\n * Estimate the token count for `text` under the supplied provider/model.\n * Falls back to the 4-char heuristic on any provider-native failure.\n */\nexport async function estimateTokens(\n text: string,\n opts: EstimateTokensOptions = {},\n): Promise<TokenEstimate> {\n const provider = opts.provider ?? null;\n const model = opts.model ?? null;\n const heuristic: TokenEstimate = {\n tokens: heuristicTokens(text),\n confidence: \"approximate\",\n method: \"4-char-heuristic\",\n provider,\n model,\n };\n\n if (!opts.precise) return heuristic;\n if (!provider) return heuristic;\n\n if (provider === \"anthropic\" && model) {\n return (await anthropicCountTokens(text, model, opts)) ?? withFallback(heuristic);\n }\n if (provider === \"gemini\" && model) {\n return (await geminiCountTokens(text, model, opts)) ?? withFallback(heuristic);\n }\n // OpenAI tiktoken + Ollama llama.cpp are not bundled — the daemon\n // deliberately keeps its dependency surface narrow. Operators who want\n // precise OpenAI counts install tiktoken separately; until then we\n // surface the heuristic with an explicit \"approximate\" tag so the\n // caller knows not to treat it as authoritative.\n return heuristic;\n}\n\nfunction withFallback(heuristic: TokenEstimate): TokenEstimate {\n return { ...heuristic, method: \"fallback-heuristic\" };\n}\n\nasync function anthropicCountTokens(\n text: string,\n model: string,\n opts: EstimateTokensOptions,\n): Promise<TokenEstimate | null> {\n const log = getLogger(\"token-estimator\");\n try {\n const client = opts.anthropicClient ?? buildAnthropicClient(opts);\n if (!client) return null;\n const result = await client.messages.countTokens({\n model,\n messages: [{ role: \"user\", content: text }],\n });\n return {\n tokens: result.input_tokens,\n confidence: \"exact\",\n method: \"anthropic-count-tokens\",\n provider: \"anthropic\",\n model,\n };\n } catch (err) {\n // Network / 401 / unknown model — fall back. Don't surface raw error\n // strings: SDK errors may contain hostname / path / token fragments.\n log.warn(\n { err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\" },\n \"anthropic count_tokens failed; falling back to heuristic\",\n );\n return null;\n }\n}\n\nasync function geminiCountTokens(\n text: string,\n model: string,\n opts: EstimateTokensOptions,\n): Promise<TokenEstimate | null> {\n const log = getLogger(\"token-estimator\");\n try {\n const client = opts.geminiClient ?? buildGeminiClient(opts);\n if (!client) return null;\n const generativeModel = client.getGenerativeModel({ model });\n const result = await generativeModel.countTokens({\n contents: [{ role: \"user\", parts: [{ text }] }],\n });\n return {\n tokens: result.totalTokens,\n confidence: \"exact\",\n method: \"gemini-count-tokens\",\n provider: \"gemini\",\n model,\n };\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message.slice(0, 120) : \"unknown\" },\n \"gemini countTokens failed; falling back to heuristic\",\n );\n return null;\n }\n}\n\nfunction buildAnthropicClient(opts: EstimateTokensOptions): Anthropic | null {\n const envName = opts.apiKeyEnv ?? \"ANTHROPIC_API_KEY\";\n const apiKey = process.env[envName];\n if (!apiKey) return null;\n return new Anthropic({ apiKey });\n}\n\nfunction buildGeminiClient(opts: EstimateTokensOptions): GoogleGenerativeAI | null {\n const envName = opts.apiKeyEnv ?? \"GOOGLE_API_KEY\";\n const apiKey = process.env[envName];\n if (!apiKey) return null;\n return new GoogleGenerativeAI(apiKey);\n}\n\n/**\n * Convenience: return heuristic estimates for all four providers when no\n * specific provider is requested. The caller (estimate_query_cost) renders\n * this as a four-row table so the client LLM can pick the cheapest one.\n */\nexport function heuristicEstimatesAllProviders(text: string): TokenEstimate[] {\n const providers: LlmProviderName[] = [\"anthropic\", \"openai\", \"gemini\", \"ollama\"];\n return providers.map((provider) => ({\n tokens: heuristicTokens(text),\n confidence: \"approximate\" as const,\n method: \"4-char-heuristic\" as const,\n provider,\n model: null,\n }));\n}\n\n/** Re-export the heuristic boundary so consumers don't have to import twice. */\nexport { CHARS_PER_TOKEN };\n","/**\n * Progressive retrieval. The client LLM asks for the smallest viable\n * answer first; on signal, it expands tier by tier until it has enough\n * context. Each tier ships *new* content only — the client stitches the\n * conversation together. There is no daemon-side LLM call: this is pure\n * structural retrieval over the BM25 hit list, which is what makes it\n * dramatically cheaper than the synthesis-based `query` tool.\n *\n * Tiers:\n * 0. lede – top hit's first paragraph (~150-300 tok)\n * 1. snippets – next-2 hits' first paragraphs + outline of top-1\n * 2. section-ledes – next-2 hits' bodies + per-section ledes of top-3\n * 3. full-bodies – next-3 hits, full bodies of top-5\n *\n * Cumulative hit counts: tier 0 = 1, tier 1 = 3, tier 2 = 5, tier 3 = 8.\n * Each call returns the *delta* — never re-ships the previous tier.\n *\n * Token accounting uses the 4-char heuristic (`heuristicTokens`). A\n * separate `estimate_query_cost` MCP tool gives the client an exact-ish\n * estimate before they commit. The progressive flow itself enforces\n * `max_tokens_total` as a hard cap so a chatty client can't blow past\n * its budget by repeatedly expanding.\n *\n * BM25-only commitment: the search path here is the same `WikiSearch`\n * minisearch instance the rest of the daemon uses. No vector code paths,\n * no embedding fallback. If retrieval quality is the bottleneck, the\n * remedy is upstream (key_terms enrichment, query expansion) — not\n * vectors.\n */\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport type { CachedHit, ProgressiveCache, ProgressiveEntry } from \"./progressive-cache.js\";\nimport { heuristicTokens } from \"./token-estimator.js\";\nimport {\n extractOutline,\n extractSectionLedes,\n firstParagraph,\n renderOutlineEntry,\n splitFrontmatter,\n truncateToTokenBudget,\n} from \"./truncate.js\";\n\n/** 16 KB matches the existing query-engine page-body clamp. */\nconst MAX_PAGE_BODY_BYTES = 16 * 1024;\n\n/** Cumulative hit counts per tier (tier 0 reveals 1, tier 1 → 3, etc). */\nconst HITS_PER_TIER: Record<number, number> = { 0: 1, 1: 3, 2: 5, 3: 8 };\n\n/** Stable string labels surfaced in the response metadata. */\nexport const TIER_LABELS: Readonly<Record<number, string>> = {\n 0: \"lede\",\n 1: \"snippets\",\n 2: \"section-ledes\",\n 3: \"full-bodies\",\n};\n\n/** Highest tier index. Tier > MAX_TIER returns has_more: false. */\nexport const MAX_TIER = 3;\n\nexport interface ProgressiveTierResponse {\n /** Tier number (0-3) of the content delivered in this response. */\n tier: number;\n /** Human-readable label of the tier (lede / snippets / section-ledes / full-bodies). */\n tier_label: string;\n /**\n * Markdown content the client LLM consumes. Empty when has_more is\n * false and no further expansion is available.\n */\n content: string;\n /** Number of hits this tier added on top of the previous tier. */\n hit_count_delta: number;\n /** Total hits referenced across all tiers shipped so far. */\n hit_count_total: number;\n /** Approximate token count of `content` (heuristic). */\n tokens_delivered: number;\n /** Cumulative tokens shipped across all tiers in this conversation. */\n tokens_shipped_total: number;\n /** True if more tiers are available within max_tokens_total. */\n has_more: boolean;\n /** UUID to pass to query_expand. Null when has_more is false. */\n continuation_token: string | null;\n /** Label of the next tier the client could request. */\n next_tier_label: string | null;\n /** Heuristic estimate of next-tier token cost. */\n next_tier_estimate_tokens: number | null;\n}\n\nexport interface ProgressiveQueryOptions {\n /** Source-of-truth wiki store for reading pages by absolute path. */\n store: WikiStore;\n /** BM25 search index. */\n search: WikiSearch;\n /** Continuation cache. */\n cache: ProgressiveCache;\n /** Initial token budget (default 512). */\n maxTokensInitial?: number;\n /** Hard cap on total tokens across all expand calls (default 8192). */\n maxTokensTotal?: number;\n}\n\n/**\n * Run BM25, pre-fetch the top-N page bodies, render tier 0, and persist a\n * continuation for `query_expand`. Returns the tier-0 response or — when\n * the corpus is empty / no hits found — a response with no content and\n * has_more: false.\n */\nexport async function queryProgressive(\n question: string,\n opts: ProgressiveQueryOptions,\n): Promise<ProgressiveTierResponse> {\n const maxInitial = clampPositive(opts.maxTokensInitial, 512);\n const maxTotal = clampPositive(opts.maxTokensTotal, 8192);\n const initialBudget = Math.min(maxInitial, maxTotal);\n\n if (opts.search.size() === 0 && opts.store.count() > 0) {\n return emptyResponse(0, \"search index is empty — rebuild required\");\n }\n\n const hits = opts.search.search(question, HITS_PER_TIER[MAX_TIER]);\n if (hits.length === 0) {\n return emptyResponse(0, \"no matching pages\");\n }\n\n const cachedHits = await prefetchHits(opts.store, hits);\n\n const content = renderTier(0, cachedHits, [], initialBudget);\n const tokens = heuristicTokens(content);\n\n const hitCountTotal = HITS_PER_TIER[0]!;\n const hasMoreTiers = MAX_TIER > 0;\n const tokensRemaining = maxTotal - tokens;\n const hasMore = hasMoreTiers && tokensRemaining > 0 && cachedHits.length > hitCountTotal;\n\n const continuation = opts.cache.put({\n question,\n hits: cachedHits,\n lastTierServed: 0,\n tokensShippedSoFar: tokens,\n maxTokensTotal: maxTotal,\n });\n\n return {\n tier: 0,\n tier_label: TIER_LABELS[0]!,\n content,\n hit_count_delta: HITS_PER_TIER[0]!,\n hit_count_total: hitCountTotal,\n tokens_delivered: tokens,\n tokens_shipped_total: tokens,\n has_more: hasMore,\n continuation_token: hasMore ? continuation : null,\n next_tier_label: hasMore ? TIER_LABELS[1]! : null,\n next_tier_estimate_tokens: hasMore ? estimateNextTierTokens(1, cachedHits) : null,\n };\n}\n\nexport interface QueryExpandOptions {\n cache: ProgressiveCache;\n additionalTokens?: number;\n}\n\n/**\n * Advance one tier on a previously-issued continuation. Returns the delta\n * content (i.e., only the new hits the next tier reveals). Returns\n * `{ error }` if the continuation is unknown or the total budget is\n * exhausted.\n */\nexport async function queryExpand(\n continuationToken: string,\n opts: QueryExpandOptions,\n): Promise<ProgressiveTierResponse | { error: string }> {\n const entry = opts.cache.get(continuationToken);\n if (!entry) {\n return { error: \"continuation_token expired or invalid\" };\n }\n\n if (entry.lastTierServed >= MAX_TIER) {\n return { error: \"no further tiers available\" };\n }\n\n const additional = clampPositive(opts.additionalTokens, 1024);\n const totalRemaining = entry.maxTokensTotal - entry.tokensShippedSoFar;\n const budget = Math.min(additional, Math.max(0, totalRemaining));\n if (budget <= 0) {\n return { error: \"max_tokens_total budget exhausted\" };\n }\n\n const nextTier = entry.lastTierServed + 1;\n const previousHitCount = HITS_PER_TIER[entry.lastTierServed] ?? 0;\n const newHits = entry.hits.slice(previousHitCount, HITS_PER_TIER[nextTier]);\n\n const content = renderTier(nextTier, entry.hits, newHits, budget);\n const tokens = heuristicTokens(content);\n\n // Capture the previous-shipped count *before* the mutator runs — the\n // cache update mutates `entry` in place (same reference), so reading\n // tokensShippedSoFar after the update double-counts the delta.\n const previousShipped = entry.tokensShippedSoFar;\n opts.cache.update(continuationToken, (e) => {\n e.lastTierServed = nextTier;\n e.tokensShippedSoFar += tokens;\n });\n\n const tokensTotal = previousShipped + tokens;\n const tokensLeftover = entry.maxTokensTotal - tokensTotal;\n const hasMore = nextTier < MAX_TIER && tokensLeftover > 0;\n\n return {\n tier: nextTier,\n tier_label: TIER_LABELS[nextTier]!,\n content,\n hit_count_delta: newHits.length,\n hit_count_total: HITS_PER_TIER[nextTier]!,\n tokens_delivered: tokens,\n tokens_shipped_total: tokensTotal,\n has_more: hasMore,\n continuation_token: hasMore ? continuationToken : null,\n next_tier_label: hasMore ? TIER_LABELS[nextTier + 1]! : null,\n next_tier_estimate_tokens: hasMore ? estimateNextTierTokens(nextTier + 1, entry.hits) : null,\n };\n}\n\n/** Pre-fetch the page bodies for a batch of hits, in parallel. */\nasync function prefetchHits(\n store: WikiStore,\n hits: ReturnType<WikiSearch[\"search\"]>,\n): Promise<CachedHit[]> {\n return Promise.all(\n hits.map(async (hit) => {\n let body = \"\";\n let truncated = false;\n try {\n const page = await store.readPage(hit.path);\n if (page) {\n if (page.body.length > MAX_PAGE_BODY_BYTES) {\n body = page.body.slice(0, MAX_PAGE_BODY_BYTES);\n truncated = true;\n } else {\n body = page.body;\n }\n } else {\n body = hit.snippet;\n }\n } catch {\n body = hit.snippet;\n }\n return {\n hit,\n body,\n relativePath: store.relativePath(hit.path),\n truncated,\n };\n }),\n );\n}\n\nfunction renderTier(\n tier: number,\n allHits: CachedHit[],\n deltaHits: CachedHit[],\n budget: number,\n): string {\n if (budget <= 0) return \"\";\n switch (tier) {\n case 0:\n return renderTier0(allHits[0], budget);\n case 1:\n return renderTier1(allHits[0], allHits.slice(1, HITS_PER_TIER[1]), budget);\n case 2:\n return renderTier2(allHits.slice(0, HITS_PER_TIER[1]), deltaHits, budget);\n case 3:\n return renderTier3(allHits.slice(0, HITS_PER_TIER[2]), deltaHits, budget);\n default:\n return \"\";\n }\n}\n\n/** Tier 0: top hit's lede paragraph. Cheapest possible payload. */\nfunction renderTier0(top: CachedHit | undefined, budget: number): string {\n if (!top) return \"\";\n const header = `## ${top.hit.title}\\n_path: ${top.relativePath} · score: ${top.hit.score.toFixed(3)}_\\n\\n`;\n const headerTokens = heuristicTokens(header);\n const remaining = budget - headerTokens;\n if (remaining <= 0) return truncateToTokenBudget(header, budget);\n const lede = firstParagraph(splitFrontmatter(top.body).body) || top.hit.snippet;\n return `${header}${truncateToTokenBudget(lede, remaining)}`.trim();\n}\n\n/**\n * Tier 1: top hit's outline + next-2 hits' ledes. Gives the client LLM a\n * map of what's available without committing to full sections.\n */\nfunction renderTier1(top: CachedHit | undefined, next: CachedHit[], budget: number): string {\n const sections: string[] = [];\n let used = 0;\n\n if (top) {\n const outline = extractOutline(top.body);\n if (outline.length > 0) {\n const lines = [`## Outline: ${top.hit.title}`, `_path: ${top.relativePath}_`, \"\"];\n for (const entry of outline) lines.push(renderOutlineEntry(entry));\n const text = `${lines.join(\"\\n\")}\\n`;\n const tokens = heuristicTokens(text);\n if (used + tokens <= budget) {\n sections.push(text);\n used += tokens;\n }\n }\n }\n\n // Per-hit slice MUST NOT enforce a minimum that exceeds the remaining\n // budget — that would let the cumulative render overshoot the cap.\n const perHit = next.length > 0 ? Math.max(0, Math.floor((budget - used) / next.length)) : 0;\n for (const h of next) {\n if (used >= budget) break;\n const remaining = budget - used;\n const block = renderHitLede(h, Math.min(perHit, remaining));\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n sections.push(block);\n used += tokens;\n }\n return sections.join(\"\\n\").trim();\n}\n\n/**\n * Tier 2: per-section ledes for top-3 hits + next-2 hits' ledes. The\n * client LLM now sees the structure of the most relevant pages without\n * paying for the prose between section headers.\n */\nfunction renderTier2(top3: CachedHit[], deltaHits: CachedHit[], budget: number): string {\n const sections: string[] = [];\n let used = 0;\n\n const perTop = top3.length > 0 ? Math.max(0, Math.floor((budget * 0.6) / top3.length)) : 0;\n for (const h of top3) {\n if (used >= budget) break;\n const remaining = budget - used;\n const block = renderHitSectionLedes(h, Math.min(perTop, remaining));\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n sections.push(block);\n used += tokens;\n }\n\n const perDelta =\n deltaHits.length > 0 ? Math.max(0, Math.floor((budget - used) / deltaHits.length)) : 0;\n for (const h of deltaHits) {\n if (used >= budget) break;\n const remaining = budget - used;\n const block = renderHitLede(h, Math.min(perDelta, remaining));\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n sections.push(block);\n used += tokens;\n }\n return sections.join(\"\\n\").trim();\n}\n\n/**\n * Tier 3: full bodies (already clamped to MAX_PAGE_BODY_BYTES at pre-fetch).\n * This is the only tier whose payload matches the legacy `query` tool's\n * fanout. Used when the client LLM has decided it needs to see the\n * complete material.\n */\nfunction renderTier3(top5: CachedHit[], deltaHits: CachedHit[], budget: number): string {\n const sections: string[] = [];\n let used = 0;\n\n const all = [...deltaHits];\n // Tier 3 is the deepest tier; if top5 hasn't been served at full body yet\n // (i.e., we're being called fresh from tier 2 → 3 and the client wants\n // *all* the bodies), include them. The cache state guarantees we don't\n // re-ship the same hits, so deltaHits is the typical population here.\n if (top5.length > 0 && deltaHits.length === 0) {\n // Defensive: should not happen given our tier indices.\n all.push(...top5);\n }\n\n const perHit = all.length > 0 ? Math.max(0, Math.floor(budget / all.length)) : 0;\n for (const h of all) {\n if (used >= budget) break;\n const remaining = budget - used;\n const block = renderHitFullBody(h, Math.min(perHit, remaining));\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n sections.push(block);\n used += tokens;\n }\n return sections.join(\"\\n\").trim();\n}\n\nfunction renderHitLede(hit: CachedHit, budget: number): string {\n if (budget <= 0) return \"\";\n const header = `## ${hit.hit.title}\\n_path: ${hit.relativePath} · score: ${hit.hit.score.toFixed(3)}_\\n\\n`;\n const headerTokens = heuristicTokens(header);\n const remaining = Math.max(0, budget - headerTokens);\n const lede = firstParagraph(splitFrontmatter(hit.body).body) || hit.hit.snippet;\n return `${header}${truncateToTokenBudget(lede, remaining)}`.trimEnd();\n}\n\nfunction renderHitSectionLedes(hit: CachedHit, budget: number): string {\n if (budget <= 0) return \"\";\n const header = `## ${hit.hit.title}\\n_path: ${hit.relativePath} · score: ${hit.hit.score.toFixed(3)}_\\n\\n`;\n const headerTokens = heuristicTokens(header);\n let remaining = Math.max(0, budget - headerTokens);\n\n const ledes = extractSectionLedes(hit.body);\n if (ledes.length === 0) {\n const lede = firstParagraph(splitFrontmatter(hit.body).body) || hit.hit.snippet;\n return `${header}${truncateToTokenBudget(lede, remaining)}`.trimEnd();\n }\n\n const out: string[] = [header];\n for (const section of ledes) {\n if (remaining <= 0) break;\n const title = section.header ? `### ${section.header.title}` : \"\";\n const block = section.header ? `${title}\\n${section.lede}\\n` : `${section.lede}\\n`;\n const tokens = heuristicTokens(block);\n if (tokens <= remaining) {\n out.push(block);\n remaining -= tokens;\n } else {\n // Squeeze a truncated lede in if there is meaningful budget left.\n if (remaining > heuristicTokens(title) + 8) {\n const truncated = truncateToTokenBudget(\n section.lede,\n remaining - heuristicTokens(title) - 2,\n );\n out.push(`${title}\\n${truncated}\\n`);\n remaining = 0;\n }\n break;\n }\n }\n return out.join(\"\").trimEnd();\n}\n\nfunction renderHitFullBody(hit: CachedHit, budget: number): string {\n if (budget <= 0) return \"\";\n const header = `## ${hit.hit.title}\\n_path: ${hit.relativePath} · score: ${hit.hit.score.toFixed(3)}${\n hit.truncated ? \" · [pre-fetch truncated]\" : \"\"\n }_\\n\\n`;\n const headerTokens = heuristicTokens(header);\n const remaining = Math.max(0, budget - headerTokens);\n const body = splitFrontmatter(hit.body).body || hit.body;\n return `${header}${truncateToTokenBudget(body, remaining)}`.trimEnd();\n}\n\nfunction estimateNextTierTokens(nextTier: number, hits: CachedHit[]): number {\n // Rough envelope: per-tier average payload size, capped by the\n // available material. This is a hint for the client LLM, not a hard\n // guarantee; the actual delivered count is returned in tokens_delivered.\n switch (nextTier) {\n case 1: {\n const sample = hits.slice(0, HITS_PER_TIER[1]);\n const approx = sample.reduce(\n (acc, h) => acc + heuristicTokens(firstParagraph(splitFrontmatter(h.body).body) || \"\"),\n 0,\n );\n return Math.min(approx, 1200);\n }\n case 2: {\n const sample = hits.slice(0, HITS_PER_TIER[2]);\n const approx = sample.reduce((acc, h) => {\n const ledes = extractSectionLedes(h.body);\n return acc + ledes.reduce((sum, s) => sum + heuristicTokens(s.lede), 0);\n }, 0);\n return Math.min(approx, 3000);\n }\n case 3: {\n const sample = hits.slice(0, HITS_PER_TIER[3]);\n const approx = sample.reduce(\n (acc, h) => acc + heuristicTokens(splitFrontmatter(h.body).body || h.body),\n 0,\n );\n return Math.min(approx, 8000);\n }\n default:\n return 0;\n }\n}\n\nfunction emptyResponse(tier: number, reason: string): ProgressiveTierResponse {\n return {\n tier,\n tier_label: TIER_LABELS[tier]!,\n content: `_${reason}_`,\n hit_count_delta: 0,\n hit_count_total: 0,\n tokens_delivered: heuristicTokens(reason) + 2,\n tokens_shipped_total: heuristicTokens(reason) + 2,\n has_more: false,\n continuation_token: null,\n next_tier_label: null,\n next_tier_estimate_tokens: null,\n };\n}\n\nfunction clampPositive(value: number | undefined, fallback: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\n/**\n * Exposed for tests + benchmark. Reuses the same retrieval/render path\n * `queryProgressive` does, then expands all the way to `maxTier` without\n * touching the cache. Returns the cumulative rendered content + token\n * count so a benchmark can compare against the legacy `query` tool's full\n * payload.\n */\nexport async function renderProgressiveCumulative(\n question: string,\n opts: {\n store: WikiStore;\n search: WikiSearch;\n maxTier: number;\n perTierBudget?: number;\n },\n): Promise<{ content: string; tokens: number; hits: number }> {\n const hits = opts.search.search(question, HITS_PER_TIER[MAX_TIER]);\n if (hits.length === 0) return { content: \"\", tokens: 0, hits: 0 };\n const cachedHits = await prefetchHits(opts.store, hits);\n\n const target = Math.min(opts.maxTier, MAX_TIER);\n const tierBudget = opts.perTierBudget ?? 1024;\n let combined = \"\";\n for (let tier = 0; tier <= target; tier++) {\n const previousHitCount = tier === 0 ? 0 : HITS_PER_TIER[tier - 1]!;\n const deltaHits = cachedHits.slice(previousHitCount, HITS_PER_TIER[tier]);\n const piece = renderTier(tier, cachedHits, deltaHits, tierBudget);\n combined += (combined ? \"\\n\\n\" : \"\") + piece;\n }\n return {\n content: combined,\n tokens: heuristicTokens(combined),\n hits: Math.min(HITS_PER_TIER[target] ?? 0, cachedHits.length),\n };\n}\n\n/** Re-export for downstream consumers (tools.ts, tests). */\nexport type { ProgressiveEntry, CachedHit };\n","/**\n * `estimate_query_cost` MCP tool implementation.\n *\n * The client LLM hits this before committing to `query` or\n * `query_progressive` so it can budget. We compute the retrieval payload\n * the daemon *would* assemble (top-k pages, truncated to 16 KB each, same\n * as `query-engine.ts`), then count tokens against the chosen provider\n * tokenizer.\n *\n * Important: this is the token count for the daemon's *retrieval payload*,\n * not the full LLM round trip. The completion the client's LLM generates\n * on top of this payload is its own line item; the estimate tells the\n * client how big the retrieval grounding will be, full stop.\n *\n * Provider parameter:\n * - omitted → returns heuristic estimates for all four providers (4-char\n * approximation, marked confidence: \"approximate\")\n * - \"anthropic\" / \"gemini\" / \"openai\" / \"ollama\" → returns one estimate\n * for that provider; when precise: true *and* the daemon has the\n * matching BYOK key, the provider-native tokenizer is invoked.\n *\n * Defaults:\n * - precise: false (heuristic; network-free, deterministic)\n * - k: 8 (matches query-engine DEFAULT_K)\n */\nimport type { LlmProviderName, WotwConfig } from \"../utils/types.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport {\n estimateTokens,\n heuristicEstimatesAllProviders,\n type TokenEstimate,\n} from \"./token-estimator.js\";\n\nconst DEFAULT_K = 8;\nconst MAX_PAGE_BODY_BYTES = 16 * 1024;\n\nexport interface EstimateQueryCostOptions {\n store: WikiStore;\n search: WikiSearch;\n config: WotwConfig;\n /** Optional provider override. Falls back to env var, then \"all\". */\n provider?: LlmProviderName;\n /** Optional model identifier. Used by Anthropic/Gemini exact paths. */\n model?: string;\n /** Whether to attempt the provider-native tokenizer. */\n precise?: boolean;\n /** Top-k hits to include in the simulated payload. Default 8. */\n k?: number;\n}\n\nexport interface EstimateQueryCostResult {\n /** Original question, echoed for client-side debugging. */\n question: string;\n /**\n * Per-provider token estimates. Length 1 when a specific provider was\n * requested; length 4 when no provider was named (one row per provider).\n */\n estimates: TokenEstimate[];\n /** Number of BM25 hits that contributed to the payload. */\n hit_count: number;\n /** Cap the daemon would have applied to each page body, in bytes. */\n per_page_byte_cap: number;\n /** Total characters in the assembled payload string. */\n retrieval_payload_chars: number;\n /** True when the BM25 corpus turned up zero matches. */\n no_hits: boolean;\n}\n\n/**\n * Build the same retrieval payload the daemon would hand to `query`'s\n * single-pass synthesis call, then run the token estimator over it. We\n * pre-fetch page bodies in parallel and clamp each to 16 KB to match\n * `query-engine.ts:27` exactly — divergence here would make the estimate\n * lie.\n */\nexport async function estimateQueryCost(\n question: string,\n opts: EstimateQueryCostOptions,\n): Promise<EstimateQueryCostResult> {\n const k = opts.k && opts.k > 0 ? Math.floor(opts.k) : DEFAULT_K;\n const hits = opts.search.search(question, k);\n if (hits.length === 0) {\n return {\n question,\n estimates:\n opts.provider !== undefined\n ? [zeroEstimate(opts.provider, opts.model ?? null)]\n : heuristicEstimatesAllProviders(\"\"),\n hit_count: 0,\n per_page_byte_cap: MAX_PAGE_BODY_BYTES,\n retrieval_payload_chars: 0,\n no_hits: true,\n };\n }\n\n // Pre-fetch + clamp identical to query-engine.\n const bodies = await Promise.all(\n hits.map(async (hit) => {\n try {\n const page = await opts.store.readPage(hit.path);\n if (!page) return hit.snippet;\n if (page.body.length > MAX_PAGE_BODY_BYTES) {\n return page.body.slice(0, MAX_PAGE_BODY_BYTES);\n }\n return page.body;\n } catch {\n return hit.snippet;\n }\n }),\n );\n\n // Render the same skeleton the query engine renders (title + path + body\n // + separator) so the token estimate reflects the real payload, not the\n // page bodies in isolation.\n const lines: string[] = [];\n lines.push(\"# Question\");\n lines.push(\"\");\n lines.push(question);\n lines.push(\"\");\n lines.push(`# Retrieved wiki pages (${hits.length})`);\n lines.push(\"\");\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i]!;\n const body = bodies[i] ?? \"\";\n lines.push(`## ${h.title} (score ${h.score.toFixed(3)})`);\n lines.push(`path: ${opts.store.relativePath(h.path)}`);\n lines.push(\"\");\n lines.push(body);\n lines.push(\"\");\n }\n const payload = lines.join(\"\\n\");\n\n const estimates = await produceEstimates(payload, opts);\n\n return {\n question,\n estimates,\n hit_count: hits.length,\n per_page_byte_cap: MAX_PAGE_BODY_BYTES,\n retrieval_payload_chars: payload.length,\n no_hits: false,\n };\n}\n\n/**\n * Produce one or more {@link TokenEstimate} rows, depending on whether the\n * caller named a specific provider. The default (no provider) renders the\n * heuristic across all four providers; that's the \"I want to compare\" path.\n */\nasync function produceEstimates(\n payload: string,\n opts: EstimateQueryCostOptions,\n): Promise<TokenEstimate[]> {\n const requested = opts.provider ?? resolveDefaultProvider(opts.config);\n if (!requested) {\n // No specific provider asked for and no daemon default. Render heuristic\n // estimates for all four so the client can compare.\n return heuristicEstimatesAllProviders(payload);\n }\n const estimate = await estimateTokens(payload, {\n provider: requested,\n model: opts.model,\n precise: opts.precise ?? false,\n apiKeyEnv: opts.config.execution.api_key_env,\n });\n return [estimate];\n}\n\n/**\n * Resolve the daemon-default provider from config. Returns the configured\n * provider when the user-facing default is not explicitly overridden.\n * Today this is just `config.llm.provider`; the indirection leaves room\n * for environment-driven overrides if we add them later.\n */\nfunction resolveDefaultProvider(config: WotwConfig): LlmProviderName | null {\n const envProvider = process.env.WOTW_LLM_PROVIDER as LlmProviderName | undefined;\n if (\n envProvider === \"anthropic\" ||\n envProvider === \"openai\" ||\n envProvider === \"gemini\" ||\n envProvider === \"ollama\"\n ) {\n return envProvider;\n }\n return config.llm?.provider ?? null;\n}\n\nfunction zeroEstimate(provider: LlmProviderName, model: string | null): TokenEstimate {\n return {\n tokens: 0,\n confidence: \"approximate\",\n method: \"4-char-heuristic\",\n provider,\n model,\n };\n}\n","/**\n * Structural narrow-query tools (Feature Pass 007).\n *\n * Three small MCP tools that expose targeted retrieval primitives at\n * deliberately small token caps:\n *\n * - `define(entity)` — single-paragraph definition, 256 tok cap.\n * - `relate(a, b)` — up to 3 atomic relationship statements\n * between two anchors, 768 tok cap.\n * - `cite_sources(claim)` — provenance citations for a claim, 512\n * tok cap.\n *\n * Each tool runs a single BM25 retrieval and renders a payload that fits\n * inside its budget. There is no daemon-side LLM call — the value is in\n * shipping precisely the structured slice the client LLM asked for, and\n * nothing more. The Codebase-Memory paper (arXiv 2603.27277) is the\n * reference for the \"structural narrow query\" pattern: 83%-quality answers\n * at ~10x token reduction vs. file-exploration.\n *\n * BM25-only commitment: the search path here is the same `WikiSearch`\n * instance the rest of the daemon uses. No vector embeddings.\n */\nimport type { FactIndex } from \"../facts/index-manager.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport { heuristicTokens } from \"./token-estimator.js\";\nimport {\n extractSectionLedes,\n extractSentencesContainingAll,\n firstParagraph,\n sentenceSplit,\n splitFrontmatter,\n truncateToTokenBudget,\n} from \"./truncate.js\";\n\nconst DEFAULT_DEFINE_TOKENS = 256;\nconst DEFAULT_RELATE_TOKENS = 768;\nconst DEFAULT_CITE_TOKENS = 512;\nconst RELATE_MAX_STATEMENTS = 3;\nconst DEFINE_SEARCH_K = 5;\nconst RELATE_SEARCH_K = 10;\nconst CITE_SEARCH_K = 5;\n\nexport interface DefineOptions {\n store: WikiStore;\n search: WikiSearch;\n maxTokens?: number;\n /**\n * Pass B (Feature 007 extension): when the FactIndex is populated, the\n * top fact for `entity` wins over the page-level definition. Falls back\n * to page-level when the fact layer is empty / disabled / sparse.\n */\n factIndex?: FactIndex | null;\n}\n\nexport interface DefineResult {\n entity: string;\n /** Definition text, truncated to the supplied token cap. */\n definition: string;\n /** Wiki-relative path of the source page. */\n source_page: string | null;\n /** BM25 score of the source page hit. */\n score: number | null;\n /** Approximate token count of `definition`. */\n tokens: number;\n /** True when no matching page was found. */\n no_hits: boolean;\n /** Layer the definition came from: \"fact\" (Pass B) or \"page\" (Pass A). */\n source_layer: \"fact\" | \"page\";\n}\n\n/**\n * Resolve `entity` to the most relevant single-paragraph definition or\n * page lede. Falls back to the BM25 snippet when the page body can't be\n * read.\n */\nexport async function defineEntity(entity: string, opts: DefineOptions): Promise<DefineResult> {\n const budget = clampPositive(opts.maxTokens, DEFAULT_DEFINE_TOKENS);\n\n // Pass B: check the fact layer first. A top fact whose entity matches\n // the request is the most precise definition we can return; fall back\n // to page-level if the layer is empty / sparse / disabled.\n if (opts.factIndex && opts.factIndex.size() > 0) {\n const factHits = opts.factIndex.search(entity, 3);\n if (factHits.length > 0) {\n const top = factHits[0]!;\n const truncated = truncateToTokenBudget(top.fact.statement, budget);\n return {\n entity,\n definition: truncated,\n source_page: top.fact.wiki_page_id,\n score: Number(top.score.toFixed(4)),\n tokens: heuristicTokens(truncated),\n no_hits: false,\n source_layer: \"fact\",\n };\n }\n }\n\n const hits = opts.search.search(entity, DEFINE_SEARCH_K);\n if (hits.length === 0) {\n return {\n entity,\n definition: \"\",\n source_page: null,\n score: null,\n tokens: 0,\n no_hits: true,\n source_layer: \"page\",\n };\n }\n const top = hits[0]!;\n const page = await opts.store.readPage(top.path);\n const body = page ? splitFrontmatter(page.raw || \"\").body || page.body : top.snippet;\n const definition = pickDefinition(body) || firstParagraph(body) || top.snippet;\n const truncated = truncateToTokenBudget(definition, budget);\n return {\n entity,\n definition: truncated,\n source_page: opts.store.relativePath(top.path),\n score: Number(top.score.toFixed(4)),\n tokens: heuristicTokens(truncated),\n no_hits: false,\n source_layer: \"page\",\n };\n}\n\n/**\n * Look for an explicit \"Definition\" section header or `**Definition**:` style\n * lead-in. Returns the matched paragraph if found, else null so the caller\n * can fall back to firstParagraph.\n */\nfunction pickDefinition(body: string): string | null {\n // 1) \"## Definition\" / \"### Definition\" section header.\n const sectionMatch = /^#{1,6}\\s+definition\\s*$/im.exec(body);\n if (sectionMatch && sectionMatch.index !== undefined) {\n const after = body.slice(sectionMatch.index + sectionMatch[0].length);\n const paragraph = firstParagraph(after);\n if (paragraph) return paragraph;\n }\n // 2) \"**Definition**:\" inline lead-in.\n const inlineMatch = /^\\s*(?:\\*\\*definition\\*\\*|definition)\\s*:\\s*(.+)$/im.exec(body);\n if (inlineMatch && inlineMatch[1]) {\n return inlineMatch[1].trim();\n }\n // 3) Per-section ledes that start with the word \"is\" or \"are\" (common\n // encyclopedia opening) — return the first section whose lede looks\n // definitional.\n for (const section of extractSectionLedes(body)) {\n if (/^[A-Z][^.]+(?:\\bis\\b|\\bare\\b)/.test(section.lede)) {\n return section.lede;\n }\n }\n return null;\n}\n\nexport interface RelateOptions {\n store: WikiStore;\n search: WikiSearch;\n maxTokens?: number;\n /** Optional cap on relationship statements (default 3). */\n maxStatements?: number;\n /**\n * Pass B extension: when the fact layer is populated, the relate query\n * searches the fused fact/question index for facts mentioning both\n * anchors before falling back to the page-level sentence scan.\n */\n factIndex?: FactIndex | null;\n}\n\nexport interface RelationStatement {\n /** The relationship sentence, verbatim from the source page. */\n statement: string;\n /** Wiki-relative path of the page the sentence came from. */\n source_page: string;\n /** BM25 score of the source page when matched against entity_a + entity_b. */\n score: number;\n}\n\nexport interface RelateResult {\n entity_a: string;\n entity_b: string;\n statements: RelationStatement[];\n tokens: number;\n no_hits: boolean;\n /** Layer the statements came from: \"fact\" (Pass B) or \"page\" (Pass A). */\n source_layer: \"fact\" | \"page\";\n}\n\n/**\n * Find sentences that mention both `entity_a` and `entity_b`. We BM25-search\n * each anchor, intersect the result sets by page path (a page is a candidate\n * only if both anchors mention it), then scan each candidate's body for\n * sentences containing both anchor substrings.\n */\nexport async function relateEntities(\n entityA: string,\n entityB: string,\n opts: RelateOptions,\n): Promise<RelateResult> {\n const budget = clampPositive(opts.maxTokens, DEFAULT_RELATE_TOKENS);\n const maxStatements = clampPositive(opts.maxStatements, RELATE_MAX_STATEMENTS);\n\n // Pass B: fact-layer first. Search the fused index for the combined\n // query; facts whose statement mentions both anchors are stronger\n // relationship signal than page-level sentence scans.\n if (opts.factIndex && opts.factIndex.size() > 0) {\n const aLow = entityA.toLowerCase();\n const bLow = entityB.toLowerCase();\n const factHits = opts.factIndex.search(`${entityA} ${entityB}`, 20);\n const matching = factHits.filter((h) => {\n const blob = `${h.fact.entity} ${h.fact.statement}`.toLowerCase();\n return blob.includes(aLow) && blob.includes(bLow);\n });\n if (matching.length > 0) {\n const statements: RelationStatement[] = [];\n let used = 0;\n for (const m of matching) {\n if (statements.length >= maxStatements) break;\n const block = `${m.fact.statement} _(${m.fact.wiki_page_id})_`;\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n statements.push({\n statement: m.fact.statement,\n source_page: m.fact.wiki_page_id,\n score: Number(m.score.toFixed(4)),\n });\n used += tokens;\n }\n if (statements.length > 0) {\n return {\n entity_a: entityA,\n entity_b: entityB,\n statements,\n tokens: used,\n no_hits: false,\n source_layer: \"fact\",\n };\n }\n }\n }\n\n const hitsA = opts.search.search(entityA, RELATE_SEARCH_K);\n const hitsB = opts.search.search(entityB, RELATE_SEARCH_K);\n\n // Page path → best combined score (sum). Allows ranking the intersection\n // before we crack open page bodies.\n const combinedScores = new Map<string, number>();\n for (const h of hitsA) {\n combinedScores.set(h.path, h.score);\n }\n for (const h of hitsB) {\n const prev = combinedScores.get(h.path);\n if (prev !== undefined) combinedScores.set(h.path, prev + h.score);\n else combinedScores.delete(h.path);\n }\n // Only keep pages that appeared in BOTH result sets — that's our\n // intersection. Sort descending by combined score.\n const intersection = [...combinedScores.entries()]\n .filter(([, score]) => score > 0)\n .sort(([, a], [, b]) => b - a);\n\n if (intersection.length === 0) {\n return {\n entity_a: entityA,\n entity_b: entityB,\n statements: [],\n tokens: 0,\n no_hits: true,\n source_layer: \"page\",\n };\n }\n\n const statements: RelationStatement[] = [];\n let used = 0;\n for (const [pagePath, score] of intersection) {\n if (statements.length >= maxStatements) break;\n if (used >= budget) break;\n const page = await opts.store.readPage(pagePath);\n if (!page) continue;\n const remaining = budget - used;\n const sentences = extractSentencesContainingAll(page.body, [entityA, entityB], remaining);\n for (const s of sentences) {\n if (statements.length >= maxStatements) break;\n const block = `${s} _(${opts.store.relativePath(pagePath)})_`;\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n statements.push({\n statement: s,\n source_page: opts.store.relativePath(pagePath),\n score: Number(score.toFixed(4)),\n });\n used += tokens;\n }\n }\n\n return {\n entity_a: entityA,\n entity_b: entityB,\n statements,\n tokens: used,\n no_hits: statements.length === 0,\n source_layer: \"page\",\n };\n}\n\nexport interface CiteSourcesOptions {\n store: WikiStore;\n search: WikiSearch;\n provenance: ProvenanceChain | null;\n maxTokens?: number;\n /**\n * Pass B: when the fact layer is populated, BM25-match the claim\n * against facts first to pick which pages to look up provenance for.\n * Yields more precise citations than page-level keyword overlap.\n */\n factIndex?: FactIndex | null;\n}\n\nexport interface CitationEntry {\n /** Wiki-relative path of the page the citation is for. */\n wiki_page: string;\n /** BM25 score of the wiki page hit. */\n score: number;\n /** Wiki page title for client-readable rendering. */\n title: string;\n /**\n * Raw source file paths that produced this wiki page. Sourced from the\n * provenance chain's `source_files` field.\n */\n source_files: string[];\n /** Short-hash prefix of the chain record for client-side referencing. */\n chain_hash: string;\n /** ISO timestamp of the provenance record. */\n timestamp: string;\n /** Provenance record type (ingest / heal / compound / query / etc). */\n type: string;\n}\n\nexport interface CiteSourcesResult {\n claim: string;\n citations: CitationEntry[];\n tokens: number;\n no_hits: boolean;\n /** True when the daemon has no provenance subsystem enabled. */\n provenance_unavailable: boolean;\n /** Layer the matched wiki pages came from: \"fact\" (Pass B) or \"page\". */\n source_layer: \"fact\" | \"page\";\n}\n\n/**\n * Find the wiki pages that best match `claim`, then return the provenance\n * citations for each (which raw source files contributed, chain hash for\n * client-side cross-referencing). Caps at the requested token budget; if\n * the citations don't all fit, the top-scoring ones are kept.\n */\nexport async function citeSources(\n claim: string,\n opts: CiteSourcesOptions,\n): Promise<CiteSourcesResult> {\n const budget = clampPositive(opts.maxTokens, DEFAULT_CITE_TOKENS);\n if (!opts.provenance) {\n return {\n claim,\n citations: [],\n tokens: 0,\n no_hits: false,\n provenance_unavailable: true,\n source_layer: \"page\",\n };\n }\n\n // Pass B: use fact-layer matches to pick the wiki pages, then look up\n // each page's most recent provenance record. Falls back to keyword\n // BM25 over page bodies when the fact layer is empty / sparse.\n let pagePaths: { absPath: string; relPath: string; score: number; title: string }[] = [];\n let sourceLayer: \"fact\" | \"page\" = \"page\";\n if (opts.factIndex && opts.factIndex.size() > 0) {\n const factHits = opts.factIndex.search(claim, CITE_SEARCH_K);\n if (factHits.length > 0) {\n sourceLayer = \"fact\";\n const seen = new Set<string>();\n for (const h of factHits) {\n if (seen.has(h.fact.wiki_page_id)) continue;\n seen.add(h.fact.wiki_page_id);\n pagePaths.push({\n absPath: `${opts.store.wikiRoot}/${h.fact.wiki_page_id}`,\n relPath: h.fact.wiki_page_id,\n score: h.score,\n title: h.fact.entity,\n });\n }\n }\n }\n if (pagePaths.length === 0) {\n const hits = opts.search.search(claim, CITE_SEARCH_K);\n if (hits.length === 0) {\n return {\n claim,\n citations: [],\n tokens: 0,\n no_hits: true,\n provenance_unavailable: false,\n source_layer: \"page\",\n };\n }\n pagePaths = hits.map((h) => ({\n absPath: h.path,\n relPath: opts.store.relativePath(h.path),\n score: h.score,\n title: h.title,\n }));\n }\n\n const citations: CitationEntry[] = [];\n let used = 0;\n for (const hit of pagePaths) {\n if (used >= budget) break;\n // recordsFor matches against wiki_files_written, which in the\n // daemon's ingestion + heal pipelines stores relative paths.\n const records = await opts.provenance.recordsFor(hit.relPath);\n if (records.length === 0) continue;\n // Use the most recent record so the citation reflects the page's\n // current provenance (most recent ingest or heal).\n const record = records[records.length - 1]!;\n const entry: CitationEntry = {\n wiki_page: hit.relPath,\n score: Number(hit.score.toFixed(4)),\n title: hit.title,\n source_files: record.source_files,\n chain_hash: record.chain_hash.slice(0, 16),\n timestamp: record.timestamp,\n type: record.type,\n };\n const block = JSON.stringify(entry);\n const tokens = heuristicTokens(block);\n if (used + tokens > budget) break;\n citations.push(entry);\n used += tokens;\n }\n\n return {\n claim,\n citations,\n tokens: used,\n no_hits: citations.length === 0,\n provenance_unavailable: false,\n source_layer: sourceLayer,\n };\n}\n\nfunction clampPositive(value: number | undefined, fallback: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\n/** Re-export sentence-split for downstream consumers (tests, benchmarks). */\nexport { sentenceSplit };\n","/**\n * `query_facts` MCP tool implementation (Feature Pass 008).\n *\n * Runs the fused BM25 query against the {@link FactIndex} — the index\n * scores facts on entity/statement match (weight 0.4) and on synthetic-\n * question match (weight 0.6), then returns the top-N facts ranked by\n * fused score. When the fact layer is disabled or empty, returns a\n * fallback marker so the caller can dispatch to page-level retrieval.\n *\n * BM25-only commitment: this module touches only `FactIndex`, which\n * wraps minisearch. No vector code paths.\n */\nimport type { FactIndex } from \"../facts/index-manager.js\";\nimport type { FactStore } from \"../facts/store.js\";\nimport type { Fact } from \"../facts/types.js\";\nimport { heuristicTokens } from \"./token-estimator.js\";\n\nconst DEFAULT_LIMIT = 5;\nconst MAX_LIMIT = 20;\n\nexport interface QueryFactsOptions {\n factIndex: FactIndex | null;\n factStore: FactStore | null;\n limit?: number;\n}\n\nexport interface QueryFactsHit {\n fact: Fact;\n score: number;\n matched_via_question: boolean;\n matched_via_fact: boolean;\n}\n\nexport interface QueryFactsResult {\n question: string;\n hits: QueryFactsHit[];\n /**\n * When the fact layer is disabled or empty, this is the string\n * \"page-level\" — the caller should fall back to `query_progressive` or\n * `query`. Otherwise null.\n */\n fallback: \"page-level\" | null;\n /** Approximate token count of the rendered hits + metadata. */\n tokens: number;\n /** Total active facts in the index (cheap; 0 when layer disabled). */\n index_size: number;\n}\n\n/**\n * Run a BM25 fact lookup. Empty / disabled fact layer surfaces a\n * `fallback: \"page-level\"` marker so the client LLM can route the\n * follow-up call without round-tripping for an empty payload.\n */\nexport function queryFacts(question: string, opts: QueryFactsOptions): QueryFactsResult {\n const limit = clampLimit(opts.limit);\n const factIndex = opts.factIndex;\n if (!factIndex || factIndex.size() === 0) {\n return {\n question,\n hits: [],\n fallback: \"page-level\",\n tokens: 0,\n index_size: factIndex?.size() ?? 0,\n };\n }\n const fused = factIndex.search(question, limit);\n const hits: QueryFactsHit[] = fused.map((h) => ({\n fact: h.fact,\n score: Number(h.score.toFixed(4)),\n matched_via_fact: h.matched_via_fact,\n matched_via_question: h.matched_via_question,\n }));\n // Render-size estimate: include the statements so the client LLM can\n // judge \"do I have what I need?\" before round-tripping for full pages.\n const rendered = hits\n .map((h) => `- ${h.fact.entity}: ${h.fact.statement} _(${h.fact.wiki_page_id})_`)\n .join(\"\\n\");\n return {\n question,\n hits,\n fallback: null,\n tokens: heuristicTokens(rendered),\n index_size: factIndex.size(),\n };\n}\n\nfunction clampLimit(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return DEFAULT_LIMIT;\n return Math.max(1, Math.min(MAX_LIMIT, Math.floor(value)));\n}\n\n/**\n * Convenience renderer for the MCP tool — produces a markdown bullet\n * list of the matched facts plus a JSON metadata blob. Identical to the\n * style used by `query_progressive`.\n */\nexport function renderFactsMarkdown(result: QueryFactsResult): string {\n if (result.fallback === \"page-level\") {\n return result.index_size === 0\n ? \"_fact layer is empty or disabled — call `query_progressive` or `query` instead_\"\n : \"_no facts matched — call `query_progressive` for page-level retrieval_\";\n }\n if (result.hits.length === 0) return \"_no facts found_\";\n return result.hits\n .map(\n (h) =>\n `- **${h.fact.entity}**: ${h.fact.statement} _(score ${h.score.toFixed(3)} · ${h.fact.wiki_page_id})_`,\n )\n .join(\"\\n\");\n}\n","/**\n * MCP tool registrations. Each tool has a zod input schema and a handler\n * that returns a CallToolResult. Tools are kept small and composable — the\n * heavy work happens in the wiki/search/query layers.\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { z } from \"zod\";\nimport { readTextOrNullAsync } from \"../utils/fs.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport { CATEGORY_DIRS } from \"../wiki/store.js\";\nimport type { IndexManager } from \"../wiki/index-manager.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { QueryEngine } from \"./query-engine.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport type { DeadLetterQueue } from \"../ingestion/dead-letter.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport type { CompoundingEngine } from \"../compounding/engine.js\";\nimport type { LlmProviderName, WotwConfig } from \"../utils/types.js\";\nimport type { ProgressiveCache } from \"./progressive-cache.js\";\nimport { queryProgressive, queryExpand } from \"./progressive-query.js\";\nimport { estimateQueryCost } from \"./cost-estimator.js\";\nimport { defineEntity, relateEntities, citeSources } from \"./narrow-query.js\";\nimport { queryFacts, renderFactsMarkdown } from \"./fact-query.js\";\nimport type { FactIndex } from \"../facts/index-manager.js\";\nimport type { FactStore } from \"../facts/store.js\";\n\nconst VALID_CATEGORIES = Object.keys(CATEGORY_DIRS) as Array<keyof typeof CATEGORY_DIRS>;\n\nexport interface ToolRegistrationContext {\n config: WotwConfig;\n store: WikiStore;\n indexManager: IndexManager;\n search: WikiSearch;\n queryEngine: QueryEngine;\n costTracker: CostTracker;\n provenance?: ProvenanceChain | null;\n compounding?: CompoundingEngine | null;\n /**\n * Dead-letter sink. Optional because legacy tests don't supply one;\n * when absent `get_stats` reports `failed_batches: null`.\n */\n deadLetter?: DeadLetterQueue | null;\n /** Count of provenance append failures observed during this daemon lifetime. */\n provenanceGapCount?: number;\n /** Optional watcher reference for degradation reporting. */\n watcher?: { isDegraded(): boolean } | null;\n /**\n * Continuation cache for `query_progressive` / `query_expand`. Shared\n * across MCP requests on the long-lived McpHttpServer instance.\n */\n progressiveCache?: ProgressiveCache | null;\n /**\n * Pass B fact-extraction sidecar. When present, the query_facts MCP\n * tool is registered and the Group A tools (define / relate /\n * cite_sources) prefer fact-layer matches before falling back to\n * page-level retrieval.\n */\n factStore?: FactStore | null;\n factIndex?: FactIndex | null;\n}\n\n/**\n * Register every MCP tool on the provided server instance.\n */\nexport function registerTools(server: McpServer, ctx: ToolRegistrationContext): void {\n const log = getLogger(\"mcp-tools\");\n\n // --- search ---------------------------------------------------------\n server.registerTool(\n \"search\",\n {\n title: \"Full-text search over the wiki\",\n description: \"Search the wiki for pages matching a query. Returns ranked hits with snippets.\",\n inputSchema: {\n query: z.string().min(1).describe(\"Search query. Supports fuzzy matching and prefix.\"),\n limit: z.number().int().min(1).max(100).default(20).optional(),\n domain: z\n .string()\n .optional()\n .describe(\"Filter results to pages matching this knowledge domain.\"),\n scope: z\n .string()\n .optional()\n .describe(\"Filter results to pages matching this project/context scope.\"),\n },\n },\n async ({ query, limit, domain, scope }) => {\n // Search index health check.\n if (ctx.search.size() === 0 && ctx.store.count() > 0) {\n return errorResult(\"search index is empty but wiki has pages — rebuild required\");\n }\n const filters = domain || scope ? { domain, scope } : undefined;\n const hits = ctx.search.search(query, limit ?? 20, filters);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n hits.map((h) => ({\n title: h.title,\n category: h.category,\n path: ctx.store.relativePath(h.path),\n score: Number(h.score.toFixed(4)),\n snippet: h.snippet,\n })),\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- list_pages -----------------------------------------------------\n server.registerTool(\n \"list_pages\",\n {\n title: \"List wiki pages\",\n description: \"List every wiki page, optionally filtered by category.\",\n inputSchema: {\n category: z.enum(VALID_CATEGORIES as [string, ...string[]]).optional(),\n },\n },\n async ({ category }) => {\n const paths = category\n ? (() => {\n const dir = ctx.store.categoryDir(category as keyof typeof CATEGORY_DIRS);\n return ctx.store.listAll().filter((p) => p.startsWith(dir));\n })()\n : ctx.store.listAll();\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n paths.map((p) => ctx.store.relativePath(p)),\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- read_page ------------------------------------------------------\n server.registerTool(\n \"read_page\",\n {\n title: \"Read a wiki page by path\",\n description:\n \"Read a single wiki page by its wiki-relative path (e.g. 'wiki/concepts/foo.md').\",\n inputSchema: {\n path: z.string().min(1),\n },\n },\n async ({ path }) => {\n // Paths are interpreted relative to the wiki root so clients can't\n // escape it via absolute paths.\n const abs = resolveWikiPath(ctx, path);\n if (!abs) {\n return errorResult(`path outside wiki root: ${path}`);\n }\n const raw = await readTextOrNullAsync(abs);\n if (raw === null) {\n return errorResult(`page not found: ${path}`);\n }\n return {\n content: [{ type: \"text\", text: raw }],\n };\n },\n );\n\n // --- query ----------------------------------------------------------\n server.registerTool(\n \"query\",\n {\n title: \"Answer a question from the wiki\",\n description:\n \"Answer a natural-language question grounded in the wiki. Returns an answer with inline citations.\",\n inputSchema: {\n question: z.string().min(1),\n k: z.number().int().min(1).max(20).default(8).optional(),\n // Review item 15: `domain` and `scope` were advertised in the\n // tool schema but destructured into `_domain` / `_scope` and\n // ignored. The schema now reflects only what's implemented;\n // re-add once wiki search supports filter-by-frontmatter.\n },\n },\n async ({ question, k }) => {\n log.info({ question }, \"mcp query\");\n const result = await ctx.queryEngine.answer(question, k ?? 8);\n if (result.skipped) {\n return errorResult(result.skipReason ?? \"query skipped\");\n }\n return {\n content: [\n { type: \"text\", text: result.answer },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n sources: result.sources.map((s) => ctx.store.relativePath(s.path)),\n cost_usd: result.costUsd,\n model: result.model,\n duration_ms: result.durationMs,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- get_index ------------------------------------------------------\n server.registerTool(\n \"get_index\",\n {\n title: \"Get the wiki index\",\n description: \"Return the current contents of wiki/index.md.\",\n inputSchema: {},\n },\n async () => {\n const text = await ctx.indexManager.read();\n if (!text) {\n return { content: [{ type: \"text\", text: \"_index not yet built_\" }], isError: true };\n }\n return { content: [{ type: \"text\", text }] };\n },\n );\n\n // --- get_stats ------------------------------------------------------\n server.registerTool(\n \"get_stats\",\n {\n title: \"Get wiki stats\",\n description: \"Return counts of wiki pages by category and today's cost.\",\n inputSchema: {},\n },\n async () => {\n const counts: Record<string, number> = {};\n for (const cat of VALID_CATEGORIES) {\n counts[cat] = ctx.store.count(cat);\n }\n // Count orphaned pages by walking the full page list once. Cheap\n // — parsing is cached at the file-system level and the frontmatter\n // check short-circuits on non-orphaned pages.\n let orphanedPages = 0;\n for (const p of ctx.store.listAll()) {\n const page = await ctx.store.readPage(p);\n if (page && page.frontmatter.status === \"orphaned\") orphanedPages += 1;\n }\n const failedBatches = ctx.deadLetter ? await ctx.deadLetter.count() : null;\n\n // Compute health summary (no LLM calls).\n let healthSummary: {\n avg_score: number;\n pages_below_50: number;\n lowest_scoring_page: string | null;\n } | null = null;\n try {\n const { computeHealthReport } = await import(\"../wiki/health.js\");\n const pages = ctx.store.listAll();\n if (pages.length > 0) {\n const report = await computeHealthReport(ctx.store, ctx.provenance ?? null, ctx.search, {\n config: ctx.config,\n });\n const avg =\n report.scores.length > 0\n ? Math.round(report.scores.reduce((s, sc) => s + sc.score, 0) / report.scores.length)\n : 0;\n const belowFifty = report.scores.filter((s) => s.score < 50);\n const lowest =\n report.scores.length > 0\n ? report.scores.reduce((min, s) => (s.score < min.score ? s : min), report.scores[0]!)\n : null;\n healthSummary = {\n avg_score: avg,\n pages_below_50: belowFifty.length,\n lowest_scoring_page: lowest ? lowest.page : null,\n };\n }\n } catch (err: unknown) {\n log.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"health computation failed\",\n );\n }\n\n // Query health metrics.\n let queryHealth: {\n total_queries_7d: number;\n zero_hit_rate_7d: number;\n zero_hit_queries_7d: string[];\n } | null = null;\n try {\n const { computeZeroHitRate } = await import(\"./query-metrics.js\");\n const metrics = computeZeroHitRate(ctx.config.health.query_log_file);\n if (metrics.total_queries > 0) {\n queryHealth = {\n total_queries_7d: metrics.total_queries,\n zero_hit_rate_7d: Number(metrics.zero_hit_rate.toFixed(4)),\n zero_hit_queries_7d: metrics.recent_zero_hit_queries.slice(0, 10),\n };\n }\n } catch (err: unknown) {\n log.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"query metrics computation failed\",\n );\n }\n\n const stats = {\n total: ctx.store.count(),\n by_category: counts,\n orphaned_pages: orphanedPages,\n cost_today_usd: ctx.costTracker.spentToday(),\n indexed_documents: ctx.search.size(),\n failed_batches: failedBatches,\n dead_letter_configured: ctx.deadLetter !== null && ctx.deadLetter !== undefined,\n provenance_gaps: ctx.provenanceGapCount ?? 0,\n watcher_degraded: ctx.watcher?.isDegraded() ?? false,\n ...(healthSummary ? { health: healthSummary } : {}),\n ...(queryHealth ? { query_health: queryHealth } : {}),\n };\n return { content: [{ type: \"text\", text: JSON.stringify(stats, null, 2) }] };\n },\n );\n\n // --- related_pages --------------------------------------------------\n server.registerTool(\n \"related_pages\",\n {\n title: \"Find pages related to a given page\",\n description: \"Return the `related:` frontmatter slugs for a given wiki page.\",\n inputSchema: {\n path: z.string().min(1),\n },\n },\n async ({ path }) => {\n const abs = resolveWikiPath(ctx, path);\n if (!abs) return errorResult(`path outside wiki root: ${path}`);\n const page = await ctx.store.readPage(abs);\n if (!page) return errorResult(`page not found: ${path}`);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n title: page.frontmatter.title,\n related: page.frontmatter.related,\n tags: page.frontmatter.tags,\n sources: page.frontmatter.sources,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- get_provenance_log --------------------------------------------\n if (ctx.provenance) {\n const chain = ctx.provenance;\n server.registerTool(\n \"get_provenance_log\",\n {\n title: \"Read recent provenance records\",\n description:\n \"Return the N most recent cryptographic provenance records. Optionally filter to records that touched a specific wiki page.\",\n inputSchema: {\n limit: z.number().int().min(1).max(500).default(20).optional(),\n path: z.string().optional(),\n },\n },\n async ({ limit, path }) => {\n const records = path ? await chain.recordsFor(path) : await chain.readRecent(limit ?? 20);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n total: chain.count(),\n returned: records.length,\n records: records.map((r) => ({\n seq: r.seq,\n id: r.id.slice(0, 16),\n timestamp: r.timestamp,\n type: r.type,\n model: r.model_id,\n sources: r.source_files.length,\n written: r.wiki_files_written.length,\n cost_usd: r.metadata?.cost_usd ?? null,\n chain_hash: r.chain_hash.slice(0, 16),\n })),\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- verify_provenance --------------------------------------------\n server.registerTool(\n \"verify_provenance\",\n {\n title: \"Verify the cryptographic provenance chain\",\n description:\n \"Walk the entire provenance chain, recomputing every record's id and chain_hash to detect tampering. Returns a verification report.\",\n inputSchema: {},\n },\n async () => {\n const result = await chain.verify();\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n ok: result.ok,\n total: result.totalRecords,\n verified: result.verifiedRecords,\n errors: result.errors.slice(0, 10),\n head: chain.head(),\n signature: await chain.signature(),\n },\n null,\n 2,\n ),\n },\n ],\n isError: !result.ok,\n };\n },\n );\n }\n\n // --- query_progressive ----------------------------------------------\n // Feature Pass 005. Smallest-viable-answer-first retrieval. Pure\n // structural over the BM25 hit list — no daemon-side LLM call.\n if (ctx.progressiveCache) {\n const cache = ctx.progressiveCache;\n server.registerTool(\n \"query_progressive\",\n {\n title: \"Progressive wiki retrieval\",\n description:\n \"Retrieve the smallest viable answer first (tier 0 = top hit's lede paragraph), with a continuation_token to expand to higher tiers as the client LLM signals it needs more context. Pure BM25 retrieval; no daemon-side LLM synthesis. Use with `query_expand` for paged retrieval.\",\n inputSchema: {\n question: z.string().min(1),\n max_tokens_initial: z\n .number()\n .int()\n .min(64)\n .max(8192)\n .default(512)\n .optional()\n .describe(\"Token budget for the initial (tier 0) response.\"),\n max_tokens_total: z\n .number()\n .int()\n .min(64)\n .max(32768)\n .default(8192)\n .optional()\n .describe(\"Hard cap on tokens shipped across all expand calls.\"),\n },\n },\n async ({ question, max_tokens_initial, max_tokens_total }) => {\n log.info({ question }, \"mcp query_progressive\");\n const result = await queryProgressive(question, {\n store: ctx.store,\n search: ctx.search,\n cache,\n maxTokensInitial: max_tokens_initial,\n maxTokensTotal: max_tokens_total,\n });\n return {\n content: [\n { type: \"text\", text: result.content },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n tier: result.tier,\n tier_label: result.tier_label,\n hit_count_delta: result.hit_count_delta,\n hit_count_total: result.hit_count_total,\n tokens_delivered: result.tokens_delivered,\n tokens_shipped_total: result.tokens_shipped_total,\n has_more: result.has_more,\n continuation_token: result.continuation_token,\n next_tier_label: result.next_tier_label,\n next_tier_estimate_tokens: result.next_tier_estimate_tokens,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- query_expand --------------------------------------------------\n server.registerTool(\n \"query_expand\",\n {\n title: \"Expand a progressive retrieval to the next tier\",\n description:\n \"Advance one tier on a prior query_progressive call. Returns ONLY the new content the next tier reveals (snippets / section-ledes / full-bodies). Pass the continuation_token from the previous response.\",\n inputSchema: {\n continuation_token: z.string().min(1),\n additional_tokens: z\n .number()\n .int()\n .min(64)\n .max(16384)\n .default(1024)\n .optional()\n .describe(\"Token budget for this expansion. Bounded by max_tokens_total.\"),\n },\n },\n async ({ continuation_token, additional_tokens }) => {\n log.info({ continuation_token: continuation_token.slice(0, 8) }, \"mcp query_expand\");\n const result = await queryExpand(continuation_token, {\n cache,\n additionalTokens: additional_tokens,\n });\n if (\"error\" in result) {\n return errorResult(result.error);\n }\n return {\n content: [\n { type: \"text\", text: result.content },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n tier: result.tier,\n tier_label: result.tier_label,\n hit_count_delta: result.hit_count_delta,\n hit_count_total: result.hit_count_total,\n tokens_delivered: result.tokens_delivered,\n tokens_shipped_total: result.tokens_shipped_total,\n has_more: result.has_more,\n continuation_token: result.continuation_token,\n next_tier_label: result.next_tier_label,\n next_tier_estimate_tokens: result.next_tier_estimate_tokens,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n }\n\n // --- estimate_query_cost --------------------------------------------\n // Feature Pass 006. Pre-flight token estimate so the client LLM knows\n // what the retrieval payload will cost before committing.\n server.registerTool(\n \"estimate_query_cost\",\n {\n title: \"Estimate retrieval-payload token cost\",\n description:\n \"Run BM25 retrieval over a candidate question and report the token count of the daemon's resulting retrieval payload, scoped per provider. Use BEFORE calling `query` or `query_progressive` to know what you're committing to. Provider defaults to WOTW_LLM_PROVIDER or the daemon's configured llm.provider; omitting both returns a four-row comparison.\",\n inputSchema: {\n question: z.string().min(1),\n provider: z\n .enum([\"anthropic\", \"openai\", \"gemini\", \"ollama\"])\n .optional()\n .describe(\"Specific provider to estimate for. Omit to compare all four.\"),\n model: z\n .string()\n .optional()\n .describe(\"Model identifier (required for precise Anthropic/Gemini counts).\"),\n precise: z\n .boolean()\n .default(false)\n .optional()\n .describe(\n \"Use the provider's native tokenizer (network call for Anthropic/Gemini). Default is the 4-char heuristic.\",\n ),\n k: z.number().int().min(1).max(20).default(8).optional(),\n },\n },\n async ({ question, provider, model, precise, k }) => {\n log.info({ question, provider, precise }, \"mcp estimate_query_cost\");\n const result = await estimateQueryCost(question, {\n store: ctx.store,\n search: ctx.search,\n config: ctx.config,\n provider: provider as LlmProviderName | undefined,\n model,\n precise: precise ?? false,\n k,\n });\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n },\n );\n\n // --- define ---------------------------------------------------------\n // Feature Pass 007: structural narrow query — single-paragraph definition.\n server.registerTool(\n \"define\",\n {\n title: \"Get a one-paragraph definition of an entity\",\n description:\n \"BM25-search for `entity` and return the most relevant single-paragraph definition or page lede. Capped at 256 tokens by default — for full-page reading use `read_page`.\",\n inputSchema: {\n entity: z.string().min(1),\n max_tokens: z.number().int().min(32).max(2048).default(256).optional(),\n },\n },\n async ({ entity, max_tokens }) => {\n log.info({ entity }, \"mcp define\");\n const result = await defineEntity(entity, {\n store: ctx.store,\n search: ctx.search,\n maxTokens: max_tokens,\n factIndex: ctx.factIndex ?? null,\n });\n return {\n content: [\n { type: \"text\", text: result.definition || `_no definition found for ${entity}_` },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n entity: result.entity,\n source_page: result.source_page,\n score: result.score,\n tokens: result.tokens,\n no_hits: result.no_hits,\n source_layer: result.source_layer,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- relate ---------------------------------------------------------\n // Feature Pass 007: up to N atomic relationship statements between\n // two anchors. Intersection-based — only pages that appear in BOTH\n // result sets are scanned for sentences.\n server.registerTool(\n \"relate\",\n {\n title: \"Find relationship statements between two entities\",\n description:\n \"Find sentences in the wiki that contain BOTH `entity_a` and `entity_b`. Returns up to 3 atomic statements by default. Capped at 768 tokens — for broader context use `query_progressive`.\",\n inputSchema: {\n entity_a: z.string().min(1),\n entity_b: z.string().min(1),\n max_tokens: z.number().int().min(64).max(4096).default(768).optional(),\n max_statements: z.number().int().min(1).max(10).default(3).optional(),\n },\n },\n async ({ entity_a, entity_b, max_tokens, max_statements }) => {\n log.info({ entity_a, entity_b }, \"mcp relate\");\n const result = await relateEntities(entity_a, entity_b, {\n store: ctx.store,\n search: ctx.search,\n maxTokens: max_tokens,\n maxStatements: max_statements,\n factIndex: ctx.factIndex ?? null,\n });\n const rendered =\n result.statements.length === 0\n ? `_no relationship statements found between ${entity_a} and ${entity_b}_`\n : result.statements.map((s) => `- ${s.statement} _(${s.source_page})_`).join(\"\\n\");\n return {\n content: [\n { type: \"text\", text: rendered },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n entity_a: result.entity_a,\n entity_b: result.entity_b,\n statement_count: result.statements.length,\n tokens: result.tokens,\n no_hits: result.no_hits,\n source_layer: result.source_layer,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- cite_sources ---------------------------------------------------\n // Feature Pass 007: provenance citations for a claim.\n server.registerTool(\n \"cite_sources\",\n {\n title: \"Get provenance citations for a claim\",\n description:\n \"BM25-search for `claim`, then return the provenance records (raw source files + chain hash + timestamp) that produced the matched wiki pages. Capped at 512 tokens by default.\",\n inputSchema: {\n claim: z.string().min(1),\n max_tokens: z.number().int().min(64).max(4096).default(512).optional(),\n },\n },\n async ({ claim, max_tokens }) => {\n log.info({ claim }, \"mcp cite_sources\");\n const result = await citeSources(claim, {\n store: ctx.store,\n search: ctx.search,\n provenance: ctx.provenance ?? null,\n maxTokens: max_tokens,\n factIndex: ctx.factIndex ?? null,\n });\n const rendered =\n result.citations.length === 0\n ? result.provenance_unavailable\n ? \"_provenance subsystem is disabled in this daemon_\"\n : `_no provenance citations found for: ${claim}_`\n : result.citations\n .map(\n (c) =>\n `- **${c.title}** (\\`${c.wiki_page}\\`, ${c.type} @ ${c.timestamp})\\n sources: ${c.source_files.length > 0 ? c.source_files.join(\", \") : \"_none_\"}\\n chain: ${c.chain_hash}`,\n )\n .join(\"\\n\");\n return {\n content: [\n { type: \"text\", text: rendered },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n claim: result.claim,\n citation_count: result.citations.length,\n tokens: result.tokens,\n no_hits: result.no_hits,\n provenance_unavailable: result.provenance_unavailable,\n source_layer: result.source_layer,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- query_facts ----------------------------------------------------\n // Feature Pass 008: structural fact-level retrieval. BM25 over the\n // facts + synthetic-questions index (weighted 0.4 + 0.6). When the\n // fact layer is empty / disabled, the response carries\n // fallback: \"page-level\" so the client LLM knows to redirect to\n // query_progressive.\n server.registerTool(\n \"query_facts\",\n {\n title: \"Atomic-fact retrieval (Pass B)\",\n description:\n 'Search the fact-level index (atomic entity/statement pairs + synthetic questions) for the best matching facts. Per Cambridge ALTA + Yanhong Li / TTIC, this gives precise atomic answers at ~10x fewer tokens than page-level retrieval. When the fact layer is disabled / empty, returns fallback:\"page-level\" so the caller can redirect to `query_progressive`.',\n inputSchema: {\n question: z.string().min(1),\n limit: z.number().int().min(1).max(20).default(5).optional(),\n },\n },\n async ({ question, limit }) => {\n log.info({ question }, \"mcp query_facts\");\n const result = queryFacts(question, {\n factIndex: ctx.factIndex ?? null,\n factStore: ctx.factStore ?? null,\n limit,\n });\n return {\n content: [\n { type: \"text\", text: renderFactsMarkdown(result) },\n {\n type: \"text\",\n text: JSON.stringify(\n {\n question: result.question,\n hit_count: result.hits.length,\n tokens: result.tokens,\n fallback: result.fallback,\n index_size: result.index_size,\n hits: result.hits.map((h) => ({\n entity: h.fact.entity,\n statement: h.fact.statement,\n wiki_page: h.fact.wiki_page_id,\n score: h.score,\n matched_via_fact: h.matched_via_fact,\n matched_via_question: h.matched_via_question,\n })),\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n\n // --- synthesize ----------------------------------------------------\n if (ctx.compounding) {\n const compounding = ctx.compounding;\n server.registerTool(\n \"synthesize\",\n {\n title: \"Run a compounding synthesis pass\",\n description:\n \"Trigger a synthesis pass that finds clusters of related wiki pages and writes higher-level synthesis pages. Budget-gated and idempotent — existing syntheses covering a cluster are skipped.\",\n inputSchema: {},\n },\n async () => {\n log.info(\"mcp synthesize triggered\");\n const result = await compounding.synthesize();\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n skipped: result.skipped,\n skip_reason: result.skipReason ?? null,\n clusters: result.clusters,\n pages_written: result.pagesWritten,\n cost_usd: result.costUsd,\n git_sha: result.gitSha,\n duration_ms: result.durationMs,\n },\n null,\n 2,\n ),\n },\n ],\n };\n },\n );\n }\n}\n\n/**\n * Resolve a wiki-relative path to an absolute path, rejecting any input\n * that would escape the wiki root.\n *\n * **M-SEC-1.** The previous implementation normalized separators to `/`\n * and then did a substring check for `..`. That catches the most obvious\n * `../../etc/passwd` case but misses at least three real-world attacks:\n *\n * 1. A Windows absolute path like `C:/etc/passwd` — the substring\n * contains no `..` so the old check accepted it and concatenated it\n * with `wiki_root` to build `/wiki/rootC:/etc/passwd`, which on\n * Windows resolves to `C:/etc/passwd`.\n * 2. A POSIX absolute path like `/etc/passwd` — the lstrip of leading\n * slashes turned it into `etc/passwd`, inside the wiki root. That's\n * \"safe\" but breaks the principle of least surprise.\n * 3. A path where `..` appears inside a filename (e.g. `..config.md`)\n * — the substring check would false-reject a legitimate file.\n *\n * The fix is the standard Node.js idiom for containment: `path.resolve`\n * the user input against the wiki root (collapses `..`, resolves\n * absolutes), then `path.relative` the result back against the wiki root\n * and reject if the relative form starts with `..` (escape) or is\n * absolute (different drive on Windows). This is robust on both POSIX\n * and Windows.\n */\nfunction resolveWikiPath(ctx: ToolRegistrationContext, wikiRelative: string): string | null {\n if (typeof wikiRelative !== \"string\" || wikiRelative.length === 0) return null;\n const wikiRoot = resolve(ctx.config.wiki_root);\n const abs = resolve(wikiRoot, wikiRelative);\n const rel = relative(wikiRoot, abs);\n if (rel === \"..\" || rel.startsWith(`..${sep}`) || rel.startsWith(\"../\") || isAbsolute(rel)) {\n return null;\n }\n return abs;\n}\n\n/** Exposed for unit tests — see M-SEC-1 in AUDIT-REPORT.md. */\nexport { resolveWikiPath as _resolveWikiPathForTests };\n\nfunction errorResult(message: string): {\n content: { type: \"text\"; text: string }[];\n isError: boolean;\n} {\n return {\n content: [{ type: \"text\", text: message }],\n isError: true,\n };\n}\n","/**\n * MCP resource registrations. Resources expose static URIs that clients\n * can read directly (not a tool call). We expose:\n *\n * wiki://index — the auto-generated wiki index markdown\n * wiki://schema — the project CLAUDE.md schema\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { join } from \"node:path\";\nimport { readTextOrNullAsync } from \"../utils/fs.js\";\nimport type { WotwConfig } from \"../utils/types.js\";\nimport type { IndexManager } from \"../wiki/index-manager.js\";\n\nexport interface ResourceRegistrationContext {\n config: WotwConfig;\n indexManager: IndexManager;\n}\n\nexport function registerResources(server: McpServer, ctx: ResourceRegistrationContext): void {\n server.registerResource(\n \"wiki-index\",\n \"wiki://index\",\n {\n title: \"Wiki Index\",\n description: \"The auto-generated catalog of every wiki page.\",\n mimeType: \"text/markdown\",\n },\n async () => {\n const text = (await ctx.indexManager.read()) ?? \"# Wiki Index\\n\\n_Not yet built._\";\n return {\n contents: [\n {\n uri: \"wiki://index\",\n mimeType: \"text/markdown\",\n text,\n },\n ],\n };\n },\n );\n\n server.registerResource(\n \"wiki-schema\",\n \"wiki://schema\",\n {\n title: \"Wiki Schema (CLAUDE.md)\",\n description: \"The project's CLAUDE.md schema and ingestion conventions.\",\n mimeType: \"text/markdown\",\n },\n async () => {\n const path = join(ctx.config.wiki_root, \"CLAUDE.md\");\n const text = (await readTextOrNullAsync(path)) ?? \"# CLAUDE.md\\n\\n_Missing._\";\n return {\n contents: [\n {\n uri: \"wiki://schema\",\n mimeType: \"text/markdown\",\n text,\n },\n ],\n };\n },\n );\n}\n","/**\n * HTTP middleware used by the MCP server: auth token check, simple rate\n * limit (token bucket per IP), structured request logging.\n */\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { Principal, TokenStore } from \"../multi-user/token-store.js\";\n\n/**\n * Constant-time string comparison used by the legacy single-token auth\n * branch (F-7). The multi-user `TokenStore` documents why it skips\n * timing-safe compare for `wotw_<64hex>` tokens, but the legacy path\n * accepts arbitrary operator-chosen strings that may be short or\n * low-entropy, so a network-level timing oracle here would be a real\n * leak. We pad the length-mismatch branch with a dummy `timingSafeEqual`\n * call so the running time of the false-return path is independent of\n * which input was longer.\n */\nfunction safeEqual(a: string, b: string): boolean {\n const aBuf = Buffer.from(a);\n const bBuf = Buffer.from(b);\n if (aBuf.length !== bBuf.length) {\n timingSafeEqual(aBuf, aBuf);\n return false;\n }\n return timingSafeEqual(aBuf, bBuf);\n}\n\nexport interface MiddlewareOptions {\n /**\n * Legacy single-token auth. When set, requests must present this exact\n * Bearer token. Ignored if `tokenStore` is provided.\n */\n authToken: string | null;\n /**\n * Multi-user token store. When present, Bearer tokens are looked up in\n * the store and the resulting principal is attached to the request.\n */\n tokenStore?: TokenStore | null;\n rateLimitRpm: number;\n /**\n * When true, trust `X-Forwarded-For` for client IP. When false\n * (default), always use `req.socket.remoteAddress`.\n */\n trustProxy?: boolean;\n}\n\n/**\n * Result of running middleware: whether to continue, and if so which\n * principal (if any) was authenticated.\n */\nexport interface MiddlewareResult {\n ok: boolean;\n principal: Principal | null;\n}\n\ninterface Bucket {\n tokens: number;\n updatedAt: number;\n}\n\n/**\n * Simple in-memory token-bucket rate limiter keyed by client IP.\n * Refills at rateLimitRpm / 60 tokens per second.\n */\nexport class RateLimiter {\n private readonly capacity: number;\n private readonly refillPerMs: number;\n private buckets = new Map<string, Bucket>();\n\n constructor(ratePerMinute: number) {\n this.capacity = Math.max(1, ratePerMinute);\n this.refillPerMs = this.capacity / 60_000;\n }\n\n /**\n * Attempt to consume one token for the given key. Returns true if allowed.\n */\n take(key: string): boolean {\n const now = Date.now();\n const existing = this.buckets.get(key);\n if (!existing) {\n this.buckets.set(key, { tokens: this.capacity - 1, updatedAt: now });\n return true;\n }\n const elapsed = now - existing.updatedAt;\n existing.tokens = Math.min(this.capacity, existing.tokens + elapsed * this.refillPerMs);\n existing.updatedAt = now;\n if (existing.tokens < 1) return false;\n existing.tokens -= 1;\n return true;\n }\n\n /** Prune old entries (call periodically if long-running). */\n sweep(maxIdleMs: number): void {\n const cutoff = Date.now() - maxIdleMs;\n for (const [key, bucket] of this.buckets) {\n if (bucket.updatedAt < cutoff) this.buckets.delete(key);\n }\n }\n}\n\n/**\n * Run the pre-request middleware chain. Returns `{ ok: true, principal }` if\n * the caller should continue handling the request, `{ ok: false }` if it has\n * already been responded to (auth failure, rate limit).\n *\n * The returned principal reflects multi-user auth: if a TokenStore is\n * configured, it resolves the Bearer token to a user. Single-user mode\n * (legacy `authToken`) returns `{ user: \"default\" }` on success, or a\n * null principal when no auth is configured at all.\n */\nexport function runMiddleware(\n req: IncomingMessage,\n res: ServerResponse,\n opts: MiddlewareOptions,\n limiter: RateLimiter,\n): MiddlewareResult {\n const log = getLogger(\"http\");\n const clientIp = extractClientIp(req, opts.trustProxy === true);\n\n // Rate limit first so a flood of 401s doesn't DoS us either.\n if (!limiter.take(clientIp)) {\n res.writeHead(429, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"rate limit exceeded\" }));\n log.warn({ clientIp, path: req.url }, \"rate limited\");\n return { ok: false, principal: null };\n }\n\n let principal: Principal | null = null;\n\n // Multi-user auth takes precedence when a token store is configured.\n if (opts.tokenStore) {\n const provided = extractBearer(req);\n if (!provided) {\n res.writeHead(401, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"unauthorized\" }));\n log.warn({ clientIp, path: req.url }, \"missing bearer token\");\n return { ok: false, principal: null };\n }\n const found = opts.tokenStore.authenticate(provided);\n if (!found) {\n res.writeHead(401, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"unauthorized\" }));\n log.warn({ clientIp, path: req.url }, \"unknown token\");\n return { ok: false, principal: null };\n }\n principal = found;\n } else if (opts.authToken) {\n // Single-token legacy mode. F-7: use a constant-time comparison\n // because the operator-chosen token may be short or low-entropy.\n const provided = extractBearer(req) ?? \"\";\n if (!safeEqual(provided, opts.authToken)) {\n res.writeHead(401, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"unauthorized\" }));\n log.warn({ clientIp, path: req.url }, \"unauthorized request\");\n return { ok: false, principal: null };\n }\n principal = { user: \"default\" };\n }\n\n log.debug({ clientIp, method: req.method, path: req.url, user: principal?.user }, \"request\");\n return { ok: true, principal };\n}\n\nfunction extractBearer(req: IncomingMessage): string | null {\n const h = req.headers[\"authorization\"];\n if (typeof h !== \"string\") return null;\n if (!h.toLowerCase().startsWith(\"bearer \")) return null;\n return h.slice(7).trim() || null;\n}\n\nfunction extractClientIp(req: IncomingMessage, trustProxy: boolean): string {\n if (trustProxy) {\n const fwd = req.headers[\"x-forwarded-for\"];\n if (typeof fwd === \"string\") return fwd.split(\",\")[0]?.trim() ?? \"unknown\";\n }\n return req.socket.remoteAddress ?? \"unknown\";\n}\n","/**\n * In-memory cache for progressive-retrieval continuations.\n *\n * `query_progressive` runs BM25 retrieval once, pre-fetches the top-k page\n * bodies, and ships the smallest viable answer (tier 0) to the client LLM.\n * `query_expand` is called with a continuation_token to receive higher\n * tiers without re-doing the search. This module owns the short-lived\n * keyed state that makes that two-step flow possible.\n *\n * Invariants:\n * - Entries expire 5 minutes after creation (TTL). After that, the\n * continuation_token is considered invalid and the client must call\n * query_progressive again.\n * - LRU eviction caps the cache at MAX_ENTRIES; the oldest *touched*\n * entry is dropped first, not the oldest *created*.\n * - All state is in-memory and per-daemon-process. Restarting the daemon\n * invalidates every continuation_token — that's intentional. The cache\n * is a session-scoped optimisation, not durable storage.\n *\n * BYOK / Pass 008 invariants: nothing in this cache touches API keys; the\n * cache stores question text + retrieved page bodies, all of which are\n * already inside the daemon's trust boundary (read from the local wiki).\n */\nimport { randomUUID } from \"node:crypto\";\nimport type { SearchHit } from \"../wiki/search.js\";\n\n/** A single cached hit: the BM25 search result plus the page body. */\nexport interface CachedHit {\n hit: SearchHit;\n /** Full page body (frontmatter stripped). */\n body: string;\n /** Wiki-relative path. */\n relativePath: string;\n /** True if the body was clamped during pre-fetch. */\n truncated: boolean;\n}\n\n/** State persisted between `query_progressive` and `query_expand` calls. */\nexport interface ProgressiveEntry {\n /** Original natural-language question. */\n question: string;\n /** Ranked BM25 hits with pre-fetched bodies. */\n hits: CachedHit[];\n /**\n * Highest tier the caller has consumed (0-3). `query_expand` starts at\n * `lastTierServed + 1`. The progressive-query handler updates this on\n * every successful return.\n */\n lastTierServed: number;\n /** Tokens already shipped to the caller (sum across tiers). */\n tokensShippedSoFar: number;\n /** Cap on the total tokens this conversation may consume. */\n maxTokensTotal: number;\n /** ISO timestamp the entry was created. */\n createdAt: string;\n /** Last access timestamp (ms). Updated on every touch for LRU semantics. */\n lastAccessMs: number;\n}\n\nconst TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst MAX_ENTRIES = 100;\n\n/** Map of continuation_token → entry. */\ntype CacheMap = Map<string, ProgressiveEntry>;\n\nexport class ProgressiveCache {\n private readonly entries: CacheMap = new Map();\n private readonly ttlMs: number;\n private readonly maxEntries: number;\n\n constructor(opts: { ttlMs?: number; maxEntries?: number } = {}) {\n this.ttlMs = opts.ttlMs ?? TTL_MS;\n this.maxEntries = opts.maxEntries ?? MAX_ENTRIES;\n }\n\n /**\n * Insert a new entry. Returns the freshly-generated continuation_token\n * the caller passes back to `query_expand`.\n */\n put(entry: Omit<ProgressiveEntry, \"createdAt\" | \"lastAccessMs\">): string {\n this.evictExpired();\n if (this.entries.size >= this.maxEntries) this.evictLru();\n const token = randomUUID();\n const now = Date.now();\n this.entries.set(token, {\n ...entry,\n createdAt: new Date(now).toISOString(),\n lastAccessMs: now,\n });\n return token;\n }\n\n /**\n * Fetch an entry by continuation_token. Touches its last-access stamp so\n * LRU eviction reflects actual usage. Returns null if missing or expired.\n */\n get(token: string): ProgressiveEntry | null {\n const entry = this.entries.get(token);\n if (!entry) return null;\n if (Date.now() - entry.lastAccessMs > this.ttlMs) {\n this.entries.delete(token);\n return null;\n }\n entry.lastAccessMs = Date.now();\n return entry;\n }\n\n /** Replace an existing entry (e.g. after advancing the tier). */\n update(token: string, mutator: (entry: ProgressiveEntry) => void): boolean {\n const entry = this.get(token);\n if (!entry) return false;\n mutator(entry);\n entry.lastAccessMs = Date.now();\n return true;\n }\n\n /** Drop an entry explicitly. No-op if not present. */\n delete(token: string): void {\n this.entries.delete(token);\n }\n\n /** Number of live (non-expired) entries. */\n size(): number {\n this.evictExpired();\n return this.entries.size;\n }\n\n /** Drop every expired entry. */\n private evictExpired(): void {\n const cutoff = Date.now() - this.ttlMs;\n for (const [token, entry] of this.entries) {\n if (entry.lastAccessMs < cutoff) this.entries.delete(token);\n }\n }\n\n /** Drop the least-recently-accessed entry. */\n private evictLru(): void {\n let oldestToken: string | null = null;\n let oldestStamp = Number.POSITIVE_INFINITY;\n for (const [token, entry] of this.entries) {\n if (entry.lastAccessMs < oldestStamp) {\n oldestStamp = entry.lastAccessMs;\n oldestToken = token;\n }\n }\n if (oldestToken) this.entries.delete(oldestToken);\n }\n}\n","/**\n * MCP server subsystem. Owns an HTTP server (node:http) and creates a\n * fresh McpServer + StreamableHTTPServerTransport per request.\n *\n * Why a fresh transport per request? The MCP SDK's streamable HTTP transport\n * enforces a hard invariant in stateless mode (`sessionIdGenerator: undefined`):\n * the transport cannot be reused across requests, because message IDs would\n * collide between concurrent clients. The SDK throws the moment you hand the\n * same transport a second request. To support multiple CLI clients hitting\n * the daemon concurrently we therefore construct the MCP server fresh for\n * each /mcp call. All the heavy state (wiki store, search index, query\n * engine, cost tracker) is shared across those short-lived McpServer\n * instances via the ToolRegistrationContext.\n */\nimport {\n createServer,\n type Server as HttpServer,\n type IncomingMessage,\n type ServerResponse,\n} from \"node:http\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport { safeFetchToFile, SafeFetchError } from \"./safe-fetch.js\";\n\n/**\n * Constant-time string comparison. Throws-on-length-mismatch in\n * timingSafeEqual is itself a timing oracle; we normalize lengths\n * to a fixed buffer first.\n */\nfunction constantTimeEqual(a: string, b: string): boolean {\n if (typeof a !== \"string\" || typeof b !== \"string\") return false;\n if (a.length !== b.length) {\n // Burn fixed time regardless to avoid length-oracle.\n try {\n timingSafeEqual(Buffer.from(a.padEnd(64, \"\\0\")), Buffer.from(b.padEnd(64, \"\\0\")));\n } catch {\n /* ignore */\n }\n return false;\n }\n return timingSafeEqual(Buffer.from(a), Buffer.from(b));\n}\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { looksLikePortInUse, portInUseError } from \"../utils/actionable-error.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { DaemonSubsystem } from \"../daemon/index.js\";\nimport type { RuntimeMode, WotwConfig } from \"../utils/types.js\";\nimport { VERSION } from \"../utils/version.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport type { IndexManager } from \"../wiki/index-manager.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport type { DeadLetterQueue } from \"../ingestion/dead-letter.js\";\nimport type { ModelRouter } from \"../ingestion/model-router.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport type { CompoundingEngine } from \"../compounding/engine.js\";\nimport { TokenStore } from \"../multi-user/token-store.js\";\nimport { QueryEngine } from \"./query-engine.js\";\nimport { registerTools } from \"./tools.js\";\nimport { registerResources } from \"./resources.js\";\nimport { RateLimiter, runMiddleware } from \"./middleware.js\";\nimport { loadAllPages } from \"../ingestion/wiki-writer.js\";\nimport { ProgressiveCache } from \"./progressive-cache.js\";\nimport type { FactStore } from \"../facts/store.js\";\nimport type { FactIndex } from \"../facts/index-manager.js\";\n\nexport interface McpServerOptions {\n config: WotwConfig;\n store: WikiStore;\n indexManager: IndexManager;\n search: WikiSearch;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n provenance?: ProvenanceChain | null;\n compounding?: CompoundingEngine | null;\n /**\n * Resolved runtime mode. Forwarded to the internal QueryEngine so that\n * MCP query tool calls dispatch through the CLI binary in CLI mode.\n */\n runtimeMode?: RuntimeMode;\n /**\n * Optional dead-letter sink. Exposed through `get_stats` so MCP clients\n * can see the count of permanently-failed batches.\n */\n deadLetter?: DeadLetterQueue | null;\n /**\n * Pass B fact-extraction sidecar. When both are provided, the daemon\n * registers `query_facts` and routes the Group A tools (define /\n * relate / cite_sources) through the fact layer first.\n */\n factStore?: FactStore | null;\n factIndex?: FactIndex | null;\n}\n\n/**\n * Daemon subsystem that wraps an MCP server bound to an HTTP transport.\n */\nexport class McpHttpServer implements DaemonSubsystem {\n readonly name = \"mcp-server\";\n private readonly opts: McpServerOptions;\n private httpServer: HttpServer | null = null;\n private limiter: RateLimiter;\n private queryEngine: QueryEngine;\n private tokenStore: TokenStore | null = null;\n /**\n * Continuation cache for `query_progressive` / `query_expand`. Lives on\n * the long-lived McpHttpServer (not per-request) so a client's tier-0\n * call and follow-up tier-1 expand call hit the same cache, even though\n * each /mcp request constructs a fresh McpServer instance.\n */\n private progressiveCache: ProgressiveCache;\n /** Set of in-flight transports so we can clean up on shutdown. */\n private inFlight: Set<StreamableHTTPServerTransport> = new Set();\n\n constructor(opts: McpServerOptions) {\n this.opts = opts;\n this.limiter = new RateLimiter(opts.config.server.rate_limit_rpm);\n this.queryEngine = new QueryEngine({\n config: opts.config,\n store: opts.store,\n search: opts.search,\n costTracker: opts.costTracker,\n modelRouter: opts.modelRouter,\n provenance: opts.provenance ?? null,\n runtimeMode: opts.runtimeMode,\n });\n this.progressiveCache = new ProgressiveCache();\n if (opts.config.multi_user.enabled) {\n this.tokenStore = new TokenStore({\n workspacesDir: opts.config.multi_user.workspaces_dir,\n });\n this.tokenStore.load();\n }\n }\n\n /** Expose the token store for CLI admin commands. */\n getTokenStore(): TokenStore | null {\n return this.tokenStore;\n }\n\n async start(): Promise<void> {\n const log = getLogger(\"mcp-server\");\n const {\n host,\n port,\n auth_token: authToken,\n rate_limit_rpm: rateLimit,\n } = this.opts.config.server;\n if (this.tokenStore) {\n log.info({ users: this.tokenStore.size() }, \"multi-user mode enabled\");\n }\n\n // M-SEC-2: When BOTH auth paths are disabled (no single-token and no\n // multi-user token store), the server accepts every request. That is\n // documented as intentional for a trusted localhost-only setup, but:\n // (a) it deserves a loud WARN banner at startup so the operator\n // knows they are running with no authentication, and\n // (b) we must refuse to start at all if the operator has also\n // bound the server to a non-loopback address. Exposing an\n // unauthenticated wiki to the LAN by silently combining those\n // two config options would be a footgun.\n const authDisabled = !authToken && !this.tokenStore;\n if (authDisabled) {\n const isLoopback = isLoopbackHost(host);\n if (!isLoopback) {\n const msg =\n `refusing to start mcp server: auth is disabled ` +\n `(server.auth_token is unset and multi_user.enabled is false) ` +\n `but server.host is \"${host}\", which is not a loopback address. ` +\n `Either (1) set server.auth_token to a secret value, ` +\n `(2) enable multi_user and create tokens with \\`wotw user add\\`, ` +\n `or (3) bind to 127.0.0.1 / ::1 if you truly want a no-auth server. ` +\n `See docs/mcp-tools.md for the trade-offs.`;\n log.error({ host }, msg);\n throw new Error(msg);\n }\n log.warn(\n { host, port },\n \"⚠️ MCP SERVER IS UNAUTHENTICATED. \" +\n \"server.auth_token is unset and multi_user.enabled is false — \" +\n \"any process that can reach this loopback address can read your \" +\n \"wiki without credentials. Set server.auth_token or enable \" +\n \"multi_user to require a bearer token.\",\n );\n }\n\n this.httpServer = createServer((req, res) => {\n // Fire-and-forget: errors are handled inside handleRequest.\n void this.handleRequest(req, res, { authToken, rateLimit }).catch((err) => {\n log.error({ err, url: req.url }, \"unhandled request error\");\n if (!res.headersSent) {\n try {\n res.writeHead(500, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"internal error\" }));\n } catch {\n /* socket already torn down */\n }\n }\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const onError = (err: unknown): void => {\n if (looksLikePortInUse(err)) {\n reject(portInUseError(port, err));\n return;\n }\n reject(err instanceof Error ? err : new Error(String(err)));\n };\n this.httpServer?.once(\"error\", onError);\n this.httpServer?.listen(port, host, () => {\n this.httpServer?.off(\"error\", onError);\n log.info({ host, port }, \"mcp server listening\");\n resolve();\n });\n });\n }\n\n async stop(): Promise<void> {\n const log = getLogger(\"mcp-server\");\n log.info(\"stopping mcp server\");\n // Close any in-flight transports so their sockets unblock.\n for (const t of this.inFlight) {\n try {\n await t.close();\n } catch {\n /* ignore */\n }\n }\n this.inFlight.clear();\n if (this.httpServer) {\n await new Promise<void>((resolve) => {\n this.httpServer?.close(() => resolve());\n });\n this.httpServer = null;\n }\n }\n\n /** Expose the query engine so other callers can reuse it. */\n getQueryEngine(): QueryEngine {\n return this.queryEngine;\n }\n\n private async handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n opts: { authToken: string | null; rateLimit: number },\n ): Promise<void> {\n const log = getLogger(\"mcp-server\");\n // Route: /healthz, /mcp, else 404.\n if (!req.url) {\n res.writeHead(404).end();\n return;\n }\n const url = new URL(req.url, `http://${req.headers.host ?? \"127.0.0.1\"}`);\n if (url.pathname === \"/healthz\") {\n res.writeHead(200, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, name: \"watcher-on-the-wall\", version: VERSION }));\n return;\n }\n // Internal admin endpoints — hosted mode only\n if (url.pathname.startsWith(\"/internal/\")) {\n await this.handleInternalRequest(url.pathname, req, res);\n return;\n }\n if (url.pathname !== \"/mcp\") {\n res.writeHead(404, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"not found\" }));\n return;\n }\n\n const mw = runMiddleware(\n req,\n res,\n {\n authToken: opts.authToken,\n tokenStore: this.tokenStore,\n rateLimitRpm: opts.rateLimit,\n trustProxy: this.opts.config.server.trust_proxy,\n },\n this.limiter,\n );\n if (!mw.ok) return;\n // Attach principal for downstream visibility (e.g. provenance metadata).\n // Currently advisory only; tool implementations can read it via the\n // context registered for this request if they care.\n if (mw.principal) {\n log.debug({ user: mw.principal.user }, \"authenticated request\");\n }\n\n // Parse body once, then hand it to the transport as a pre-parsed body\n // (the SDK accepts this to avoid double-reading the request stream).\n let body: unknown = undefined;\n if (req.method === \"POST\") {\n try {\n body = await readJsonBody(req);\n } catch (err) {\n log.warn({ err }, \"failed to parse request body\");\n res.writeHead(400, { \"content-type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32700, message: \"Parse error\" },\n id: null,\n }),\n );\n return;\n }\n }\n\n // Build a fresh McpServer + Transport for this single request.\n // This is mandatory in stateless mode — the SDK throws on reuse.\n const mcpServer = new McpServer(\n { name: \"watcher-on-the-wall\", version: VERSION },\n { capabilities: { tools: {}, resources: {} } },\n );\n registerTools(mcpServer, {\n config: this.opts.config,\n store: this.opts.store,\n indexManager: this.opts.indexManager,\n search: this.opts.search,\n queryEngine: this.queryEngine,\n costTracker: this.opts.costTracker,\n provenance: this.opts.provenance ?? null,\n compounding: this.opts.compounding ?? null,\n deadLetter: this.opts.deadLetter ?? null,\n progressiveCache: this.progressiveCache,\n factStore: this.opts.factStore ?? null,\n factIndex: this.opts.factIndex ?? null,\n });\n registerResources(mcpServer, {\n config: this.opts.config,\n indexManager: this.opts.indexManager,\n });\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // stateless\n });\n this.inFlight.add(transport);\n\n // Ensure cleanup when the connection ends, regardless of path taken.\n const cleanup = async (): Promise<void> => {\n this.inFlight.delete(transport);\n try {\n await transport.close();\n } catch {\n /* ignore */\n }\n try {\n await mcpServer.close();\n } catch {\n /* ignore */\n }\n };\n res.on(\"close\", () => {\n void cleanup();\n });\n\n try {\n await mcpServer.connect(transport);\n await transport.handleRequest(req, res, body);\n } catch (err) {\n log.error({ err, method: req.method, url: req.url }, \"mcp request failed\");\n if (!res.headersSent) {\n // Review item 54: don't embed errMsg(err) in the JSON-RPC error\n // message. SDK error shapes can carry headers / paths / tokens\n // that leak via the response body. Log the structured detail;\n // return a generic message.\n log.error({ err }, \"MCP request failed\");\n res.writeHead(500, { \"content-type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal error\" },\n id: null,\n }),\n );\n }\n await cleanup();\n }\n }\n\n /**\n * Handle /internal/* admin endpoints. These are only useful in hosted\n * mode — they let the cloud control plane inspect queue state, trigger\n * index rebuilds, health checks, export/import. Authenticated via\n * x-admin-key header matching ADMIN_SERVICE_KEY env var.\n */\n private async handleInternalRequest(\n pathname: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> {\n const log = getLogger(\"internal-api\");\n // Review items 50 + 59: prefer the dedicated internal-admin secret\n // (rip-and-replace per G4 with tenant_count=0); fall back to\n // ADMIN_SERVICE_KEY only while wotw-cloud is being migrated to the\n // split-secret scheme. Compare in constant time to close the timing\n // oracle the original `!==` opened (item 59).\n const adminKey = process.env.WOTW_INTERNAL_ADMIN_KEY ?? process.env.ADMIN_SERVICE_KEY;\n const providedRaw = req.headers[\"x-admin-key\"];\n const providedKey = typeof providedRaw === \"string\" ? providedRaw : \"\";\n if (!adminKey || !constantTimeEqual(providedKey, adminKey)) {\n res.writeHead(401, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"unauthorized\" }));\n return;\n }\n\n // Parse JSON body for POST requests\n let body: Record<string, unknown> = {};\n if (req.method === \"POST\") {\n try {\n body = (await readJsonBody(req)) as Record<string, unknown>;\n } catch {\n res.writeHead(400, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"invalid JSON body\" }));\n return;\n }\n }\n\n const json = (status: number, data: unknown): void => {\n res.writeHead(status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(data));\n };\n\n // Review item 55: every /internal/* endpoint accepted body.tenant_id\n // and trusted it for logging/routing without verifying it matches\n // the daemon's own config.hosted.tenant_id. A misrouted control-\n // plane call would execute against the wrong tenant. Verify once\n // here so downstream handlers can trust body.tenant_id.\n const expectedTenant = this.opts.config.hosted.tenant_id;\n const providedTenant = typeof body.tenant_id === \"string\" ? body.tenant_id : null;\n if (providedTenant !== null && expectedTenant !== null && providedTenant !== expectedTenant) {\n log.warn(\n { providedTenant, expectedTenant, pathname },\n \"internal: rejected tenant_id mismatch\",\n );\n json(403, { error: \"tenant_id mismatch\" });\n return;\n }\n\n try {\n switch (pathname) {\n case \"/internal/queue-status\": {\n // Only meaningful if TenantScheduler is active (import at queue level)\n json(200, {\n note: \"queue-status available when hosted mode is active\",\n hosted: this.opts.config.hosted.enabled,\n });\n break;\n }\n\n case \"/internal/rebuild-index\": {\n log.info({ tenantId: body.tenant_id }, \"rebuilding search index\");\n const startMs = Date.now();\n const pages = await loadAllPages(this.opts.store);\n this.opts.search.rebuild(pages);\n const duration = Date.now() - startMs;\n json(200, { pages_indexed: pages.length, duration_ms: duration });\n break;\n }\n\n case \"/internal/health-check\": {\n log.info({ tenantId: body.tenant_id }, \"running health check\");\n const allPages = await loadAllPages(this.opts.store);\n const orphaned = allPages.filter((p) => p.frontmatter?.status === \"orphaned\").length;\n\n json(200, {\n total_pages: allPages.length,\n orphaned_pages: orphaned,\n tenant_id: body.tenant_id,\n });\n break;\n }\n\n case \"/internal/verify\": {\n // G5 closure (Pass 018, v0.8.2): full provenance chain verify.\n // Walks every record; recomputes id + chain_hash; validates\n // HMAC via KeyStore lookup (or 4-tier fallback for pre-v0.8.2\n // records). Returns the same VerificationResult shape that\n // ProvenanceChain.verify() produces. Surface contract frozen\n // for the future wotw-verify Go CLI (CT5.01, separate pass).\n //\n // Errors are returned in the body (200 with ok:false), not as\n // HTTP errors — a \"the chain is broken\" response is still a\n // successful call to the endpoint. HTTP 503 only if provenance\n // is disabled / unavailable on this daemon.\n if (!this.opts.provenance) {\n json(503, {\n error: \"provenance_disabled\",\n note: \"this daemon has provenance.enabled=false\",\n });\n break;\n }\n log.info({ tenantId: body.tenant_id }, \"running provenance chain verify\");\n const startMs = Date.now();\n const result = await this.opts.provenance.verify();\n const durationMs = Date.now() - startMs;\n json(200, {\n ok: result.ok,\n total_records: result.totalRecords,\n verified_records: result.verifiedRecords,\n errors: result.errors,\n duration_ms: durationMs,\n });\n break;\n }\n\n case \"/internal/export\": {\n // Review item 56: pre-fix returned 200 \"acknowledged\" but did\n // NO work. A control plane that trusts the response would mark\n // the tenant exported when no export happened. Return 501 so\n // callers cannot mistake a stub for a completed export.\n log.warn({ tenantId: body.tenant_id }, \"export requested — not implemented\");\n json(501, {\n error: \"not_implemented\",\n note: \"tenant export is performed by the wotw-cloud layer, not the daemon\",\n });\n break;\n }\n\n case \"/internal/import\": {\n // Review item 56: same shape as /internal/export — pre-fix\n // returned 200 with no work. 501 so callers cannot mistake a\n // stub for a completed restore. Body fields (backup_path)\n // are explicitly NOT echoed in the response (X4 footnote on\n // S8-F-006).\n log.warn({ tenantId: body.tenant_id }, \"import requested — not implemented\");\n json(501, {\n error: \"not_implemented\",\n note: \"tenant import is performed by the wotw-cloud layer, not the daemon\",\n });\n break;\n }\n\n case \"/internal/ingest\": {\n // Pass 012 — file-upload → daemon-ingestion sync.\n //\n // wotw-cloud's /api/sources/trigger-ingest POSTs here after a user\n // uploads a file to Supabase Storage. We download the file from\n // the signed URL into the configured raw_path; chokidar then\n // picks it up via the existing FileWatcher → IngestionQueue\n // pipeline, which writes provenance records that cloud-sink\n // mirrors back to Supabase (Pass 010 work). Cloud-side maps\n // source_files → raw_source_id via the filename prefix.\n //\n // Fire-and-forget shape: this endpoint returns 202 once the file\n // is on disk and chokidar can see it. Ingestion completion is\n // signaled asynchronously via the provenance sink.\n const rawSourceId = body.raw_source_id;\n const signedUrl = body.signed_url;\n const filename = body.filename;\n if (typeof rawSourceId !== \"string\" || !rawSourceId) {\n json(400, { error: \"raw_source_id required\" });\n break;\n }\n // Review item 57: length + charset bounds on raw_source_id +\n // filename so filesystem NAME_MAX truncation can't collide\n // distinct uploads onto the same on-disk name, and Unicode\n // normalization can't smuggle a `../` past the basename check.\n if (rawSourceId.length > 64 || !/^[a-zA-Z0-9_-]+$/.test(rawSourceId)) {\n json(400, { error: \"raw_source_id must be ≤64 chars [A-Za-z0-9_-]\" });\n break;\n }\n if (typeof signedUrl !== \"string\" || !signedUrl.startsWith(\"https://\")) {\n json(400, { error: \"signed_url required (must be https://...)\" });\n break;\n }\n if (typeof filename !== \"string\" || !filename) {\n json(400, { error: \"filename required\" });\n break;\n }\n // Review item 57: bound filename to avoid NAME_MAX truncation\n // collision (most Linux fs's truncate at 255 bytes). Allow\n // common printable ASCII but reject control chars + slashes.\n if (filename.length > 200) {\n json(400, { error: \"filename must be ≤200 chars\" });\n break;\n }\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(filename)) {\n json(400, { error: \"filename must not contain control characters\" });\n break;\n }\n // Disallow path separators / traversal in filename.\n if (\n filename.includes(\"/\") ||\n filename.includes(\"\\\\\") ||\n filename.includes(\"..\") ||\n filename.startsWith(\".\")\n ) {\n json(400, { error: \"filename must be a clean basename\" });\n break;\n }\n\n const rawPath = this.opts.config.raw_path;\n // Prefix the raw_source_id so the cloud-sink can map provenance\n // records' source_files back to the originating raw_sources row.\n const targetName = `${rawSourceId}-${filename}`;\n const resolvedRawPath = resolve(rawPath);\n const targetPath = resolve(join(resolvedRawPath, targetName));\n // Defense-in-depth: ensure the resolved target lives inside raw_path.\n if (targetPath !== resolvedRawPath && !targetPath.startsWith(resolvedRawPath + sep)) {\n json(400, { error: \"target path escapes raw_path\" });\n break;\n }\n\n // Pre-create the parent directory so safeFetchToFile can stream\n // straight into it.\n try {\n await mkdir(dirname(targetPath), { recursive: true });\n } catch (err) {\n log.error(\n { err: err instanceof Error ? err.message : String(err), rawSourceId },\n \"ingest: mkdir failed\",\n );\n json(500, { error: \"internal\" });\n break;\n }\n\n try {\n // SSRF-hardened fetch (review items 49 + 52 + 53 + 58).\n // Layered defenses per X4-C-1: hostname allowlist + DNS\n // private-IP rejection + content-length cap + streaming with\n // byte-count cap + redirect:\"error\" + AbortSignal timeout.\n const result = await safeFetchToFile(signedUrl, {\n targetPath,\n timeoutMs: 30_000,\n maxBytes: 32 * 1024 * 1024,\n // Supabase Storage and S3 presigned URLs are the only\n // upstream sources the daemon trusts. The allowlist is\n // substring-matched to cover storage.<project>.supabase.co,\n // <bucket>.s3.<region>.amazonaws.com, and the\n // amazonaws.com host shape S3 presigned URLs use.\n hostnameAllowlist: [\".supabase.co\", \".s3.amazonaws.com\", \".amazonaws.com\"],\n });\n log.info(\n {\n rawSourceId,\n filename,\n bytes: result.bytes,\n contentType: result.contentType,\n targetName,\n },\n \"ingest: file landed in raw_path\",\n );\n json(202, {\n status: \"accepted\",\n raw_source_id: rawSourceId,\n target_name: targetName,\n bytes: result.bytes,\n });\n } catch (err) {\n // Never echo internal hostnames / paths / upstream errors\n // back to the caller — they're a path/host enumeration\n // oracle (review item 53). Log the structured detail; return\n // a code-only error to the caller.\n const code = err instanceof SafeFetchError ? err.code : \"WRITE_FAILED\";\n log.warn(\n {\n rawSourceId,\n filename,\n code,\n err: err instanceof Error ? err.message : String(err),\n },\n \"ingest: download rejected\",\n );\n const status =\n code === \"UPSTREAM_NON_2XX\"\n ? 502\n : code === \"FETCH_TIMEOUT\"\n ? 504\n : code === \"PRIVATE_IP_BLOCKED\" ||\n code === \"HOSTNAME_NOT_ALLOWED\" ||\n code === \"INVALID_URL\" ||\n code === \"INVALID_SCHEME\"\n ? 400\n : code === \"CONTENT_LENGTH_TOO_LARGE\" || code === \"CONTENT_LENGTH_DURING_STREAM\"\n ? 413\n : code === \"CONTENT_TYPE_NOT_ALLOWED\"\n ? 415\n : 500;\n json(status, { error: \"ingest_rejected\", code });\n }\n break;\n }\n\n default: {\n json(404, { error: `unknown internal endpoint: ${pathname}` });\n }\n }\n } catch (err) {\n log.error({ err, pathname }, \"internal endpoint error\");\n json(500, { error: \"internal error\" });\n }\n }\n}\n\n/**\n * True if `host` is a loopback address / hostname. Used by the M-SEC-2\n * refuse-to-start check. We only accept the canonical loopback forms;\n * anything else (including `0.0.0.0`, LAN IPs, public IPs, or random\n * hostnames) is treated as externally reachable.\n */\nfunction isLoopbackHost(host: string): boolean {\n if (host === \"127.0.0.1\") return true;\n if (host === \"::1\") return true;\n if (host === \"localhost\") return true;\n // `127.0.0.0/8` is all loopback on most platforms.\n if (/^127(?:\\.\\d{1,3}){3}$/.test(host)) return true;\n return false;\n}\n\n/** Read a JSON request body (up to 4MB). */\nasync function readJsonBody(req: IncomingMessage): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let total = 0;\n const MAX = 4 * 1024 * 1024;\n req.on(\"data\", (chunk: Buffer) => {\n total += chunk.length;\n if (total > MAX) {\n reject(new Error(\"body too large\"));\n req.destroy();\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (chunks.length === 0) return resolve(undefined);\n try {\n resolve(JSON.parse(Buffer.concat(chunks).toString(\"utf8\")));\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n req.on(\"error\", (err) => reject(err));\n });\n}\n\nexport { QueryEngine } from \"./query-engine.js\";\nexport { registerTools } from \"./tools.js\";\nexport { registerResources } from \"./resources.js\";\n","/**\n * Compounding engine. Periodically (or on demand) the daemon can run a\n * synthesis pass: look at the accumulated wiki, find clusters of related\n * pages, and ask the agent to write higher-level synthesis pages that\n * weave them together.\n *\n * Design philosophy:\n * - Never rewrite existing pages — synthesis is always additive and\n * lands under wiki/syntheses/.\n * - Cluster detection is a cheap structural heuristic (shared tags and\n * frontmatter `related` edges). The expensive LLM work only runs on\n * the clusters that clear the minimum-size threshold.\n * - Budget-gated via CostTracker like the ingestion queue.\n * - Every synthesis is recorded in the provenance chain as an operation\n * of type \"compound\".\n */\nimport { join, relative } from \"node:path\";\nimport { errMsg } from \"../utils/errors.js\";\nimport { getLogger } from \"../utils/logger.js\";\nimport type { RuntimeMode, WotwConfig, WikiPage } from \"../utils/types.js\";\nimport type { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport { runtimeAwareComplete } from \"../llm/runtime-aware.js\";\nimport type { ModelRouter } from \"../ingestion/model-router.js\";\nimport { loadAllPages } from \"../ingestion/wiki-writer.js\";\nimport { newPage } from \"../wiki/page.js\";\nimport type { WikiStore } from \"../wiki/store.js\";\nimport type { IndexManager } from \"../wiki/index-manager.js\";\nimport type { WikiSearch } from \"../wiki/search.js\";\nimport { repairBidirectionalLinks } from \"../wiki/cross-reference.js\";\nimport { commitWikiChanges } from \"../ingestion/git-committer.js\";\nimport type { ProvenanceChain } from \"../provenance/chain.js\";\nimport { sha256File, sha256Files, sha256Hex } from \"../provenance/hash.js\";\n\n/** Per-source body cap in bytes (clamps prompt size). */\nconst MAX_SOURCE_BODY_BYTES = 16 * 1024;\n/** Max tokens for the synthesis body response. */\nconst SYNTHESIS_MAX_TOKENS = 8192;\n\nexport interface CompoundingEngineOptions {\n config: WotwConfig;\n store: WikiStore;\n indexManager: IndexManager;\n search: WikiSearch;\n costTracker: CostTracker;\n modelRouter: ModelRouter;\n provenance?: ProvenanceChain | null;\n /**\n * Resolved runtime mode. Defaults to \"api\" so legacy callers and test rigs\n * keep working without change. When set to \"cli\" the engine spawns the\n * `claude` binary for every cluster and logs cost=0 (subscription-covered).\n */\n runtimeMode?: RuntimeMode;\n}\n\nexport interface CompoundingOutcome {\n skipped: boolean;\n skipReason?: string;\n clusters: ClusterSummary[];\n pagesWritten: number;\n costUsd: number;\n gitSha: string | null;\n durationMs: number;\n}\n\nexport interface ClusterSummary {\n tag: string;\n pages: string[];\n synthesisPath: string | null;\n skipped: boolean;\n reason?: string;\n}\n\n/** In-memory representation of a candidate synthesis cluster. */\ninterface Cluster {\n tag: string;\n pages: WikiPage[];\n}\n\nexport class CompoundingEngine {\n private readonly opts: CompoundingEngineOptions;\n\n constructor(opts: CompoundingEngineOptions) {\n this.opts = opts;\n }\n\n /**\n * Run a single compounding pass. Returns the list of clusters that were\n * considered and the synthesis pages that were written (if any).\n */\n async synthesize(): Promise<CompoundingOutcome> {\n const log = getLogger(\"compounding\");\n const started = Date.now();\n const runtimeMode: RuntimeMode = this.opts.runtimeMode ?? \"api\";\n\n if (!this.opts.config.compounding.enabled) {\n return {\n skipped: true,\n skipReason: \"compounding disabled in config\",\n clusters: [],\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n }\n\n // Global budget pre-flight — use the ingest budget as a ceiling since\n // compounding is qualitatively similar to ingestion (agent writes pages).\n // CLI mode is subscription-covered, so the model resolves to cli_model\n // and the budget check is skipped.\n const model =\n runtimeMode === \"cli\"\n ? this.opts.config.execution.cli_model\n : this.opts.modelRouter.modelFor(\"compound_eval\");\n const estimate =\n runtimeMode === \"cli\" ? 0 : this.opts.modelRouter.computeCost(model, 20_000, 6_000);\n if (runtimeMode !== \"cli\" && this.opts.costTracker.wouldExceedDaily(estimate)) {\n return {\n skipped: true,\n skipReason: \"daily budget exceeded\",\n clusters: [],\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n }\n\n const pages = await loadAllPages(this.opts.store);\n if (pages.length < this.opts.config.compounding.min_source_pages) {\n return {\n skipped: true,\n skipReason: `wiki has ${pages.length} pages, minimum is ${this.opts.config.compounding.min_source_pages}`,\n clusters: [],\n pagesWritten: 0,\n costUsd: 0,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n }\n\n const clusters = this.findClusters(pages);\n log.info({ clusters: clusters.length }, \"candidate clusters\");\n\n const outcomes: ClusterSummary[] = [];\n const writtenAbsPaths: string[] = [];\n let totalCostUsd = 0;\n\n for (const cluster of clusters) {\n // Skip clusters that already have a synthesis covering all their pages.\n if (this.hasExistingSynthesis(cluster, pages)) {\n outcomes.push({\n tag: cluster.tag,\n pages: cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path)),\n synthesisPath: null,\n skipped: true,\n reason: \"existing synthesis covers this cluster\",\n });\n continue;\n }\n\n // Per-cluster budget check so one expensive cluster doesn't torch the day.\n // (Skipped in CLI mode — no per-call billing.)\n if (runtimeMode !== \"cli\" && this.opts.costTracker.wouldExceedDaily(estimate)) {\n outcomes.push({\n tag: cluster.tag,\n pages: cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path)),\n synthesisPath: null,\n skipped: true,\n reason: \"budget exceeded mid-run\",\n });\n continue;\n }\n\n try {\n const result = await this.synthesizeCluster(cluster, model, runtimeMode);\n if (result) {\n totalCostUsd += result.costUsd;\n if (result.writtenPath) writtenAbsPaths.push(result.writtenPath);\n outcomes.push({\n tag: cluster.tag,\n pages: cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path)),\n synthesisPath: result.writtenPath\n ? relative(this.opts.config.wiki_root, result.writtenPath)\n : null,\n skipped: result.writtenPath === null,\n reason: result.writtenPath === null ? \"agent wrote no synthesis file\" : undefined,\n });\n\n this.opts.costTracker.logUsage({\n operation: \"compound\",\n model,\n costUsd: result.costUsd,\n });\n }\n } catch (err) {\n log.error({ err, tag: cluster.tag }, \"cluster synthesis failed\");\n outcomes.push({\n tag: cluster.tag,\n pages: cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path)),\n synthesisPath: null,\n skipped: true,\n reason: `error: ${errMsg(err)}`,\n });\n }\n }\n\n // If nothing was written we're done.\n if (writtenAbsPaths.length === 0) {\n return {\n skipped: false,\n clusters: outcomes,\n pagesWritten: 0,\n costUsd: totalCostUsd,\n gitSha: null,\n durationMs: Date.now() - started,\n };\n }\n\n // Post-write: rebuild links, search, and index; commit.\n const allPages = await loadAllPages(this.opts.store);\n const mutated = repairBidirectionalLinks(this.opts.store, allPages);\n for (const p of mutated) await this.opts.store.writePage(p);\n const finalPages = mutated.length > 0 ? await loadAllPages(this.opts.store) : allPages;\n await this.opts.indexManager.rebuild(finalPages);\n this.opts.search.rebuild(finalPages);\n\n // Provenance record.\n if (this.opts.provenance) {\n try {\n await this.recordProvenance({\n model,\n clusters: outcomes,\n writtenAbsPaths,\n costUsd: totalCostUsd,\n });\n } catch (err) {\n log.error({ err }, \"failed to append compound provenance\");\n }\n }\n\n // Git commit all the things.\n const commitPaths = [\n ...writtenAbsPaths,\n ...mutated.map((p) => p.path),\n `${this.opts.store.wikiDir}/index.md`,\n ...(this.opts.provenance ? [this.opts.provenance.path] : []),\n ];\n const commit = await commitWikiChanges({\n wikiRoot: this.opts.config.wiki_root,\n paths: [...new Set(commitPaths)],\n operationId: `compound-${Date.now()}`,\n operation: \"compound\",\n metadata: {\n clusters: outcomes.filter((o) => !o.skipped).length,\n pages_written: writtenAbsPaths.length,\n cost_usd: totalCostUsd.toFixed(6),\n model,\n },\n });\n\n return {\n skipped: false,\n clusters: outcomes,\n pagesWritten: writtenAbsPaths.length,\n costUsd: totalCostUsd,\n gitSha: commit.sha,\n durationMs: Date.now() - started,\n };\n }\n\n /**\n * Find candidate clusters. Current heuristic: group every page by each of\n * its tags; a cluster is a tag with >= min_source_pages pages. We filter\n * out 'source' pages since the interesting synthesis is over concepts\n * and entities, not raw inputs.\n */\n private findClusters(pages: WikiPage[]): Cluster[] {\n const minSize = this.opts.config.compounding.min_source_pages;\n const byTag = new Map<string, WikiPage[]>();\n for (const p of pages) {\n if (p.frontmatter.category === \"source\") continue;\n if (p.frontmatter.category === \"synthesis\") continue;\n for (const tag of p.frontmatter.tags ?? []) {\n if (!byTag.has(tag)) byTag.set(tag, []);\n byTag.get(tag)!.push(p);\n }\n }\n const clusters: Cluster[] = [];\n for (const [tag, tagPages] of byTag) {\n if (tagPages.length >= minSize) {\n clusters.push({ tag, pages: tagPages });\n }\n }\n // Deterministic ordering: largest clusters first, then alpha by tag.\n clusters.sort((a, b) => {\n if (b.pages.length !== a.pages.length) return b.pages.length - a.pages.length;\n return a.tag.localeCompare(b.tag);\n });\n return clusters;\n }\n\n /**\n * Check whether a synthesis page already exists that covers a superset\n * of this cluster. If yes, we skip to avoid duplicate work.\n */\n private hasExistingSynthesis(cluster: Cluster, pages: WikiPage[]): boolean {\n const clusterPaths = new Set(\n cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path)),\n );\n for (const p of pages) {\n if (p.frontmatter.category !== \"synthesis\") continue;\n const sources = new Set(p.frontmatter.sources ?? []);\n // An existing synthesis covers the cluster if it lists every clustered\n // page as one of its sources.\n let covers = true;\n for (const c of clusterPaths) {\n if (!sources.has(c)) {\n covers = false;\n break;\n }\n }\n if (covers) return true;\n }\n return false;\n }\n\n /**\n * Single-pass synthesis for a cluster. Pre-assembles full source page\n * bodies into the prompt; the model returns markdown body content only;\n * the daemon assembles frontmatter from the cluster metadata and writes\n * the synthesis page atomically via WikiStore.\n *\n * Frontmatter shape (daemon-assembled, not model-chosen):\n * - title = cluster.tag (verbatim)\n * - category = \"synthesis\"\n * - sources = wiki-relative paths of every clustered page\n * - tags = [cluster.tag]\n * - confidence = \"medium\"\n * - created/updated = today\n *\n * Source page bodies are clamped to MAX_SOURCE_BODY_BYTES (16KB) each.\n * Pages exceeding the cap get a `_[truncated]_` marker. With min-cluster\n * size 3 and typical pages 2-5KB, prompts stay well within model context.\n */\n private async synthesizeCluster(\n cluster: Cluster,\n model: string,\n runtimeMode: RuntimeMode,\n ): Promise<{ costUsd: number; writtenPath: string | null } | null> {\n const log = getLogger(\"compounding\");\n const slug = slugifyTag(cluster.tag);\n const outAbs = join(this.opts.store.categoryDir(\"synthesis\"), `${slug}.md`);\n const outRel = relative(this.opts.config.wiki_root, outAbs);\n const sourceRelPaths = cluster.pages.map((p) => relative(this.opts.config.wiki_root, p.path));\n\n const systemPrompt = [\n \"You are the watcher-on-the-wall compounding agent.\",\n \"You write synthesis pages that connect related wiki pages into higher-level insights.\",\n \"Rules:\",\n \" 1. Use ONLY the source page contents provided in the user message. Do not invent facts.\",\n \" 2. Return markdown body content ONLY. Do NOT include YAML frontmatter — the daemon writes it.\",\n \" 3. Cite each source inline using [[wiki-link]] syntax or [Title](path) markdown links.\",\n \" 4. If the sources don't support a claim, omit it.\",\n \" 5. Aim for a clear synthesis that identifies connections and higher-level themes, not a summary of each source in turn.\",\n ].join(\"\\n\");\n\n // Pre-assemble source page bodies. The model sees the complete content\n // it needs to synthesize from; no in-call Read tool required.\n const sourceSections = cluster.pages.map((p) => {\n const relPath = relative(this.opts.config.wiki_root, p.path);\n const truncated = p.body.length > MAX_SOURCE_BODY_BYTES;\n const body = truncated\n ? `${p.body.slice(0, MAX_SOURCE_BODY_BYTES)}\\n\\n_[truncated]_`\n : p.body;\n return [\n `## ${p.frontmatter.title}`,\n `path: ${relPath}`,\n `category: ${p.frontmatter.category}`,\n \"\",\n body,\n ].join(\"\\n\");\n });\n\n const userPrompt = [\n `# Synthesis request: tag \"${cluster.tag}\"`,\n \"\",\n `## Source pages (${cluster.pages.length})`,\n \"\",\n sourceSections.join(\"\\n\\n---\\n\\n\"),\n \"\",\n \"---\",\n \"Write a synthesis that connects these sources into higher-level insights.\",\n \"Output markdown body content ONLY — no YAML frontmatter, no surrounding code fences.\",\n \"Cite every source inline.\",\n ].join(\"\\n\");\n\n log.info(\n { tag: cluster.tag, pages: cluster.pages.length, outRel, runtimeMode },\n \"synthesizing cluster\",\n );\n\n const result = await runtimeAwareComplete(userPrompt, {\n systemPrompt,\n model,\n maxTokens: SYNTHESIS_MAX_TOKENS,\n config: this.opts.config,\n runtimeMode,\n });\n\n const bodyText = stripFrontmatterIfPresent(result.text).trim();\n if (!bodyText) {\n log.warn({ tag: cluster.tag }, \"model returned empty synthesis body\");\n return { costUsd: result.costUsd, writtenPath: null };\n }\n\n // Daemon assembles frontmatter from cluster metadata and writes the\n // synthesis page atomically. Title uses the cluster tag verbatim.\n const synthesisPage = newPage(outAbs, cluster.tag, \"synthesis\", bodyText, {\n sources: sourceRelPaths,\n tags: [cluster.tag],\n confidence: \"medium\",\n });\n try {\n await this.opts.store.writePage(synthesisPage);\n } catch (err) {\n log.error({ err, tag: cluster.tag, outAbs }, \"failed to write synthesis page\");\n return { costUsd: result.costUsd, writtenPath: null };\n }\n return { costUsd: result.costUsd, writtenPath: outAbs };\n }\n\n /** Append a provenance record describing this synthesis pass. */\n private async recordProvenance(args: {\n model: string;\n clusters: ClusterSummary[];\n writtenAbsPaths: string[];\n costUsd: number;\n }): Promise<void> {\n if (!this.opts.provenance) return;\n const wikiRoot = this.opts.config.wiki_root;\n const toRel = (abs: string): string => relative(wikiRoot, abs) || abs;\n\n // source_files: the union of every clustered source, wiki-relative.\n const sourceSet = new Set<string>();\n for (const c of args.clusters) {\n for (const p of c.pages) sourceSet.add(p);\n }\n const sourceFiles = [...sourceSet].sort();\n\n // source_hashes: hash the current on-disk content of each source (as\n // the synthesis was derived from this snapshot).\n const sourceHashes: string[] = [];\n for (const rel of sourceFiles) {\n const abs = join(wikiRoot, rel);\n const h = await sha256File(abs);\n sourceHashes.push(h ?? \"missing\");\n }\n\n // wiki_files_written: the synthesis pages produced.\n const hashesByAbs = await sha256Files(args.writtenAbsPaths);\n const wikiFileHashes: Record<string, string> = {};\n for (const abs of args.writtenAbsPaths) {\n const h = hashesByAbs[abs];\n if (h) wikiFileHashes[toRel(abs)] = h;\n }\n\n await this.opts.provenance.append({\n type: \"compound\",\n source_files: sourceFiles,\n source_hashes: sourceHashes,\n prompt_hash: sha256Hex(args.clusters.map((c) => `${c.tag}:${c.pages.join(\",\")}`).join(\"|\")),\n model_id: args.model,\n response_hash: sha256Hex(Object.values(wikiFileHashes).join(\"|\")),\n wiki_files_written: Object.keys(wikiFileHashes),\n wiki_file_hashes_after: wikiFileHashes,\n metadata: {\n cluster_count: args.clusters.length,\n pages_written: args.writtenAbsPaths.length,\n cost_usd: Number(args.costUsd.toFixed(6)),\n },\n });\n }\n}\n\n/**\n * Defensive frontmatter stripper. The prompt instructs the model NOT to\n * emit YAML frontmatter, but models sometimes ignore that. If the response\n * starts with `---\\n...\\n---` we strip it so the daemon-assembled\n * frontmatter is the only one present. Returns the body without\n * frontmatter; returns the input unchanged if no frontmatter detected.\n */\nfunction stripFrontmatterIfPresent(text: string): string {\n const trimmed = text.trimStart();\n if (!trimmed.startsWith(\"---\")) return text;\n const afterFirst = trimmed.slice(3);\n // Frontmatter must end with a newline before closing ---\n const closingMatch = afterFirst.match(/\\n---\\s*\\n/);\n if (!closingMatch || closingMatch.index === undefined) return text;\n return afterFirst.slice(closingMatch.index + closingMatch[0].length);\n}\n\n/** Convert a free-form tag into a safe filename slug. */\nfunction slugifyTag(tag: string): string {\n return (\n tag\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 64) || \"cluster\"\n );\n}\n","/**\n * CloudProvenanceSink — fire-and-forget HTTP sink that mirrors each\n * provenance record append to wotw-cloud's Supabase replica.\n *\n * JSONL on disk is the canonical chain. This sink is a sync-replica so\n * the user-facing /provenance UI in wotw-cloud has real data. Sink\n * failures (network, wotw-cloud down, validation) log but never throw —\n * the JSONL append already succeeded, the cloud replica catches up on\n * the next successful append's chain-hash linkage check.\n *\n * Configuration via hosted-mode env vars (all required for the sink to\n * be active; if any is missing, the daemon constructs without a sink\n * and JSONL-only operation continues — allows local dev and\n * interactive mode):\n *\n * WOTW_WIKI_ID — UUID of the wiki this daemon serves\n * WOTW_API_BASE_URL — base URL of wotw-cloud (default https://wotw.dev)\n * ADMIN_SERVICE_KEY — shared secret for /api/internal/* admin endpoints\n *\n * The sink POSTs to `${WOTW_API_BASE_URL}/api/internal/append-provenance`\n * with `x-admin-key: ${ADMIN_SERVICE_KEY}` and a JSON body of\n * `{ wiki_id, seq, record_id, chain_hash, operation_type, source_files,\n * wiki_files_written, model_id, timestamp, record_json }`.\n *\n * wotw-cloud's endpoint validates against the table's `unique (wiki_id,\n * seq)` constraint with ON CONFLICT DO NOTHING — so daemon retries\n * (network blip, restart mid-append) won't double-write.\n */\nimport type { ProvenanceRecord } from \"../utils/types.js\";\nimport { getLogger } from \"../utils/logger.js\";\n\nconst DEFAULT_API_BASE_URL = \"https://wotw.dev\";\nconst REQUEST_TIMEOUT_MS = 5_000;\n\nexport interface CloudSinkOptions {\n wikiId: string;\n apiBaseUrl?: string;\n adminServiceKey: string;\n /** Override for tests. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * The shape sent to wotw-cloud's /api/internal/append-provenance endpoint.\n * Mirrors the Supabase `provenance_records` table columns + the full\n * record under `record_json` for the verify-chain endpoint.\n */\nexport interface CloudSinkPayload {\n wiki_id: string;\n seq: number;\n record_id: string;\n chain_hash: string;\n operation_type: string;\n source_files: string[];\n wiki_files_written: string[];\n model_id: string | null;\n timestamp: string;\n record_json: ProvenanceRecord;\n}\n\nexport class CloudProvenanceSink {\n readonly wikiId: string;\n readonly apiBaseUrl: string;\n private readonly adminServiceKey: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: CloudSinkOptions) {\n this.wikiId = opts.wikiId;\n this.apiBaseUrl = opts.apiBaseUrl ?? DEFAULT_API_BASE_URL;\n // Review item 41: WOTW_API_BASE_URL must be https:// — otherwise the\n // admin key gets sent as plaintext `x-admin-key` over HTTP. Fail\n // closed at construction time rather than letting a misconfigured\n // tenant happily exfil the key on first append().\n if (!this.apiBaseUrl.startsWith(\"https://\")) {\n throw new Error(\n `cloud-sink: apiBaseUrl must be https:// (got \"${this.apiBaseUrl}\"); ` +\n `WOTW_API_BASE_URL is misconfigured. Refusing to send admin key over plaintext.`,\n );\n }\n this.adminServiceKey = opts.adminServiceKey;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n }\n\n /**\n * POST a single record to wotw-cloud. Returns a Promise that resolves\n * to true on success (HTTP 200 or 409-on-conflict) and false on\n * failure. NEVER throws — caller treats failure as logged-and-moved-on.\n */\n async append(record: ProvenanceRecord): Promise<boolean> {\n const log = getLogger(\"provenance.cloud-sink\");\n const url = `${this.apiBaseUrl}/api/internal/append-provenance`;\n\n const payload: CloudSinkPayload = {\n wiki_id: this.wikiId,\n seq: record.seq,\n record_id: record.id,\n chain_hash: record.chain_hash,\n operation_type: record.type,\n source_files: record.source_files,\n wiki_files_written: record.wiki_files_written,\n model_id: record.model_id,\n timestamp: record.timestamp,\n record_json: record,\n };\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n try {\n const res = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-admin-key\": this.adminServiceKey,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n // 200 = inserted, 409 = idempotent conflict (already present). Both\n // are \"the cloud knows about this record\"; treat as success.\n if (res.ok || res.status === 409) {\n log.debug(\n {\n seq: record.seq,\n id: record.id.slice(0, 12),\n status: res.status,\n },\n \"provenance record synced to cloud\",\n );\n return true;\n }\n const body = await res.text().catch(() => \"\");\n log.warn(\n {\n seq: record.seq,\n id: record.id.slice(0, 12),\n status: res.status,\n body: body.slice(0, 500),\n },\n \"cloud sink rejected provenance record\",\n );\n return false;\n } catch (err) {\n log.warn(\n {\n seq: record.seq,\n id: record.id.slice(0, 12),\n err: err instanceof Error ? err.message : String(err),\n },\n \"cloud sink request failed\",\n );\n return false;\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\n/**\n * Construct a CloudProvenanceSink from env vars if all required vars\n * are present. Returns null if any required env var is missing (allows\n * local dev / interactive mode to operate JSONL-only without errors).\n */\nexport function cloudSinkFromEnv(env: NodeJS.ProcessEnv = process.env): CloudProvenanceSink | null {\n const wikiId = env.WOTW_WIKI_ID;\n // Review item 50: prefer the dedicated cloud-sink secret; fall back to\n // ADMIN_SERVICE_KEY only while wotw-cloud is being migrated to the\n // split-secret scheme.\n const adminServiceKey = env.WOTW_CLOUD_SINK_SECRET ?? env.ADMIN_SERVICE_KEY;\n if (!wikiId || !adminServiceKey) {\n return null;\n }\n return new CloudProvenanceSink({\n wikiId,\n apiBaseUrl: env.WOTW_API_BASE_URL || undefined,\n adminServiceKey,\n });\n}\n","/**\n * Daemon child-process entrypoint. This file is what `spawnDaemon` forks into.\n * It reads environment variables, constructs a Daemon instance, wires up\n * the Phase 2+ subsystems (watcher → ingestion queue → wiki layer), and calls run().\n */\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { Daemon } from \"./index.js\";\nimport { LintScheduler } from \"./lint-scheduler.js\";\nimport { DekArchiveScheduler } from \"./dek-archive-scheduler.js\";\nimport { getLogger, initLogger, setLoggerContext } from \"../utils/logger.js\";\nimport { WikiStore } from \"../wiki/store.js\";\nimport { IndexManager } from \"../wiki/index-manager.js\";\nimport { WikiSearch } from \"../wiki/search.js\";\nimport { CostTracker } from \"../ingestion/cost-tracker.js\";\nimport { DeadLetterQueue } from \"../ingestion/dead-letter.js\";\nimport { ModelRouter } from \"../ingestion/model-router.js\";\nimport { IngestionQueue } from \"../ingestion/queue.js\";\nimport { FileWatcher } from \"../watcher/index.js\";\nimport { McpHttpServer } from \"../server/index.js\";\nimport { ProvenanceChain } from \"../provenance/chain.js\";\nimport type { KeyStore as KeyStoreType } from \"../keys/store.js\";\nimport { CompoundingEngine } from \"../compounding/engine.js\";\n\nasync function main(): Promise<void> {\n // Early fallback logger so daemon.init() failures are captured to disk.\n // Review item 7: in hosted mode the docker entrypoint cd's into\n // WIKI_ROOT before calling main(), so process.cwd() points at the\n // persistent Fly volume. A boot-failure log written there bloats\n // tenant storage with noise the user cannot inspect. Use /tmp in\n // hosted mode (ephemeral, only relevant if the real logger can't\n // come up anyway). Outside hosted mode, keep cwd to stay portable.\n const isHosted = process.env.WOTW_HOSTED === \"true\" || process.env.WOTW_HOSTED === \"1\";\n const fallbackLogDir = isHosted ? \"/tmp/wotw\" : `${process.cwd()}/.wotw`;\n const fallbackLogPath = `${fallbackLogDir}/daemon.log`;\n try {\n mkdirSync(fallbackLogDir, { recursive: true });\n initLogger(\"info\", fallbackLogPath);\n } catch {\n try {\n mkdirSync(fallbackLogDir, { recursive: true });\n writeFileSync(\n fallbackLogPath,\n `[${new Date().toISOString()}] FATAL: failed to initialize fallback logger\\n`,\n { flag: \"a\" },\n );\n } catch {\n /* nothing more we can do */\n }\n }\n\n const args = process.argv.slice(2);\n let configPath: string | null = null;\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--config\" && args[i + 1]) {\n configPath = args[i + 1] ?? null;\n i++;\n }\n }\n\n const workingDir = process.cwd();\n\n const daemon = new Daemon({ configPath, workingDir });\n try {\n const config = await daemon.init();\n\n // Hosted mode: tag every log line with the tenant ID\n if (config.hosted.enabled && config.hosted.tenant_id) {\n setLoggerContext({ tenantId: config.hosted.tenant_id });\n }\n\n const log = getLogger(\"daemon-entry\");\n\n // Resolved by daemon.init() — guaranteed non-null at this point.\n // The runtime mode is forwarded to every subsystem that invokes the\n // agent (ingestion, query, compounding) so they all dispatch through\n // the same execution path.\n const runtimeMode = daemon.getExecutionMode()?.mode ?? \"api\";\n\n // Wiki layer\n const store = new WikiStore({ wikiRoot: config.wiki_root });\n await store.ensureLayout();\n const indexManager = new IndexManager(store);\n const search = new WikiSearch();\n\n // Provenance chain (Phase 4). Initialized before any subsystem that may\n // append to it so early queries/ingestions don't race the file creation.\n let provenance: ProvenanceChain | null = null;\n // PASS-019 Part C: hoisted out of the provenance-enabled block so the\n // DEK auto-archive scheduler below can reference them.\n let keyStore: KeyStoreType | null = null;\n let workspaceId: string | undefined;\n if (config.provenance.enabled) {\n // Hosted-mode cloud sink (SD-1 closure, Pass-pair with wotw-cloud\n // /api/internal/append-provenance). Sink is null in local /\n // interactive mode where WOTW_WIKI_ID + ADMIN_SERVICE_KEY aren't set;\n // JSONL-only operation continues with no errors.\n const { cloudSinkFromEnv } = await import(\"../provenance/cloud-sink.js\");\n const sink = cloudSinkFromEnv();\n if (sink) {\n log.info(\n { wikiId: sink.wikiId, apiBaseUrl: sink.apiBaseUrl },\n \"provenance cloud sink active\",\n );\n }\n // G5 closure (Pass 018, v0.8.2): workspace KeyStore for end-to-end\n // attestation. Provisions a per-workspace DEK encrypted under a\n // KEK from Fly secrets (WOTW_WORKSPACE_KEK). Each chain append\n // signs with the active DEK and stamps `key_id` so verify can\n // look up the right DEK after rotation. Opt-in: requires both\n // hosted mode (workspace_id from tenant_id) AND the KEK env var.\n // Without these, ProvenanceChain falls back to the v0.8.1\n // single-key 4-tier resolution.\n const tenantId =\n config.hosted.enabled && config.hosted.tenant_id ? config.hosted.tenant_id : undefined;\n workspaceId = tenantId;\n if (tenantId && process.env.WOTW_WORKSPACE_KEK) {\n const { readKekFromEnv } = await import(\"../keys/envelope.js\");\n const { KeyStore } = await import(\"../keys/store.js\");\n try {\n const kek = readKekFromEnv();\n keyStore = new KeyStore({ path: `${config.wiki_root}/.wotw/keys.db`, kek });\n const existing = keyStore.active(tenantId);\n if (existing) {\n log.info(\n { keyId: existing.key_id.slice(0, 8), workspaceId: tenantId },\n \"workspace key store ready (existing active DEK)\",\n );\n } else {\n const provisioned = keyStore.provision(tenantId);\n log.info(\n { keyId: provisioned.key_id.slice(0, 8), workspaceId: tenantId },\n \"workspace key store ready (new DEK provisioned)\",\n );\n }\n } catch (err) {\n log.fatal(\n { err: err instanceof Error ? err.message : String(err) },\n \"workspace key store init failed; refusing to start with attestation enabled but broken\",\n );\n throw err;\n }\n }\n provenance = new ProvenanceChain({\n path: config.provenance.chain_file,\n sink,\n // Review items 42 + 43: tenant_id folds into canonical payload;\n // HMAC key falls back to a per-tenant derivation when no explicit\n // env override is set. Together these make forge / delete /\n // cross-tenant-replay detectable.\n tenantId,\n workspaceId: tenantId,\n keyStore,\n });\n await provenance.init();\n log.info({ path: provenance.path, records: provenance.count() }, \"provenance chain ready\");\n if (config.provenance.verify_on_startup) {\n const result = await provenance.verify();\n if (!result.ok) {\n log.fatal({ errors: result.errors.slice(0, 5) }, \"provenance chain verification failed\");\n throw new Error(\n `Provenance chain is corrupt: ${result.errors.length} error(s). Refusing to start.`,\n );\n }\n log.info(\n { total: result.totalRecords, verified: result.verifiedRecords },\n \"provenance chain verified\",\n );\n }\n }\n\n // Ingestion layer\n const costTracker = new CostTracker({\n trackFile: config.cost.track_file,\n maxDailyUsd: config.cost.max_daily_usd,\n maxPerIngestUsd: config.cost.max_per_ingest_usd,\n maxPerQueryUsd: config.cost.max_per_query_usd,\n });\n const modelRouter = new ModelRouter(config);\n // Dead-letter sink for permanently-failed batches (Feature 4). An\n // empty `dead_letter_file` disables the ledger entirely — the queue\n // still constructs the instance, but every write becomes a no-op.\n const deadLetter = new DeadLetterQueue({\n path: config.ingestion.dead_letter_file,\n runtimeMode,\n });\n // Pass B fact-extraction sidecar. Persistent SQLite at\n // <wiki_root>/.wotw/facts.db, with a parallel in-memory minisearch\n // index loaded from the live rows. Best-effort: extraction is gated\n // by `fact_extraction.enabled` + runtime cost-freeness, and any\n // failure inside the layer never breaks ingestion.\n const { FactStore } = await import(\"../facts/store.js\");\n const { FactIndex } = await import(\"../facts/index-manager.js\");\n const { isExtractionActive } = await import(\"../facts/extractor.js\");\n const factsDbPath = `${config.wiki_root}/.wotw/facts.db`;\n const factStore = new FactStore({ path: factsDbPath });\n const factIndex = new FactIndex();\n try {\n factIndex.rebuild(factStore.listActive(), factStore.listActiveQuestions());\n } catch (err) {\n log.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"facts.db: initial index rebuild failed; layer will be empty until next reindex\",\n );\n }\n const extractionStatus = isExtractionActive(config, runtimeMode);\n log.info(\n {\n active: extractionStatus.active,\n reason: extractionStatus.reason,\n path: factsDbPath,\n facts: factIndex.size(),\n questions: factIndex.questionCount(),\n },\n \"fact-extraction layer status\",\n );\n\n const ingestion = new IngestionQueue({\n config,\n store,\n indexManager,\n search,\n costTracker,\n modelRouter,\n provenance,\n runtimeMode,\n deadLetter,\n factStore,\n factIndex,\n });\n\n // Compounding engine (Phase 4) — not a subsystem; invoked on demand via\n // the CLI/MCP surface. Shares the same wiki/search/cost instances as\n // the ingestion queue.\n const compounding = new CompoundingEngine({\n config,\n store,\n indexManager,\n search,\n costTracker,\n modelRouter,\n provenance,\n runtimeMode,\n });\n\n // Watcher layer — hands every flushed batch to the ingestion queue.\n // Forwards runtimeMode so the debounce window is widened in CLI mode.\n const watcher = new FileWatcher({\n config,\n runtimeMode,\n onBatch: async (batch) => {\n const outcome = await ingestion.enqueue(batch);\n // Review item 25: when ingestion skipped for budget reasons,\n // signal retainForRetry so the watcher does NOT mark the files\n // as processed. Otherwise budget-skipped files would be silently\n // lost (never re-tried tomorrow when the daily window resets).\n if (\n outcome?.skipped === true &&\n typeof outcome.skipReason === \"string\" &&\n /budget|cap|exceeds/i.test(outcome.skipReason)\n ) {\n return { retainForRetry: true };\n }\n return undefined;\n },\n });\n\n // MCP server layer — bound to the same wiki/ingestion instances.\n const mcp = new McpHttpServer({\n config,\n store,\n indexManager,\n search,\n costTracker,\n modelRouter,\n provenance,\n compounding,\n runtimeMode,\n deadLetter,\n factStore,\n factIndex,\n });\n\n // Feature 1: periodic background lint. Cheap no-op unless\n // `lint.schedule_enabled` is true in config. Runs alongside the\n // ingestion/watcher loop on a timer.\n const lintScheduler = new LintScheduler({ config });\n\n // PASS-019 Part C: DEK auto-archive cron. Hourly tick scans\n // workspace_keys for `rotating` DEKs past their overlap window\n // (default 24h, configurable via WOTW_DEK_OVERLAP_HOURS) and\n // transitions them to `archived`. No-op unless keyStore is set\n // (hosted mode + WOTW_WORKSPACE_KEK present). Idempotent.\n const dekArchiveScheduler =\n keyStore && workspaceId ? new DekArchiveScheduler({ keyStore, workspaceId }) : null;\n\n daemon.attachSubsystem(ingestion);\n daemon.attachSubsystem(watcher);\n daemon.attachSubsystem(mcp);\n daemon.attachSubsystem(lintScheduler);\n if (dekArchiveScheduler) daemon.attachSubsystem(dekArchiveScheduler);\n\n // Review item 23: startReconciliation was defined + tested but\n // never called. The watcher relies on it to catch files dropped\n // into raw/ during daemon downtime (chokidar's initial-add events\n // fire on startup, but only once). Without a periodic reconcile\n // pass, any file the watcher's `processedPaths` Set already saw\n // and that gets re-touched, deleted, or replaced during a brief\n // hiccup is silently lost. 5-minute interval matches review intent.\n watcher.startReconciliation(5 * 60 * 1000);\n\n // Startup banner (Feature 3). Printed AFTER all subsystems are\n // wired but BEFORE run() blocks, so the operator always sees a\n // single line confirming the MCP URL and runtime mode.\n const mcpUrl = `http://${config.server.host}:${config.server.port}/mcp`;\n log.info(\n {\n mode: runtimeMode,\n mcp: mcpUrl,\n wikiRoot: config.wiki_root,\n deadLetter: deadLetter.enabled ? deadLetter.path : \"(disabled)\",\n lintSchedule: config.lint.schedule_enabled\n ? `every ${config.lint.interval_hours}h`\n : \"(disabled)\",\n },\n `Watcher on the Wall started in ${runtimeMode.toUpperCase()} mode — MCP at ${mcpUrl}`,\n );\n\n await daemon.run();\n } catch (err) {\n const log = getLogger(\"daemon-entry\");\n log.fatal({ err }, \"daemon failed to start\");\n process.exit(1);\n }\n}\n\nvoid main();\n","/**\n * Commander.js entrypoint for the `wotw` CLI. Registers all subcommands and\n * dispatches to their implementations.\n *\n * Special case: if this process was spawned with WOTW_DAEMON_CHILD=1 in the\n * environment (via `child_process.spawn` with `detached: true` and\n * `stdio: 'ignore'` — see decision D-16), it immediately hands control to\n * the daemon entry module. This keeps a single compiled entrypoint while\n * still supporting detached daemon spawning.\n */\nimport { Command } from \"commander\";\nimport { isActionableError } from \"../utils/actionable-error.js\";\nimport { errMsg } from \"../utils/errors.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerStartCommand } from \"./commands/start.js\";\nimport { registerStopCommand } from \"./commands/stop.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerQueryCommand } from \"./commands/query.js\";\nimport { registerAuditCommand } from \"./commands/audit.js\";\nimport { registerLintCommand } from \"./commands/lint.js\";\nimport { registerLogsCommand } from \"./commands/logs.js\";\nimport { registerInstallHookCommand } from \"./commands/install-hook.js\";\nimport { registerUninstallHookCommand } from \"./commands/uninstall-hook.js\";\nimport { registerServeCommand } from \"./commands/serve.js\";\nimport { registerSynthesizeCommand } from \"./commands/synthesize.js\";\nimport { registerSearchCommand } from \"./commands/search.js\";\nimport { registerStaleCommand } from \"./commands/stale.js\";\nimport { registerUserCommand } from \"./commands/user.js\";\nimport { registerApproveCommand } from \"./commands/approve.js\";\nimport { registerRejectCommand } from \"./commands/reject.js\";\nimport { registerCandidatesCommand } from \"./commands/candidates.js\";\nimport { registerFactsCommand } from \"./commands/facts.js\";\nimport { registerKeysCommand } from \"./commands/keys.js\";\nimport { registerWorkspaceCommand } from \"./commands/workspace.js\";\n\nimport { VERSION } from \"../utils/version.js\";\n\nasync function main(): Promise<void> {\n // If spawned as a detached daemon child (D-16), bypass CLI parsing entirely.\n if (process.env.WOTW_DAEMON_CHILD === \"1\") {\n await import(\"../daemon/entry.js\");\n return;\n }\n\n const program = new Command();\n program\n .name(\"wotw\")\n .description(\"watcher-on-the-wall — a self-bootstrapping persistent AI knowledge daemon\")\n .version(VERSION, \"-v, --version\", \"Print the version and exit\")\n .helpOption(\"-h, --help\", \"Show help\")\n .showHelpAfterError();\n\n registerInitCommand(program);\n registerStartCommand(program);\n registerStopCommand(program);\n registerStatusCommand(program);\n registerQueryCommand(program);\n registerSearchCommand(program);\n registerAuditCommand(program);\n registerLintCommand(program);\n registerLogsCommand(program);\n registerInstallHookCommand(program);\n registerUninstallHookCommand(program);\n registerServeCommand(program);\n registerStaleCommand(program);\n registerSynthesizeCommand(program);\n registerUserCommand(program);\n registerApproveCommand(program);\n registerRejectCommand(program);\n registerCandidatesCommand(program);\n registerFactsCommand(program);\n registerKeysCommand(program);\n registerWorkspaceCommand(program);\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err: unknown) => {\n if (isActionableError(err)) {\n process.stderr.write(`✖ ${err.message}\\n`);\n if (process.env.WOTW_DEBUG === \"1\") {\n const cause = (err as { cause?: unknown }).cause;\n if (cause instanceof Error && cause.stack) {\n process.stderr.write(`\\nUnderlying cause:\\n${cause.stack}\\n`);\n }\n }\n process.exit(1);\n }\n process.stderr.write(`fatal: ${errMsg(err)}\\n`);\n if (process.env.WOTW_DEBUG === \"1\") {\n process.stderr.write(`${err instanceof Error ? (err.stack ?? \"\") : \"\"}\\n`);\n }\n process.exit(1);\n});\n","/**\n * `wotw init` — interactive setup wizard.\n *\n * Unlike the original single-shot scaffolder, this command is Obsidian-aware:\n * it detects existing Obsidian vaults from the platform-specific registry,\n * lets the user overlay `raw/` + `wiki/` inside an existing vault (or create\n * a fresh one), sanity-checks the runtime (Claude CLI vs API key), and\n * offers to launch the result in Obsidian on completion.\n *\n * It is also idempotent: re-running `wotw init` on an already-initialized\n * directory verifies the structure and exits 0 without touching anything.\n *\n * Non-interactive mode (no TTY, CI, `--yes`) skips every prompt, uses the\n * current working directory (or `--path`) as the vault, accepts all\n * defaults, and does not open Obsidian.\n *\n * Layout after scaffolding (vault root = vault/):\n *\n * vault/\n * .gitignore\n * wotw.yaml <- config, paths relative to vault root\n * CLAUDE.md <- LLM schema\n * wiki/\n * index.md <- starter page with sentinel block\n * log.md\n * concepts/\n * entities/\n * sources/\n * comparisons/\n * syntheses/\n * queries/\n * raw/ <- user drops source files here\n * .obsidian/ <- only created for fresh vaults\n * app.json\n * appearance.json\n * graph.json\n *\n * Flags:\n * wotw init [dir] <- positional path, overrides auto-detect\n * -p, --path <dir> <- same, long-form alternative\n * -y, --yes <- skip prompts, accept defaults\n * --no-open <- skip the \"open in Obsidian\" step\n * -f, --force <- overwrite existing scaffold (legacy escape hatch)\n */\nimport * as p from \"@clack/prompts\";\nimport type { Command } from \"commander\";\nimport { getTelemetrySink, recordInitFailure } from \"../../telemetry/index.js\";\nimport { initTargetNotEmptyError, isActionableError } from \"../../utils/actionable-error.js\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { basename, isAbsolute, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { findApiKey, findOnPath } from \"../../ingestion/execution-mode.js\";\nimport { defaultConfig } from \"../../daemon/config.js\";\nimport { ensureDirSync, expandHome } from \"../../utils/fs.js\";\nimport { ensureGitRepo } from \"../../utils/git.js\";\nimport { fail, info, line, success, warn } from \"../output.js\";\nimport {\n findEnclosingVault,\n findObsidianVaults,\n openInObsidian,\n type ObsidianVault,\n} from \"../lib/vault-detect.js\";\n\n/** Ordered list of category subdirectories created under `wiki/`. */\nconst WIKI_CATEGORY_DIRS = [\n \"concepts\",\n \"entities\",\n \"sources\",\n \"comparisons\",\n \"syntheses\",\n \"queries\",\n] as const;\n\ninterface InitOptions {\n /** Legacy CLI flag / programmatic override for the vault path. */\n path?: string;\n /** Suppress prompts and accept defaults (also auto-set when stdin is not a TTY). */\n yes?: boolean;\n /**\n * When `false`, skip the \"open in Obsidian\" step. Commander maps `--no-open`\n * to `open: false`.\n */\n open?: boolean;\n /** Overwrite an existing scaffold. Bypasses the idempotency short-circuit. */\n force?: boolean;\n /**\n * Internal override used by tests: treat stdin as non-TTY even when it\n * actually is, to take the deterministic code path.\n */\n nonInteractive?: boolean;\n}\n\n/** Result of the idempotency check at the start of the wizard. */\ninterface AlreadyInitializedResult {\n initialized: boolean;\n reason?: string;\n}\n\n/** What `runInit` returns to the caller (CLI action or tests). */\nexport interface RunInitResult {\n /** Absolute vault path that was initialized (or found already-initialized). */\n root: string;\n /** Set when the idempotency check short-circuited the wizard. */\n alreadyInitialized: boolean;\n /** True when a fresh vault was scaffolded (no pre-existing `.obsidian/`). */\n createdFreshVault: boolean;\n /**\n * When the user overlaid into a subdirectory rather than the vault root,\n * this is the absolute path to that subdirectory. Otherwise null.\n */\n overlaySubdir: string | null;\n}\n\n/** Runtime detection summary for Step 4. */\ninterface RuntimeDetection {\n mode: \"cli\" | \"api\" | \"none\";\n cliPath: string | null;\n apiKeyEnv: string | null;\n}\n\n/** Decoded selection from the vault-location prompt. */\ntype Choice = { kind: \"vault\"; vault: ObsidianVault } | { kind: \"cwd\" } | { kind: \"custom\" };\n\n/**\n * Attach the `init` subcommand to a Commander program.\n */\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init [dir]\")\n .description(\"Scaffold a new wiki vault (Obsidian-aware interactive wizard)\")\n .option(\"-p, --path <dir>\", \"Vault path (skip detection)\")\n .option(\"-y, --yes\", \"Accept all defaults, no prompts\")\n .option(\"--no-open\", \"Skip opening Obsidian on completion\")\n .option(\"-f, --force\", \"Overwrite existing files if already initialized\")\n .action(async (dir: string | undefined, opts: InitOptions) => {\n try {\n const pathArg = dir ?? opts.path;\n await runInit({ ...opts, path: pathArg });\n } catch (err) {\n if (errMsg(err) === \"cancelled\") {\n // User hit Ctrl-C during a prompt — exit quietly.\n process.exitCode = 0;\n return;\n }\n // Opt-in BYO-DSN telemetry: best-effort categorical record of\n // the failure, gated on WOTW_TELEMETRY_DSN being set. Default\n // is no-op. See docs/telemetry.md.\n recordInitFailure(getTelemetrySink(), err);\n // Re-throw ActionableErrors so the CLI top-level handler\n // renders them with structured suggestions. Plain errors\n // continue to use the legacy \"init failed: ...\" path.\n if (isActionableError(err)) {\n throw err;\n }\n fail(`init failed: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n });\n}\n\n/**\n * Implementation used by the CLI action and by integration tests.\n *\n * Returns a {@link RunInitResult}. Throws on fatal scaffolding errors; swallows\n * user-cancels from the prompt library via the \"cancelled\" sentinel error.\n */\nexport async function runInit(opts: InitOptions): Promise<RunInitResult> {\n const interactive = isInteractive(opts);\n const ttyPath = resolveExplicitVaultPath(opts.path);\n\n if (interactive) {\n p.intro(\"watcher-on-the-wall — setup wizard\");\n }\n\n // --- Step 2: vault location ---------------------------------------------\n let vaultPath: string;\n const envVaultPath = resolveEnvVaultPath();\n if (ttyPath !== null) {\n vaultPath = ttyPath;\n } else if (envVaultPath !== null) {\n vaultPath = envVaultPath;\n } else if (interactive) {\n vaultPath = await promptForVaultPath();\n } else {\n vaultPath = process.cwd();\n }\n\n // --- Step 1.5: idempotency check (on the resolved vault path) ----------\n const already = checkAlreadyInitialized(vaultPath);\n if (already.initialized && !opts.force) {\n const msg = `Already initialized at ${vaultPath} — structure verified, nothing to do.`;\n if (interactive) {\n p.note(msg, \"Idempotent re-run\");\n p.outro(\"Nothing to do.\");\n } else {\n info(msg);\n }\n return {\n root: vaultPath,\n alreadyInitialized: true,\n createdFreshVault: false,\n overlaySubdir: null,\n };\n }\n\n // --- Step 2.5: non-empty-target guard ------------------------------------\n // If the target exists, has content, AND is neither an Obsidian vault nor a\n // partial wotw scaffold, refuse to scaffold over it. This catches the\n // \"stranger ran `wotw init` in their Downloads folder\" mistake before the\n // wizard scatters files into a user-meaningful directory.\n if (!opts.force) {\n const collision = detectNonEmptyTargetCollision(vaultPath);\n if (collision !== null) {\n throw initTargetNotEmptyError(vaultPath, collision);\n }\n }\n\n // --- Step 3: overlay detection -----------------------------------------\n const hasObsidianDir = existsSync(join(vaultPath, \".obsidian\"));\n let overlayDir: string = vaultPath;\n let overlaySubdir: string | null = null;\n\n if (hasObsidianDir && interactive) {\n const overlay = await ask(\n p.confirm({\n message: \"Found an existing Obsidian vault. Create raw/ and wiki/ inside it?\",\n initialValue: true,\n }),\n );\n if (!overlay) {\n const sub = await ask(\n p.text({\n message: \"Subdirectory name (will be created inside the vault)\",\n placeholder: \"wotw\",\n initialValue: \"wotw\",\n validate(value) {\n if (!value || value.trim().length === 0) return \"Subdirectory name is required\";\n if (value.includes(\"/\") || value.includes(\"\\\\\")) {\n return \"Must be a single directory name, not a path\";\n }\n return undefined;\n },\n }),\n );\n overlayDir = join(vaultPath, sub.trim());\n overlaySubdir = overlayDir;\n }\n } else if (!hasObsidianDir && interactive) {\n p.note(\n `No existing .obsidian/ detected — ${vaultPath} will be set up as a fresh vault with sensible Obsidian defaults.`,\n \"Fresh vault\",\n );\n }\n\n // --- Step 4: runtime detection -----------------------------------------\n const runtime = detectRuntime();\n if (interactive) {\n p.note(runtimeSummary(runtime), \"Runtime\");\n } else if (runtime.mode === \"none\") {\n warn(\n \"No runtime detected. Install `claude` CLI (https://docs.claude.com/claude-code) or set ANTHROPIC_API_KEY.\",\n );\n }\n\n // --- Step 5: scaffold --------------------------------------------------\n const isFreshVault = !hasObsidianDir;\n const spin = interactive ? p.spinner() : null;\n spin?.start(\"Scaffolding wiki structure\");\n try {\n await scaffoldVault(overlayDir, vaultPath, { isFreshVault, force: opts.force === true });\n spin?.stop(\"Wiki scaffolded\");\n } catch (err) {\n spin?.stop(\"Scaffold failed\");\n throw err;\n }\n\n // --- Step 6: open in Obsidian ------------------------------------------\n // The scaffold is already complete at this point. This prompt is purely\n // optional, so a cancel (Escape / Ctrl-C) must NOT abort the wizard with\n // a misleading \"Cancelled.\" — it means \"no, don't open\" and we proceed to\n // the success + next-steps output (PASS-023 dogfood finding #9).\n if (interactive && opts.open !== false) {\n const rawAnswer = await p.confirm({\n message: \"Open vault in Obsidian now? (Esc to skip)\",\n initialValue: true,\n });\n const shouldOpen = p.isCancel(rawAnswer) ? false : rawAnswer;\n if (shouldOpen) {\n const ok = await openInObsidian(vaultPath);\n if (!ok) {\n const anyVaults = findObsidianVaults().length > 0;\n if (anyVaults) {\n p.note(\n \"Couldn't open Obsidian automatically. Open the folder as a vault manually.\",\n \"Launch skipped\",\n );\n } else {\n p.note(\n \"Obsidian doesn't appear to be installed. Your wiki is plain markdown and\\n\" +\n \"works without it, but for the best experience install Obsidian from\\n\" +\n \"https://obsidian.md and open this folder as a vault.\",\n \"Obsidian not installed\",\n );\n }\n }\n }\n }\n\n // --- Step 7: success + next steps --------------------------------------\n const nextSteps = renderNextSteps();\n if (interactive) {\n p.note(nextSteps, \"Next steps\");\n p.outro(\"Done! Your wiki is ready.\");\n } else {\n success(`Wiki initialized at ${vaultPath}`);\n line(\"\");\n line(nextSteps);\n }\n\n return {\n root: vaultPath,\n alreadyInitialized: false,\n createdFreshVault: isFreshVault,\n overlaySubdir,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Step helpers\n// ---------------------------------------------------------------------------\n\n/**\n * True when the user should see interactive prompts. Stdin must be a TTY\n * AND `--yes` / non-interactive override must not be set.\n */\nfunction isInteractive(opts: InitOptions): boolean {\n if (opts.nonInteractive === true) return false;\n if (opts.yes === true) return false;\n return process.stdin.isTTY === true;\n}\n\n/** Resolve an explicit `--path` / positional argument to an absolute path, or null. */\nfunction resolveExplicitVaultPath(input: string | undefined): string | null {\n if (!input) return null;\n const expanded = expandHome(input);\n return isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);\n}\n\n/**\n * Resolve the OBSIDIAN_VAULT_PATH environment variable to an absolute path,\n * or null when unset/empty. This takes precedence over the cwd fallback in\n * non-interactive mode and over the prompt in interactive mode — operators\n * who export it expect it to be honored.\n */\nfunction resolveEnvVaultPath(): string | null {\n const raw = process.env.OBSIDIAN_VAULT_PATH;\n if (!raw || raw.trim().length === 0) return null;\n const expanded = expandHome(raw.trim());\n return isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);\n}\n\n/**\n * Detect whether `vaultPath` is a non-empty directory that is NEITHER an\n * Obsidian vault NOR an existing wotw scaffold. Returns the list of\n * conflicting top-level entries when collision detected, or null when the\n * target is safe to scaffold into.\n *\n * Considered safe:\n * - path doesn't exist (will be created during scaffold)\n * - path is an empty directory\n * - path contains `.obsidian/` (overlay path; prompt will handle subdir choice)\n * - path is already a wotw scaffold (caught upstream by checkAlreadyInitialized)\n */\nfunction detectNonEmptyTargetCollision(vaultPath: string): string[] | null {\n if (!existsSync(vaultPath)) return null;\n let entries: string[];\n try {\n entries = readdirSync(vaultPath);\n } catch {\n // Unreadable — let downstream EACCES handling surface that distinctly.\n return null;\n }\n // Hidden-file noise + macOS/Windows housekeeping entries don't count.\n const ignored = new Set([\".DS_Store\", \"Thumbs.db\", \".git\", \".gitignore\"]);\n const meaningful = entries.filter((e) => !ignored.has(e));\n if (meaningful.length === 0) return null;\n // Obsidian vault → overlay flow handles this.\n if (meaningful.includes(\".obsidian\")) return null;\n // Existing wotw scaffold → idempotency guard handles this.\n if (\n meaningful.includes(\".wotw\") ||\n meaningful.includes(\"wotw.config.yaml\") ||\n (meaningful.includes(\"raw\") && meaningful.includes(\"wiki\"))\n ) {\n return null;\n }\n return meaningful;\n}\n\n/**\n * Interactive vault-location prompt. Lists detected Obsidian vaults plus\n * \"current directory\" and \"enter a custom path\" options, then returns the\n * chosen absolute path. Falls back to a free-form text prompt when no\n * vaults are discovered.\n */\nasync function promptForVaultPath(): Promise<string> {\n const vaults = findObsidianVaults();\n const cwd = process.cwd();\n const enclosing = findEnclosingVault(cwd);\n\n // Move the enclosing vault to the front of the list if present.\n if (enclosing !== null) {\n const idx = vaults.findIndex((v) => resolve(v.path) === resolve(enclosing));\n if (idx > 0) {\n const [found] = vaults.splice(idx, 1);\n if (found) vaults.unshift(found);\n } else if (idx === -1) {\n vaults.unshift({\n name: basename(enclosing),\n path: enclosing,\n ts: Date.now(),\n open: false,\n });\n }\n }\n\n // Encode the choice as a string id (`vault:<idx>`, `cwd`, `custom`) so we\n // sidestep clack's distributive typing over object values. We decode it\n // back into `Choice` after the prompt returns.\n const options: Array<{ value: string; label: string; hint?: string }> = [];\n vaults.forEach((v, idx) => {\n options.push({\n value: `vault:${idx}`,\n label: v.name,\n hint: v.path,\n });\n });\n options.push({\n value: \"cwd\",\n label: \"Create new vault here\",\n hint: cwd,\n });\n options.push({\n value: \"custom\",\n label: \"Enter a custom path\",\n });\n\n if (vaults.length === 0) {\n // No registry hits — go straight to a text prompt, defaulting to cwd.\n const custom = await ask(\n p.text({\n message: \"Vault path\",\n placeholder: cwd,\n initialValue: cwd,\n validate: validateVaultPath,\n }),\n );\n return resolvePromptPath(custom);\n }\n\n const choiceId = await ask(\n p.select<string>({\n message: \"Where should the wiki live?\",\n options,\n }),\n );\n\n const choice = decodeChoice(choiceId, vaults);\n if (choice.kind === \"vault\") return resolve(choice.vault.path);\n if (choice.kind === \"cwd\") return cwd;\n\n const custom = await ask(\n p.text({\n message: \"Vault path\",\n placeholder: cwd,\n initialValue: cwd,\n validate: validateVaultPath,\n }),\n );\n return resolvePromptPath(custom);\n}\n\n/**\n * Decode the string id produced by the vault `p.select` prompt back into a\n * typed `Choice` discriminated union. Unknown ids fall back to `\"custom\"` so\n * the caller's text-prompt branch fires.\n */\nfunction decodeChoice(id: string, vaults: ObsidianVault[]): Choice {\n if (id === \"cwd\") return { kind: \"cwd\" };\n if (id.startsWith(\"vault:\")) {\n const idx = Number.parseInt(id.slice(\"vault:\".length), 10);\n const v = Number.isInteger(idx) ? vaults[idx] : undefined;\n if (v) return { kind: \"vault\", vault: v };\n }\n return { kind: \"custom\" };\n}\n\n/** Validate a text-prompt path. Returns an error message or undefined. */\nfunction validateVaultPath(value: string | undefined): string | undefined {\n if (!value || value.trim().length === 0) return \"Path is required\";\n return undefined;\n}\n\n/** Expand `~`, make absolute relative to cwd. */\nfunction resolvePromptPath(raw: string): string {\n const expanded = expandHome(raw.trim());\n return isAbsolute(expanded) ? expanded : resolve(process.cwd(), expanded);\n}\n\n/** Wrap a clack prompt with cancel-to-exception plumbing. */\nasync function ask<T>(prompt: Promise<T | symbol>): Promise<T> {\n const result = await prompt;\n if (p.isCancel(result)) {\n p.cancel(\"Cancelled.\");\n throw new Error(\"cancelled\");\n }\n return result as T;\n}\n\n// ---------------------------------------------------------------------------\n// Idempotency\n// ---------------------------------------------------------------------------\n\n/**\n * Quick structural check: does the target directory look like a fully\n * initialized wotw vault? We look for a config file at the vault root plus\n * the wiki/ and raw/ subdirectories with the expected category layout.\n */\nfunction checkAlreadyInitialized(vaultPath: string): AlreadyInitializedResult {\n if (!existsSync(vaultPath)) return { initialized: false };\n try {\n if (!statSync(vaultPath).isDirectory()) {\n return { initialized: false, reason: \"target is not a directory\" };\n }\n } catch {\n return { initialized: false };\n }\n\n const hasConfig = CONFIG_CANDIDATES.some((name) => existsSync(join(vaultPath, name)));\n if (!hasConfig) return { initialized: false };\n\n const rawDir = join(vaultPath, \"raw\");\n const wikiDir = join(vaultPath, \"wiki\");\n if (!existsSync(rawDir) || !existsSync(wikiDir)) return { initialized: false };\n\n // Category subdirectories must all be present — otherwise the user has a\n // half-built vault and we should treat it as uninitialized.\n for (const cat of WIKI_CATEGORY_DIRS) {\n if (!existsSync(join(wikiDir, cat))) {\n return { initialized: false, reason: `missing wiki/${cat}` };\n }\n }\n return { initialized: true };\n}\n\n/**\n * Config filenames we recognize when checking for an existing scaffold.\n * Lines up with cosmiconfig's searchPlaces in `src/daemon/config.ts`.\n */\nconst CONFIG_CANDIDATES = [\n \"wotw.yaml\",\n \"wotw.yml\",\n \"wotw.config.yaml\",\n \"wotw.config.yml\",\n \"wotw.config.json\",\n \".wotwrc\",\n \".wotwrc.json\",\n \".wotwrc.yaml\",\n \".wotwrc.yml\",\n] as const;\n\n// ---------------------------------------------------------------------------\n// Runtime detection\n// ---------------------------------------------------------------------------\n\n/** Silent sibling of `resolveExecutionMode` — detect without throwing. */\nfunction detectRuntime(): RuntimeDetection {\n const defaults = defaultConfig();\n const cliPath = findOnPath(defaults.execution.cli_path);\n if (cliPath !== null) {\n return { mode: \"cli\", cliPath, apiKeyEnv: null };\n }\n const keyEnv = findApiKey(defaults.execution.api_key_env);\n if (keyEnv !== null) {\n return { mode: \"api\", cliPath: null, apiKeyEnv: keyEnv };\n }\n return { mode: \"none\", cliPath: null, apiKeyEnv: null };\n}\n\n/** Human-readable one-paragraph summary of the detected runtime. */\nfunction runtimeSummary(runtime: RuntimeDetection): string {\n if (runtime.mode === \"cli\") {\n return `CLI mode (claude binary found at ${runtime.cliPath ?? \"<unknown>\"})`;\n }\n if (runtime.mode === \"api\") {\n return `API mode (${runtime.apiKeyEnv} detected)`;\n }\n return (\n \"No runtime detected. You'll need either:\\n\" +\n \" • claude CLI binary on your PATH (free with Claude Pro/Max)\\n\" +\n \" • ANTHROPIC_API_KEY environment variable (pay-per-token)\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// Scaffolding\n// ---------------------------------------------------------------------------\n\ninterface ScaffoldOptions {\n isFreshVault: boolean;\n force: boolean;\n}\n\n/**\n * Create every directory and file the daemon needs inside `overlayDir`. The\n * Obsidian `.obsidian/` directory and the top-level `.gitignore` are written\n * at `vaultRoot`, which is the same as `overlayDir` except when the user\n * opted into a subdirectory overlay.\n */\nasync function scaffoldVault(\n overlayDir: string,\n vaultRoot: string,\n opts: ScaffoldOptions,\n): Promise<void> {\n // Directories\n ensureDirSync(overlayDir);\n ensureDirSync(join(overlayDir, \"raw\"));\n const wikiDir = join(overlayDir, \"wiki\");\n ensureDirSync(wikiDir);\n for (const cat of WIKI_CATEGORY_DIRS) {\n ensureDirSync(join(wikiDir, cat));\n }\n // Candidates staging directory (for approve/reject workflow).\n ensureDirSync(join(overlayDir, \"candidates\"));\n ensureDirSync(join(overlayDir, \"candidates\", \"rejected\"));\n\n // Templates (index.md, log.md, CLAUDE.md) live next to the compiled JS\n // at runtime; fall back to the source tree for `pnpm test`.\n const templatesDir = resolveTemplatesDir();\n const indexTemplate = readFileSync(join(templatesDir, \"index.md\"), \"utf8\");\n const logTemplate = readFileSync(join(templatesDir, \"log.md\"), \"utf8\");\n const claudeTemplate = readFileSync(join(templatesDir, \"CLAUDE.md\"), \"utf8\");\n const gettingStartedTemplate = readFileSync(join(templatesDir, \"getting-started.md\"), \"utf8\");\n\n const isoNow = new Date().toISOString();\n const indexWithTs = indexTemplate.replace(\"__WOTW_UPDATED_ISO__\", isoNow);\n const gettingStartedWithTs = gettingStartedTemplate.replace(/__WOTW_UPDATED_ISO__/g, isoNow);\n\n writeIfMissingOrForce(join(wikiDir, \"index.md\"), indexWithTs, opts.force);\n writeIfMissingOrForce(join(wikiDir, \"log.md\"), logTemplate, opts.force);\n writeIfMissingOrForce(join(wikiDir, \"getting-started.md\"), gettingStartedWithTs, opts.force);\n writeIfMissingOrForce(join(overlayDir, \"CLAUDE.md\"), claudeTemplate, opts.force);\n\n // wotw.yaml at vault root (NOT overlayDir — the config discovery walks up\n // from cwd, so the config file lives at the user-facing vault root).\n const configPath = join(vaultRoot, \"wotw.yaml\");\n const configYaml = renderDefaultConfigYaml(overlayDir, vaultRoot);\n writeIfMissingOrForce(configPath, configYaml, opts.force);\n\n // .gitignore at vault root — append-or-create.\n writeGitignore(join(vaultRoot, \".gitignore\"));\n\n // Obsidian metadata — only for fresh vaults.\n if (opts.isFreshVault) {\n writeObsidianDefaults(vaultRoot);\n }\n\n // Initialize a git repo at the vault root so every ingestion becomes a\n // commit. Silent no-op if a repo already exists.\n await ensureGitRepo(vaultRoot, \"chore: wotw init — scaffold wiki store\");\n}\n\n/**\n * Write a file if it does not already exist. Honors `--force` by\n * overwriting when set. The intent is to preserve user content during\n * overlay operations.\n */\nfunction writeIfMissingOrForce(path: string, contents: string, force: boolean): void {\n if (!existsSync(path) || force) {\n writeFileSync(path, contents);\n }\n}\n\n/**\n * Render the wotw.yaml emitted by `wotw init`. Paths are stored relative to\n * the vault root so the vault is portable between machines — users just run\n * `wotw start` from inside the vault and cosmiconfig + resolveConfigPaths\n * walk from there.\n *\n * If the wiki is being overlaid into a subdirectory of the vault root, the\n * subdirectory name is baked into the relative paths.\n */\nfunction renderDefaultConfigYaml(overlayDir: string, vaultRoot: string): string {\n const rel = relativeForYaml(overlayDir, vaultRoot);\n const wikiRoot = rel === \".\" ? \".\" : `./${rel}`;\n const rawPath = rel === \".\" ? \"./raw\" : `./${rel}/raw`;\n return `# watcher-on-the-wall configuration\n# Generated by 'wotw init'. Edit freely; see docs/configuration.md.\n#\n# wiki_root is the directory that contains the wiki/ subfolder of generated\n# pages (index.md, concepts/, entities/, …). raw_path is where you drop\n# source files — a sibling of wiki/ at the vault root.\n\nwiki_root: ${wikiRoot}\nraw_path: ${rawPath}\n\nexecution:\n mode: auto\n cli_model: claude-sonnet-4-5\n\nserver:\n port: 8787\n host: 127.0.0.1\n`;\n}\n\n/**\n * POSIX-style relative path from `from` to `to`, used when rendering the\n * `wotw.yaml`. When they're equal, returns \".\".\n */\nfunction relativeForYaml(to: string, from: string): string {\n const a = resolve(from);\n const b = resolve(to);\n if (a === b) return \".\";\n // Compute a platform-agnostic relative form by string prefix comparison.\n // The init command only ever produces subdir overlays of a vault root, so\n // a simple prefix strip is sufficient (and avoids `..` noise).\n const prefix = a.endsWith(\"/\") || a.endsWith(\"\\\\\") ? a : `${a}/`;\n if (b.startsWith(prefix)) {\n return b.slice(prefix.length).replace(/\\\\/g, \"/\");\n }\n // Fallback: just use the last segment.\n return basename(b);\n}\n\n/**\n * Write a new `.gitignore` if one doesn't exist, otherwise append the wotw\n * rules to the existing file if they aren't already present.\n */\nfunction writeGitignore(gitignorePath: string): void {\n const wotwBlock = [\n \"\",\n \"# wotw daemon state\",\n \".wotw/\",\n \"*.pid\",\n \"*.lock\",\n \"\",\n \"# OS\",\n \".DS_Store\",\n \"Thumbs.db\",\n \"desktop.ini\",\n \"\",\n ].join(\"\\n\");\n\n const fullGitignore = [\n \"# Obsidian — device-specific (do not sync)\",\n \".obsidian/workspace.json\",\n \".obsidian/workspace-mobile.json\",\n \".obsidian/cache/\",\n \".obsidian/plugins/*/data.json\",\n \"\",\n \"# wotw daemon state\",\n \".wotw/\",\n \"*.pid\",\n \"*.lock\",\n \"\",\n \"# OS\",\n \".DS_Store\",\n \"Thumbs.db\",\n \"desktop.ini\",\n \"\",\n ].join(\"\\n\");\n\n if (!existsSync(gitignorePath)) {\n writeFileSync(gitignorePath, fullGitignore);\n return;\n }\n\n const existing = readFileSync(gitignorePath, \"utf8\");\n if (existing.includes(\"# wotw\") || existing.includes(\".wotw/\")) {\n return;\n }\n const separator = existing.endsWith(\"\\n\") ? \"\" : \"\\n\";\n writeFileSync(gitignorePath, `${existing}${separator}${wotwBlock}`);\n}\n\n/**\n * Create `.obsidian/` with minimal sane defaults. Only called for fresh\n * vaults — we never touch an existing `.obsidian/` directory because\n * Obsidian owns it after first launch.\n */\nfunction writeObsidianDefaults(vaultRoot: string): void {\n const obsDir = join(vaultRoot, \".obsidian\");\n if (existsSync(obsDir)) return;\n mkdirSync(obsDir, { recursive: true });\n\n const appJson = {\n attachmentFolderPath: \"raw/assets\",\n newFileLocation: \"folder\",\n newFileFolderPath: \"raw\",\n alwaysUpdateLinks: true,\n showUnsupportedFiles: false,\n };\n const appearanceJson = { accentColor: \"#7c3aed\" };\n const graphJson = {\n \"collapse-filter\": false,\n search: \"\",\n showTags: true,\n showAttachments: false,\n showOrphans: true,\n \"collapse-color-groups\": false,\n colorGroups: [\n { query: \"path:wiki/sources\", color: { a: 1, rgb: 5145874 } },\n { query: \"path:wiki/syntheses\", color: { a: 1, rgb: 10371072 } },\n { query: \"path:raw\", color: { a: 1, rgb: 8421504 } },\n ],\n \"collapse-display\": false,\n showArrow: true,\n textFadeMultiplier: 0,\n nodeSizeMultiplier: 1,\n lineSizeMultiplier: 1,\n };\n\n writeFileSync(join(obsDir, \"app.json\"), `${JSON.stringify(appJson, null, 2)}\\n`);\n writeFileSync(join(obsDir, \"appearance.json\"), `${JSON.stringify(appearanceJson, null, 2)}\\n`);\n writeFileSync(join(obsDir, \"graph.json\"), `${JSON.stringify(graphJson, null, 2)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Template resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the templates directory. When bundled by tsup, templates are copied\n * next to the compiled JS. In development we fall back to the source directory.\n */\nfunction resolveTemplatesDir(): string {\n const here = fileURLToPath(import.meta.url);\n const candidates = [\n resolve(here, \"..\", \"..\", \"..\", \"wiki\", \"templates\"),\n resolve(here, \"..\", \"..\", \"wiki\", \"templates\"),\n resolve(here, \"..\", \"..\", \"..\", \"..\", \"src\", \"wiki\", \"templates\"),\n ];\n for (const c of candidates) {\n if (existsSync(join(c, \"CLAUDE.md\"))) return c;\n }\n throw new Error(`Could not locate wiki templates directory. Checked: ${candidates.join(\", \")}`);\n}\n\n// ---------------------------------------------------------------------------\n// Output\n// ---------------------------------------------------------------------------\n\n/** The \"what should I do next?\" panel printed on successful init. */\nfunction renderNextSteps(): string {\n return [\n \"1. Drop source files into raw/\",\n \"2. wotw start # Launch the Watcher\",\n \"3. wotw status # Check it's running\",\n '4. wotw query \"your question\" # Ask your wiki anything',\n \"\",\n \"(Advanced) wotw install-hook # Auto-start with Claude Code sessions\",\n \"\",\n \"The Watcher is ready. It will handle the rest.\",\n ].join(\"\\n\");\n}\n","/**\n * Public re-exports for the telemetry module. Other subsystems import\n * the sink + categorizer; never import private modules directly.\n */\nexport type { TelemetryFailureCategory, TelemetryFailureEvent, TelemetrySink } from \"./types.js\";\nexport { MemorySink, NoopSink, SentrySink, getTelemetrySink, validateEvent } from \"./sink.js\";\nexport { categorizeInitFailure, recordInitFailure } from \"./categorize.js\";\n","/**\n * Telemetry sink implementations.\n *\n * Privacy posture (PASS-023):\n * - **Disabled by default.** `getTelemetrySink()` returns a no-op\n * `NoopSink` unless `WOTW_TELEMETRY_DSN` is set in the environment.\n * - **No embedded DSN.** The daemon ships zero Sentry credentials.\n * Operators who want crash visibility provide their own Sentry\n * project DSN — they own the data, not 3030 Labs.\n * - **Categorical events only.** Even when active, the sink rejects\n * payloads that contain anything other than the stable enum\n * fields defined in {@link TelemetryFailureEvent}. No vault paths,\n * no API keys, no user-controlled strings.\n * - **Init failures only.** v1 sinks do not record successful\n * operations, runtime telemetry, or query patterns. Init-time\n * failures are the only emission surface.\n *\n * See [`docs/telemetry.md`](../../docs/telemetry.md) for the user-facing\n * opt-in instructions and privacy guarantees.\n */\nimport { getLogger } from \"../utils/logger.js\";\nimport type { TelemetryFailureCategory, TelemetryFailureEvent, TelemetrySink } from \"./types.js\";\n\nconst ALLOWED_CATEGORIES: readonly TelemetryFailureCategory[] = [\n \"init/missing-vault-path\",\n \"init/target-not-empty\",\n \"init/config-parse-error\",\n \"init/native-binding-load-failure\",\n \"init/wiki-dir-permission-denied\",\n \"init/port-in-use\",\n \"init/daemon-already-running\",\n \"init/runtime-not-detected\",\n \"init/scaffold-failed\",\n \"init/unknown-failure\",\n];\n\nconst ALLOWED_FIELDS = new Set([\n \"category\",\n \"daemonVersion\",\n \"platform\",\n \"arch\",\n \"nodeVersion\",\n \"stage\",\n]);\n\n/**\n * Strict validator. Rejects any payload that does not match the\n * stable {@link TelemetryFailureEvent} shape. Returns `null` on\n * success, an error string on failure.\n */\nexport function validateEvent(event: unknown): string | null {\n if (typeof event !== \"object\" || event === null) {\n return \"event must be an object\";\n }\n const obj = event as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n if (!ALLOWED_FIELDS.has(key)) {\n return `disallowed field on telemetry event: ${key}`;\n }\n }\n if (typeof obj.category !== \"string\") {\n return \"category must be a string\";\n }\n if (!ALLOWED_CATEGORIES.includes(obj.category as TelemetryFailureCategory)) {\n return `category not in allow-list: ${obj.category}`;\n }\n if (typeof obj.daemonVersion !== \"string\" || obj.daemonVersion.length === 0) {\n return \"daemonVersion must be a non-empty string\";\n }\n if (typeof obj.platform !== \"string\") return \"platform must be a string\";\n if (typeof obj.arch !== \"string\") return \"arch must be a string\";\n if (typeof obj.nodeVersion !== \"string\") {\n return \"nodeVersion must be a string\";\n }\n if (obj.stage !== undefined && typeof obj.stage !== \"string\") {\n return \"stage must be a string when present\";\n }\n return null;\n}\n\n/**\n * No-op sink. The default when telemetry is disabled. Calls are\n * silently discarded; nothing leaves the process.\n */\nexport class NoopSink implements TelemetrySink {\n recordInitFailure(_event: TelemetryFailureEvent): void {\n // intentionally empty\n }\n}\n\n/**\n * In-memory sink used by tests + opt-in debug mode. Records events\n * to an array readable via {@link recorded}. Validates every event\n * against {@link validateEvent} — a violation throws.\n */\nexport class MemorySink implements TelemetrySink {\n private events: TelemetryFailureEvent[] = [];\n\n recordInitFailure(event: TelemetryFailureEvent): void {\n const err = validateEvent(event);\n if (err !== null) {\n throw new Error(`MemorySink validation rejected event: ${err}`);\n }\n this.events.push({ ...event });\n }\n\n recorded(): readonly TelemetryFailureEvent[] {\n return this.events.slice();\n }\n\n clear(): void {\n this.events.length = 0;\n }\n}\n\n/**\n * Sentry-backed sink. Lazy-loads the Sentry SDK on first use so the\n * daemon's hot path doesn't pay for the import in the (vastly more\n * common) telemetry-disabled case.\n *\n * The DSN comes from `WOTW_TELEMETRY_DSN` (passed in via constructor).\n * Errors during Sentry send are swallowed — telemetry must never\n * cause an additional user-visible failure.\n */\nexport class SentrySink implements TelemetrySink {\n private readonly dsn: string;\n private initialized = false;\n\n constructor(dsn: string) {\n this.dsn = dsn;\n }\n\n recordInitFailure(event: TelemetryFailureEvent): void {\n const err = validateEvent(event);\n if (err !== null) {\n const log = getLogger(\"telemetry\");\n log.warn({ err }, \"telemetry event failed validation; dropping\");\n return;\n }\n void this.send(event);\n }\n\n private async send(event: TelemetryFailureEvent): Promise<void> {\n const log = getLogger(\"telemetry\");\n try {\n if (!this.initialized) {\n // Dynamic import so the Sentry SDK is only loaded when telemetry\n // is actually opted-in.\n const Sentry = await loadSentry();\n if (Sentry === null) {\n log.warn(\n \"WOTW_TELEMETRY_DSN is set but @sentry/node is not installed; falling back to no-op\",\n );\n return;\n }\n Sentry.init({\n dsn: this.dsn,\n // No automatic instrumentation — we want exactly one event type.\n defaultIntegrations: false,\n tracesSampleRate: 0,\n beforeSend(evt) {\n // Strip any auto-populated user data the SDK may inject.\n delete evt.user;\n delete evt.request;\n delete evt.contexts;\n return evt;\n },\n });\n this.initialized = true;\n }\n const Sentry = await loadSentry();\n if (Sentry === null) return;\n Sentry.captureEvent({\n message: `wotw init failure: ${event.category}`,\n level: \"error\",\n tags: {\n category: event.category,\n daemon_version: event.daemonVersion,\n platform: event.platform,\n arch: event.arch,\n node_version: event.nodeVersion,\n stage: event.stage ?? \"(none)\",\n },\n });\n await Sentry.flush(2000);\n } catch (sendErr) {\n // Telemetry MUST be best-effort.\n log.debug({ sendErr }, \"telemetry send failed (suppressed)\");\n }\n }\n}\n\ninterface SentryShim {\n init(opts: {\n dsn: string;\n defaultIntegrations: false;\n tracesSampleRate: number;\n beforeSend(evt: { user?: unknown; request?: unknown; contexts?: unknown }): unknown;\n }): void;\n captureEvent(evt: { message: string; level: string; tags: Record<string, string> }): void;\n flush(timeoutMs: number): Promise<boolean>;\n}\n\nasync function loadSentry(): Promise<SentryShim | null> {\n try {\n // @sentry/node is an OPTIONAL peer dependency. The default install of\n // @3030-labs/wotw does NOT pull it in — users who opt in to\n // telemetry install it explicitly (`npm install @sentry/node`).\n // Use a dynamic specifier so TypeScript doesn't require its types\n // to be present at typecheck time.\n const specifier = \"@sentry/node\";\n const mod = (await import(/* webpackIgnore: true */ specifier)) as {\n default?: SentryShim;\n } & SentryShim;\n return mod.default ?? mod;\n } catch {\n return null;\n }\n}\n\n/**\n * Factory: choose a sink based on the active environment. Defaults\n * to {@link NoopSink} unless `WOTW_TELEMETRY_DSN` is set.\n */\nexport function getTelemetrySink(env: NodeJS.ProcessEnv = process.env): TelemetrySink {\n const dsn = env.WOTW_TELEMETRY_DSN;\n if (dsn === undefined || dsn.trim().length === 0) {\n return new NoopSink();\n }\n return new SentrySink(dsn.trim());\n}\n","/**\n * Map an ActionableError code (or arbitrary error) to a stable\n * TelemetryFailureCategory. The mapping is deterministic and\n * exhaustive — every ActionableError code in the v0.8.4 surface\n * has a corresponding category. Unknown errors map to\n * `init/unknown-failure`.\n */\nimport { arch, platform } from \"node:os\";\nimport { type ActionableErrorCode, isActionableError } from \"../utils/actionable-error.js\";\nimport { VERSION } from \"../utils/version.js\";\nimport type { TelemetryFailureCategory, TelemetryFailureEvent, TelemetrySink } from \"./types.js\";\n\nconst ACTIONABLE_TO_CATEGORY: Record<ActionableErrorCode, TelemetryFailureCategory> = {\n MISSING_VAULT_PATH: \"init/missing-vault-path\",\n INIT_TARGET_NOT_EMPTY: \"init/target-not-empty\",\n CONFIG_PARSE_ERROR: \"init/config-parse-error\",\n NATIVE_BINDING_LOAD_FAILURE: \"init/native-binding-load-failure\",\n WIKI_DIR_PERMISSION_DENIED: \"init/wiki-dir-permission-denied\",\n PORT_IN_USE: \"init/port-in-use\",\n DAEMON_ALREADY_RUNNING: \"init/daemon-already-running\",\n // The next three actionable codes can fire outside of init but are\n // mapped for symmetry — when they DO fire during init they categorize\n // sensibly. Init-time emission is gated by the caller; this mapping\n // is just the translation.\n INVALID_API_KEY: \"init/runtime-not-detected\",\n RATE_LIMITED: \"init/runtime-not-detected\",\n VAULT_FILE_LOCKED: \"init/scaffold-failed\",\n};\n\nexport function categorizeInitFailure(err: unknown): TelemetryFailureCategory {\n if (isActionableError(err)) {\n return ACTIONABLE_TO_CATEGORY[err.code] ?? \"init/unknown-failure\";\n }\n return \"init/unknown-failure\";\n}\n\n/**\n * Convenience helper: build the full event payload + emit to the sink.\n * Wraps the sink call with try/catch — telemetry must never crash the\n * caller, even on misconfigured sinks.\n */\nexport function recordInitFailure(\n sink: TelemetrySink,\n err: unknown,\n stage?: string,\n): TelemetryFailureEvent {\n const event: TelemetryFailureEvent = {\n category: categorizeInitFailure(err),\n daemonVersion: VERSION,\n platform: platform(),\n arch: arch(),\n nodeVersion: process.version,\n ...(stage !== undefined ? { stage } : {}),\n };\n try {\n sink.recordInitFailure(event);\n } catch {\n // Telemetry failure must NOT propagate.\n }\n return event;\n}\n","/**\n * `wotw start` — start the daemon. Supports both foreground and detached modes.\n *\n * Foreground: runs the daemon in this process (Ctrl-C to stop). Useful for dev.\n * Detached: forks a child process that survives terminal close. Default.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { checkDaemonAlive } from \"../../daemon/lifecycle.js\";\nimport { runDaemonForeground, spawnDaemon } from \"../../daemon/process-manager.js\";\nimport { getLogger } from \"../../utils/logger.js\";\nimport { fail, info, success } from \"../output.js\";\n\ninterface StartOptions {\n detach?: boolean;\n foreground?: boolean;\n config?: string;\n autoApprove?: boolean;\n}\n\n/**\n * Attach the `start` subcommand.\n */\nexport function registerStartCommand(program: Command): void {\n program\n .command(\"start\")\n .description(\"Start the watcher-on-the-wall daemon\")\n .option(\"-d, --detach\", \"Run as a detached background process (default)\")\n .option(\"-f, --foreground\", \"Run in the foreground (Ctrl-C to stop)\")\n .option(\"-c, --config <path>\", \"Path to a config file\")\n .option(\"--auto-approve\", \"Bypass candidates staging (pages go directly to wiki/)\")\n .action(async (opts: StartOptions) => {\n try {\n await runStart(opts);\n } catch (err) {\n fail(`start failed: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n });\n}\n\n/**\n * Implementation used by the CLI action.\n */\nexport async function runStart(opts: StartOptions): Promise<void> {\n const foreground = opts.foreground === true;\n const detached = !foreground && opts.detach !== false;\n\n // Always load config first so we know the PID / log file locations.\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n\n // --auto-approve disables staging for this daemon run.\n if (opts.autoApprove === true) {\n config.ingestion.staging = false;\n }\n\n const alive = checkDaemonAlive(config.daemon.pid_file);\n if (alive.alive) {\n info(`Daemon is already running (PID ${alive.pid}).`);\n return;\n }\n if (alive.stale && alive.pid !== null) {\n info(`Stale PID file detected (PID ${alive.pid} no longer alive). Cleaning up.`);\n }\n\n if (foreground) {\n info(\"Starting daemon in foreground mode. Press Ctrl-C to stop.\");\n // Spawn the same fully-wired entry.js the detached path uses, attached\n // to this terminal. Previously this ran a bare `new Daemon()` with no\n // subsystems registered — it \"started\" but ingested nothing\n // (PASS-023 dogfood finding #18).\n const exitCode = await runDaemonForeground({\n configPath: loaded.path,\n pidFile: config.daemon.pid_file,\n logFile: config.daemon.log_file,\n workingDir: process.cwd(),\n });\n process.exitCode = exitCode;\n return;\n }\n\n if (detached) {\n info(\"Starting daemon in detached mode...\");\n try {\n const { pid } = await spawnDaemon({\n configPath: loaded.path,\n pidFile: config.daemon.pid_file,\n logFile: config.daemon.log_file,\n workingDir: process.cwd(),\n });\n success(`Daemon started. PID ${pid}.`);\n info(`Logs: ${config.daemon.log_file}`);\n info(`MCP server will bind to http://${config.server.host}:${config.server.port}/mcp`);\n } catch (err) {\n const log = getLogger(\"cli:start\");\n log.error({ err }, \"spawnDaemon failed\");\n throw err;\n }\n }\n}\n","/**\n * Process manager for spawning the daemon as a true background process.\n * Parent calls {@link spawnDaemon}, which spawns a detached child using\n * `child_process.spawn(process.execPath, ...)`, unrefs it, and returns the\n * child's PID once it has written its PID file (or times out).\n *\n * We intentionally avoid {@link fork} because it opens an IPC channel that\n * keeps the parent alive even after {@link ChildProcess.unref}. Spawn with\n * `stdio: 'ignore'` has no such channel and lets the parent exit cleanly.\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirExists } from \"../utils/fs.js\";\nimport { readPidFile } from \"./lifecycle.js\";\n\n/**\n * Resolve the path to the daemon entrypoint built by tsup.\n */\nexport function resolveDaemonEntrypoint(): string {\n // When bundled by tsup, daemon code lives alongside the CLI entrypoint.\n // We use import.meta.url of *this* module and walk up to the dist root.\n const here = fileURLToPath(import.meta.url);\n const distRoot = resolve(here, \"..\", \"..\"); // dist/daemon -> dist\n const candidates = [\n resolve(distRoot, \"daemon\", \"entry.js\"),\n resolve(distRoot, \"daemon-entry.js\"),\n resolve(distRoot, \"daemon\", \"index.js\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n // Fallback: assume it lives next to the CLI in a flat dist layout\n return resolve(distRoot, \"daemon\", \"entry.js\");\n}\n\nexport interface SpawnDaemonOptions {\n configPath: string | null;\n pidFile: string;\n logFile: string;\n workingDir: string;\n entrypoint?: string;\n timeoutMs?: number;\n}\n\n/**\n * Spawn the daemon as a detached child process. Waits for the PID file to appear\n * before returning, so callers know the daemon has fully initialized.\n */\nexport async function spawnDaemon(opts: SpawnDaemonOptions): Promise<{ pid: number }> {\n const entry = opts.entrypoint ?? resolveDaemonEntrypoint();\n if (!dirExists(opts.workingDir)) {\n throw new Error(`spawnDaemon: working directory does not exist: ${opts.workingDir}`);\n }\n if (!existsSync(entry)) {\n throw new Error(\n `spawnDaemon: daemon entrypoint not found at ${entry}. Did you run 'pnpm build'?`,\n );\n }\n\n const args = [entry, \"--daemon-child\"];\n if (opts.configPath) args.push(\"--config\", opts.configPath);\n\n const child = spawn(process.execPath, args, {\n cwd: opts.workingDir,\n detached: true,\n stdio: \"ignore\",\n env: {\n ...process.env,\n WOTW_DAEMON_CHILD: \"1\",\n WOTW_PID_FILE: opts.pidFile,\n WOTW_LOG_FILE: opts.logFile,\n },\n });\n\n // Detach so the child survives parent exit\n child.unref();\n\n // Wait for the child to write its PID file (or exit with an error)\n const timeoutMs = opts.timeoutMs ?? 15_000;\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const pidContents = readPidFile(opts.pidFile);\n if (pidContents && pidContents.pid === child.pid) {\n return { pid: child.pid };\n }\n if (child.exitCode !== null) {\n throw new Error(\n `Daemon child exited prematurely with code ${child.exitCode}. ` +\n `Early-init errors (config validation, native-binding load, ` +\n `permission denied on wiki_root/raw_path) land in the daemon ` +\n `log file, not on stdout. Check it for the actual cause:\\n` +\n ` cat ${opts.logFile}\\n` +\n `If the log is empty or missing, the child crashed before ` +\n `logger initialization — re-run with \\`wotw start --foreground\\` ` +\n `to see the error directly on your terminal.`,\n );\n }\n await new Promise((r) => setTimeout(r, 100));\n }\n // Timed out — tear down the child if still alive\n try {\n if (child.pid) process.kill(child.pid, \"SIGTERM\");\n } catch {\n /* ignore */\n }\n throw new Error(\n `Daemon did not initialize within ${timeoutMs}ms. ` +\n `If the daemon is still alive but slow to bind, check ${opts.logFile} ` +\n `for progress logs and increase startup timeout if needed.`,\n );\n}\n\nexport interface RunForegroundOptions {\n configPath: string | null;\n pidFile: string;\n logFile: string;\n workingDir: string;\n entrypoint?: string;\n}\n\n/**\n * Run the daemon in the FOREGROUND by spawning the same fully-wired\n * `entry.js` the detached path uses — but attached to this terminal\n * (`stdio: \"inherit\"`, no detach). Resolves when the child exits with the\n * child's exit code.\n *\n * Why spawn rather than run in-process: `entry.js` wires every subsystem\n * (ingestion, watcher, MCP server, fact store) via a module-level\n * `void main()` side effect. Importing it into the CLI process would be\n * fragile; spawning the identical entrypoint reuses the validated path\n * verbatim. This closes PASS-023 dogfood finding #18 — previously the\n * foreground branch ran a bare `new Daemon()` with ZERO subsystems\n * registered, so `wotw start --foreground` looked like it started but\n * ingested nothing.\n *\n * SIGINT/SIGTERM are forwarded to the child so Ctrl-C stops the daemon\n * cleanly (the child's own signal handlers run its graceful shutdown).\n */\nexport async function runDaemonForeground(opts: RunForegroundOptions): Promise<number> {\n const entry = opts.entrypoint ?? resolveDaemonEntrypoint();\n if (!dirExists(opts.workingDir)) {\n throw new Error(`runDaemonForeground: working directory does not exist: ${opts.workingDir}`);\n }\n if (!existsSync(entry)) {\n throw new Error(\n `runDaemonForeground: daemon entrypoint not found at ${entry}. Did you run 'pnpm build'?`,\n );\n }\n\n const args = [entry, \"--daemon-child\"];\n if (opts.configPath) args.push(\"--config\", opts.configPath);\n\n const child = spawn(process.execPath, args, {\n cwd: opts.workingDir,\n detached: false,\n stdio: \"inherit\",\n env: {\n ...process.env,\n WOTW_DAEMON_CHILD: \"1\",\n WOTW_PID_FILE: opts.pidFile,\n WOTW_LOG_FILE: opts.logFile,\n // Stream pretty logs to the inherited terminal so the foreground run\n // isn't silent (the daemon would otherwise write JSON to the log file).\n WOTW_LOG_STDOUT: \"1\",\n },\n });\n\n const forward = (signal: NodeJS.Signals): void => {\n if (child.pid && child.exitCode === null) {\n try {\n child.kill(signal);\n } catch {\n /* child already gone */\n }\n }\n };\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n\n return await new Promise<number>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n process.off(\"SIGINT\", forward);\n process.off(\"SIGTERM\", forward);\n // Signal-terminated (e.g. Ctrl-C) is a clean foreground stop → 0.\n resolve(signal !== null ? 0 : (code ?? 0));\n });\n });\n}\n","/**\n * `wotw stop` — gracefully shut down the running daemon by sending SIGTERM.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { checkDaemonAlive, removePidFile, terminateAndWait } from \"../../daemon/lifecycle.js\";\nimport { fail, info, success, warn } from \"../output.js\";\n\ninterface StopOptions {\n timeout?: string;\n force?: boolean;\n}\n\n/**\n * Attach the `stop` subcommand.\n */\nexport function registerStopCommand(program: Command): void {\n program\n .command(\"stop\")\n .description(\"Stop the watcher-on-the-wall daemon\")\n .option(\"-t, --timeout <ms>\", \"Seconds to wait for graceful shutdown\", \"10\")\n .option(\"-f, --force\", \"Force-kill (SIGKILL) if graceful shutdown times out\")\n .action(async (opts: StopOptions) => {\n try {\n await runStop(opts);\n } catch (err) {\n fail(`stop failed: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n });\n}\n\n/**\n * Implementation used by the CLI action.\n */\nexport async function runStop(opts: StopOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n\n const status = checkDaemonAlive(config.daemon.pid_file);\n if (!status.alive) {\n if (status.stale && status.pid !== null) {\n warn(`Stale PID file (PID ${status.pid} not running). Cleaning up.`);\n removePidFile(config.daemon.pid_file);\n } else {\n info(\"Daemon is not running.\");\n }\n return;\n }\n\n const timeoutMs = Math.max(1, Number(opts.timeout ?? \"10\")) * 1000;\n info(`Stopping daemon (PID ${status.pid})...`);\n const exited = await terminateAndWait(status.pid as number, timeoutMs);\n\n if (!exited) {\n if (opts.force) {\n warn(\"Graceful shutdown timed out. Sending SIGKILL.\");\n try {\n process.kill(status.pid as number, \"SIGKILL\");\n } catch {\n /* ignore */\n }\n removePidFile(config.daemon.pid_file);\n success(\"Daemon force-killed.\");\n } else {\n fail(\"Daemon did not exit within timeout. Use --force to SIGKILL.\");\n process.exitCode = 1;\n return;\n }\n } else {\n removePidFile(config.daemon.pid_file);\n success(\"Daemon stopped.\");\n }\n}\n","/**\n * `wotw status` — print daemon health, wiki stats, recent events, cost summary.\n *\n * In Phase 1 the stats are sourced directly from the filesystem (PID file + wiki\n * directory counts). In later phases we will also report provenance chain length\n * and recent events from the wiki-events.jsonl log.\n */\nimport type { Command } from \"commander\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { checkDaemonAlive } from \"../../daemon/lifecycle.js\";\nimport { sumCostsForDay } from \"../../ingestion/cost-tracker.js\";\nimport { DeadLetterQueue } from \"../../ingestion/dead-letter.js\";\nimport { box, chalk, info, keyValueTable, line } from \"../output.js\";\n\ninterface StatusOptions {\n json?: boolean;\n}\n\n/**\n * Attach the `status` subcommand.\n */\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Show daemon health and wiki stats\")\n .option(\"--json\", \"Emit machine-readable JSON instead of a pretty table\")\n .action(async (opts: StatusOptions) => {\n await runStatus(opts);\n });\n}\n\n/**\n * Implementation used by the CLI action.\n */\nexport async function runStatus(opts: StatusOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const status = checkDaemonAlive(config.daemon.pid_file);\n\n const wikiRoot = config.wiki_root;\n const pageCount = countMarkdownFiles(join(wikiRoot, \"wiki\"));\n const orphanedCount = countOrphanedPages(join(wikiRoot, \"wiki\"));\n const rawCount = countFiles(config.raw_path);\n // `resolveConfigPaths` already resolves chain_file to an absolute path\n // (against wiki_root). Re-joining with wikiRoot here double-prefixes it\n // into a nonexistent path, so the count silently read 0 even with a full\n // chain on disk (PASS-023 dogfood finding #23). Use the resolved path\n // directly.\n const provenanceCount = countProvenanceRecords(config.provenance.chain_file);\n // Shared helper from cost-tracker — single source of truth for JSONL\n // parsing (L-CODE-3). Previously `status` re-implemented its own parser\n // which trivially drifted from CostTracker.spentToday().\n const costToday = sumCostsForDay(config.cost.track_file);\n // Dead-letter count — cheap read from the JSONL sink. Empty-path\n // config disables the queue (count() returns 0 without touching disk).\n const deadLetter = new DeadLetterQueue({ path: config.ingestion.dead_letter_file });\n const failedBatches = await deadLetter.count();\n\n const uptimeSeconds = status.contents\n ? Math.floor((Date.now() - Date.parse(status.contents.started_at)) / 1000)\n : null;\n\n const payload = {\n running: status.alive,\n pid: status.pid,\n stale_pid_file: status.stale,\n started_at: status.contents?.started_at ?? null,\n uptime_seconds: uptimeSeconds,\n config_path: loaded.path,\n wiki_root: wikiRoot,\n raw_path: config.raw_path,\n server: {\n host: config.server.host,\n port: config.server.port,\n },\n stats: {\n wiki_pages: pageCount,\n orphaned_pages: orphanedCount,\n raw_files: rawCount,\n provenance_records: provenanceCount,\n failed_batches: failedBatches,\n cost_today_usd: Number(costToday.toFixed(4)),\n },\n };\n\n if (opts.json === true) {\n line(JSON.stringify(payload, null, 2));\n return;\n }\n\n const running = status.alive ? chalk.green(\"● running\") : chalk.red(\"● stopped\");\n // Compute a quick health summary (no LLM calls — pure computation).\n let healthLine = \"—\";\n if (pageCount > 0) {\n try {\n const { WikiStore } = await import(\"../../wiki/store.js\");\n const { WikiSearch } = await import(\"../../wiki/search.js\");\n const { loadAllPages } = await import(\"../../ingestion/wiki-writer.js\");\n const { computeHealthReport } = await import(\"../../wiki/health.js\");\n const store = new WikiStore({ wikiRoot });\n const search = new WikiSearch();\n const allPages = await loadAllPages(store);\n search.rebuild(allPages);\n const report = await computeHealthReport(store, null, search, { config });\n const avg =\n report.scores.length > 0\n ? Math.round(report.scores.reduce((s, p) => s + p.score, 0) / report.scores.length)\n : 0;\n const belowFifty = report.scores.filter((s) => s.score < 50).length;\n healthLine = `${avg} avg`;\n if (belowFifty > 0) healthLine += chalk.yellow(` (${belowFifty} need attention)`);\n } catch {\n healthLine = \"error computing\";\n }\n }\n\n // Query health — zero-hit rate monitoring.\n let queryHealthLine = \"no queries recorded\";\n try {\n const { computeZeroHitRate } = await import(\"../../server/query-metrics.js\");\n const metrics = computeZeroHitRate(config.health.query_log_file);\n if (metrics.total_queries > 0) {\n const pct = (metrics.zero_hit_rate * 100).toFixed(0);\n queryHealthLine = `${pct}% zero-hit rate (${metrics.zero_hits}/${metrics.total_queries} queries, 7d window)`;\n if (metrics.zero_hit_rate > config.health.zero_hit_threshold) {\n queryHealthLine = chalk.yellow(queryHealthLine);\n }\n }\n } catch {\n queryHealthLine = \"—\";\n }\n\n const rows: Array<[string, string]> = [\n [\"status\", running],\n [\"pid\", status.pid !== null ? String(status.pid) : \"—\"],\n [\"started\", status.contents?.started_at ?? \"—\"],\n [\"uptime\", uptimeSeconds !== null ? formatDuration(uptimeSeconds) : \"—\"],\n [\"config\", loaded.path ?? \"(defaults)\"],\n [\"wiki root\", wikiRoot],\n [\"raw path\", config.raw_path],\n [\"server\", `http://${config.server.host}:${config.server.port}/mcp`],\n [\"wiki pages\", String(pageCount)],\n [\n \"orphaned pages\",\n orphanedCount > 0 ? chalk.yellow(String(orphanedCount)) : String(orphanedCount),\n ],\n [\"wiki health\", healthLine],\n [\"query health\", queryHealthLine],\n [\"raw files\", String(rawCount)],\n [\"provenance records\", String(provenanceCount)],\n [\n \"failed batches\",\n failedBatches > 0 ? chalk.red(String(failedBatches)) : String(failedBatches),\n ],\n [\"cost today\", `$${costToday.toFixed(4)}`],\n ];\n box(keyValueTable(rows), \"watcher-on-the-wall\");\n if (failedBatches > 0) {\n info(\n `${failedBatches} permanently-failed batch(es) in ${config.ingestion.dead_letter_file}. ` +\n `Inspect with 'wotw logs' or replay manually.`,\n );\n }\n\n if (status.stale) {\n info(\"A stale PID file was detected and is being ignored. Run `wotw start` to relaunch.\");\n }\n}\n\nfunction countMarkdownFiles(dir: string): number {\n if (!existsSync(dir)) return 0;\n let count = 0;\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(d, name);\n let s: ReturnType<typeof statSync>;\n try {\n s = statSync(full);\n } catch {\n continue;\n }\n if (s.isDirectory()) walk(full);\n else if (s.isFile() && name.endsWith(\".md\")) count++;\n }\n };\n walk(dir);\n return count;\n}\n\n/**\n * Count wiki pages whose frontmatter is marked `status: orphaned`. Uses\n * gray-matter directly instead of going through parsePage so we don't\n * pay for body parsing on pages we only peek at. Best-effort: anything\n * that fails to parse is ignored.\n */\nfunction countOrphanedPages(dir: string): number {\n if (!existsSync(dir)) return 0;\n let count = 0;\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(d, name);\n let s: ReturnType<typeof statSync>;\n try {\n s = statSync(full);\n } catch {\n continue;\n }\n if (s.isDirectory()) {\n walk(full);\n continue;\n }\n if (!s.isFile() || !name.endsWith(\".md\")) continue;\n try {\n const raw = readFileSync(full, \"utf8\");\n const parsed = matter(raw);\n if ((parsed.data as Record<string, unknown>).status === \"orphaned\") count++;\n } catch {\n /* ignore — a partially-written file should not break `status` */\n }\n }\n };\n walk(dir);\n return count;\n}\n\nfunction countFiles(dir: string): number {\n if (!existsSync(dir)) return 0;\n let count = 0;\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(d, name);\n let s: ReturnType<typeof statSync>;\n try {\n s = statSync(full);\n } catch {\n continue;\n }\n if (s.isDirectory()) walk(full);\n else if (s.isFile()) count++;\n }\n };\n walk(dir);\n return count;\n}\n\nfunction countProvenanceRecords(chainFile: string): number {\n if (!existsSync(chainFile)) return 0;\n try {\n const text = readFileSync(chainFile, \"utf8\");\n return text.split(\"\\n\").filter((l) => l.trim().length > 0).length;\n } catch {\n return 0;\n }\n}\n\nfunction formatDuration(totalSeconds: number): string {\n const h = Math.floor(totalSeconds / 3600);\n const m = Math.floor((totalSeconds % 3600) / 60);\n const s = totalSeconds % 60;\n if (h > 0) return `${h}h ${m}m ${s}s`;\n if (m > 0) return `${m}m ${s}s`;\n return `${s}s`;\n}\n","/**\n * `wotw query \"question\"` — query the running daemon's MCP server over HTTP.\n *\n * This issues a single JSON-RPC call to the `query` tool and pretty-prints\n * the answer. If no daemon is running we tell the user to start one.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { checkDaemonAlive } from \"../../daemon/lifecycle.js\";\nimport { callMcpTool } from \"./lib/mcp-client.js\";\nimport { box, fail, info, line } from \"../output.js\";\n\ninterface QueryOptions {\n json?: boolean;\n k?: string;\n}\n\n/**\n * Attach the `query` subcommand.\n */\nexport function registerQueryCommand(program: Command): void {\n program\n .command(\"query <question>\")\n .description(\"Ask the wiki a natural-language question\")\n .option(\"--json\", \"Emit JSON instead of prose\")\n .option(\"-k, --k <count>\", \"Number of retrieved pages to use (1-20)\", \"8\")\n .action(async (question: string, opts: QueryOptions) => {\n await runQuery(question, opts);\n });\n}\n\n/** Query implementation. */\nexport async function runQuery(question: string, opts: QueryOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const status = checkDaemonAlive(config.daemon.pid_file);\n\n if (!status.alive) {\n fail(\"No daemon is running. Start one with `wotw start`.\");\n process.exitCode = 1;\n return;\n }\n\n const k = Math.max(1, Math.min(20, Number(opts.k) || 8));\n info(`Asking the wiki...`);\n try {\n const result = await callMcpTool({\n host: config.server.host,\n port: config.server.port,\n authToken: config.server.auth_token,\n tool: \"query\",\n args: { question, k },\n });\n if (opts.json) {\n line(JSON.stringify(result, null, 2));\n return;\n }\n const text = extractText(result);\n box(text || \"(no answer)\", \"answer\");\n } catch (err) {\n fail(`Query failed: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n}\n\nfunction extractText(result: unknown): string {\n const r = result as { content?: { type?: string; text?: string }[] };\n if (!r?.content) return \"\";\n return r.content\n .filter((c) => c.type === \"text\")\n .map((c) => c.text ?? \"\")\n .join(\"\\n\\n\");\n}\n","/**\n * Small MCP client used by CLI commands to talk to the daemon over HTTP.\n * Uses the SDK's StreamableHTTPClientTransport so our format stays in sync\n * with whatever version of the spec the server is using.\n */\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { VERSION } from \"../../../utils/version.js\";\n\nexport interface McpCallOptions {\n host: string;\n port: number;\n authToken: string | null;\n tool: string;\n args: Record<string, unknown>;\n /**\n * Per-request timeout in milliseconds. Defaults to 60s which is fine for\n * interactive queries; long-running tools like `synthesize` should pass a\n * larger value (e.g. 10 minutes).\n */\n timeoutMs?: number;\n}\n\n/**\n * Connect to the daemon's MCP server, call a tool, and return the result.\n */\nexport async function callMcpTool(opts: McpCallOptions): Promise<unknown> {\n const url = new URL(`http://${opts.host}:${opts.port}/mcp`);\n const transport = new StreamableHTTPClientTransport(url, {\n requestInit: opts.authToken\n ? { headers: { authorization: `Bearer ${opts.authToken}` } }\n : undefined,\n });\n const client = new Client({ name: \"wotw-cli\", version: VERSION }, { capabilities: {} });\n try {\n await client.connect(transport);\n const result = await client.callTool(\n { name: opts.tool, arguments: opts.args },\n undefined,\n opts.timeoutMs ? { timeout: opts.timeoutMs } : undefined,\n );\n return result;\n } finally {\n await client.close().catch(() => undefined);\n await transport.close().catch(() => undefined);\n }\n}\n","/**\n * `wotw audit [page]` — walk the provenance chain for a given wiki page, or\n * verify the entire chain with `--verify`.\n *\n * The chain is read directly from disk — no daemon required. This is by\n * design: audit should work even if the daemon is down or compromised, and\n * the chain itself is the source of truth.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { relative, resolve } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { ProvenanceChain } from \"../../provenance/chain.js\";\nimport type { ProvenanceRecord } from \"../../utils/types.js\";\nimport { chalk, fail, info, keyValueTable, line, success, warn } from \"../output.js\";\n\ninterface AuditOptions {\n verify?: boolean;\n format?: \"tree\" | \"json\";\n limit?: string;\n}\n\n/**\n * Attach the `audit` subcommand.\n */\nexport function registerAuditCommand(program: Command): void {\n program\n .command(\"audit [page]\")\n .description(\"Walk the cryptographic provenance chain for a wiki page\")\n .option(\"--verify\", \"Verify the entire chain instead of walking a single page\")\n .option(\"--format <fmt>\", \"Output format: tree | json\", \"tree\")\n .option(\"--limit <n>\", \"Max records to show when no page is given\", \"20\")\n .action(async (page: string | undefined, opts: AuditOptions) => {\n await runAudit(page, opts);\n });\n}\n\n/**\n * Implementation used by the CLI action.\n */\nexport async function runAudit(page: string | undefined, opts: AuditOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n\n if (!config.provenance.enabled) {\n warn(\"Provenance is disabled in config (provenance.enabled = false).\");\n return;\n }\n\n const chainFile = config.provenance.chain_file;\n if (!existsSync(chainFile)) {\n fail(`No provenance chain found at ${chainFile}.`);\n info(\"The chain is created when the daemon processes its first ingestion or query.\");\n process.exitCode = 1;\n return;\n }\n\n const chain = new ProvenanceChain({ path: chainFile });\n await chain.init();\n\n if (opts.verify) {\n await verifyChain(chain, opts);\n return;\n }\n\n if (!page) {\n await listRecent(chain, opts);\n return;\n }\n\n await walkPage(chain, config.wiki_root, page, opts);\n}\n\n/** Verify the full chain and print a report. */\nasync function verifyChain(chain: ProvenanceChain, opts: AuditOptions): Promise<void> {\n info(\"Walking provenance chain...\");\n const result = await chain.verify();\n if (opts.format === \"json\") {\n line(\n JSON.stringify(\n {\n ok: result.ok,\n total: result.totalRecords,\n verified: result.verifiedRecords,\n errors: result.errors,\n head: chain.head(),\n signature: await chain.signature(),\n },\n null,\n 2,\n ),\n );\n if (!result.ok) process.exitCode = 1;\n return;\n }\n line(\"\");\n line(\n keyValueTable([\n [\"total records\", String(result.totalRecords)],\n [\"verified\", String(result.verifiedRecords)],\n [\"errors\", String(result.errors.length)],\n [\"head\", chain.head().slice(0, 16) + \"…\"],\n [\"signature\", (await chain.signature()).slice(0, 16) + \"…\"],\n ]),\n );\n line(\"\");\n if (result.ok) {\n success(`Chain intact: ${result.totalRecords} record(s) verified.`);\n } else {\n fail(`Chain corrupt: ${result.errors.length} error(s) found.`);\n for (const err of result.errors.slice(0, 10)) {\n line(` ${chalk.red(\"×\")} seq=${err.seq} id=${err.id.slice(0, 12)} — ${err.reason}`);\n }\n process.exitCode = 1;\n }\n}\n\n/** List recent records when no specific page is given. */\nasync function listRecent(chain: ProvenanceChain, opts: AuditOptions): Promise<void> {\n const limit = Math.max(1, Math.min(500, Number(opts.limit) || 20));\n const records = await chain.readRecent(limit);\n if (records.length === 0) {\n info(\"Provenance chain is empty.\");\n return;\n }\n if (opts.format === \"json\") {\n line(JSON.stringify({ total: chain.count(), records }, null, 2));\n return;\n }\n line(\"\");\n line(chalk.dim(`Showing ${records.length} of ${chain.count()} records:`));\n line(\"\");\n for (const r of records) {\n printRecord(r);\n }\n line(\"\");\n info(`Run 'wotw audit <wiki/path.md>' to see history for a specific page.`);\n info(`Run 'wotw audit --verify' to check chain integrity.`);\n}\n\n/** Walk the provenance trail for a specific page. */\nasync function walkPage(\n chain: ProvenanceChain,\n wikiRoot: string,\n page: string,\n opts: AuditOptions,\n): Promise<void> {\n const normalized = normalizePagePath(wikiRoot, page);\n const records = await chain.recordsFor(normalized);\n if (records.length === 0) {\n warn(`No provenance records found for ${normalized}.`);\n return;\n }\n if (opts.format === \"json\") {\n line(JSON.stringify({ page: normalized, records }, null, 2));\n return;\n }\n line(\"\");\n line(chalk.bold(`History for ${normalized}:`));\n line(chalk.dim(` ${records.length} record(s) in chain`));\n line(\"\");\n for (const r of records) printRecord(r);\n}\n\n/** Pretty-print a single provenance record. */\nfunction printRecord(r: ProvenanceRecord): void {\n const icon =\n r.type === \"ingest\" ? \"⇥\" : r.type === \"query\" ? \"?\" : r.type === \"compound\" ? \"⊕\" : \"•\";\n const cost =\n typeof r.metadata?.cost_usd === \"number\" ? `$${r.metadata.cost_usd.toFixed(4)}` : \"—\";\n line(\n ` ${chalk.cyan(icon)} ${chalk.dim(`#${r.seq}`)} ${chalk.bold(r.type.padEnd(8))} ${chalk.dim(r.timestamp)}`,\n );\n line(` id=${chalk.yellow(r.id.slice(0, 12))} model=${r.model_id} cost=${cost}`);\n if (r.wiki_files_written.length > 0) {\n line(\n ` wrote: ${r.wiki_files_written.slice(0, 3).join(\", \")}${r.wiki_files_written.length > 3 ? `, +${r.wiki_files_written.length - 3} more` : \"\"}`,\n );\n }\n if (r.source_files.length > 0) {\n line(\n ` from: ${r.source_files.slice(0, 3).join(\", \")}${r.source_files.length > 3 ? `, +${r.source_files.length - 3} more` : \"\"}`,\n );\n }\n}\n\n/**\n * Normalize a user-provided page argument into the form stored in the\n * provenance chain (wiki-relative path as-is in `wiki_files_written`).\n */\nfunction normalizePagePath(wikiRoot: string, page: string): string {\n // If user gave an absolute path, convert to wiki-relative.\n if (page.startsWith(\"/\")) {\n const rel = relative(wikiRoot, resolve(page));\n return rel || page;\n }\n // Strip leading \"./\"\n return page.replace(/^\\.\\//, \"\");\n}\n","/**\n * `wotw logs` — tail the daemon log file.\n *\n * Two modes:\n * - Default: print the last N lines (default 20) and exit.\n * - `--follow` / `-f`: print the last N lines, then stream new lines\n * as the daemon writes them. Exits cleanly on SIGINT/SIGTERM.\n *\n * The command opens the file directly rather than going through the\n * running daemon — this way the operator can read logs even if the\n * daemon is stopped or crashed. We read in 64KB chunks from the end of\n * the file so very large logs don't blow up memory.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport {\n existsSync,\n openSync,\n readSync,\n statSync,\n closeSync,\n watchFile,\n unwatchFile,\n} from \"node:fs\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { errorLine, line, warn } from \"../output.js\";\n\ninterface LogsOptions {\n follow?: boolean;\n lines?: string;\n}\n\n/** Default number of lines to print when neither `--lines` nor `--follow` are given. */\nconst DEFAULT_LINES = 20;\n\n/** Read chunk size when tailing from the end of the file. */\nconst TAIL_CHUNK = 64 * 1024;\n\n/**\n * Attach the `logs` subcommand.\n */\nexport function registerLogsCommand(program: Command): void {\n program\n .command(\"logs\")\n .description(\"Tail the daemon log file\")\n .option(\"-f, --follow\", \"Follow new log lines as they are written\")\n .option(\"-n, --lines <count>\", \"Number of trailing lines to print (default 20)\")\n .action(async (opts: LogsOptions) => {\n await runLogs(opts);\n });\n}\n\n/**\n * CLI entry point. Resolves the daemon log file from config and prints\n * the last N lines, optionally following new writes.\n */\nexport async function runLogs(opts: LogsOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const logFile = config.daemon.log_file;\n\n const requestedLines = parseLines(opts.lines);\n if (requestedLines === null) {\n errorLine(`invalid --lines value: ${opts.lines}`);\n process.exit(1);\n return;\n }\n\n if (!existsSync(logFile)) {\n warn(`no log file at ${logFile}. Start the daemon first (wotw start).`);\n return;\n }\n\n // Print the tail once, unconditionally. In follow mode we then hook\n // a watcher for new writes; in default mode we return immediately.\n const startSize = printLastLines(logFile, requestedLines);\n\n if (opts.follow !== true) return;\n\n await followLog(logFile, startSize);\n}\n\n/**\n * Parse a `--lines <count>` option. Returns the DEFAULT_LINES value when\n * the option is unset, or `null` for invalid input so the caller can\n * surface a clean error message.\n */\nfunction parseLines(raw: string | undefined): number | null {\n if (raw === undefined) return DEFAULT_LINES;\n const n = Number.parseInt(raw, 10);\n if (Number.isNaN(n) || n < 0) return null;\n return n;\n}\n\n/**\n * Print the last `count` lines of `filePath` to stdout. Reads from the\n * end of the file in fixed-size chunks so we don't load the whole log\n * into memory on a large file. Returns the file size at the time we\n * finished reading, so the follow-mode caller can resume from there.\n */\nfunction printLastLines(filePath: string, count: number): number {\n const stat = statSync(filePath);\n const size = stat.size;\n if (count === 0 || size === 0) return size;\n\n const fd = openSync(filePath, \"r\");\n try {\n let readStart = Math.max(0, size - TAIL_CHUNK);\n let chunk = Buffer.alloc(0);\n // Keep growing the chunk backwards until we have `count` newlines\n // or we've read the whole file.\n // Worst-case this still bounds at `size` bytes because readStart\n // monotonically decreases to 0.\n while (true) {\n const len = size - readStart;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, readStart);\n chunk = buf;\n const newlines = countNewlines(chunk);\n if (newlines >= count || readStart === 0) break;\n readStart = Math.max(0, readStart - TAIL_CHUNK);\n }\n const text = chunk.toString(\"utf8\");\n const lines = text.split(\"\\n\");\n // If the final line is empty (file ends with \\n) drop it so we\n // don't print an extra blank line.\n if (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n const tail = lines.slice(-count);\n for (const l of tail) line(l);\n } finally {\n closeSync(fd);\n }\n return size;\n}\n\n/**\n * Follow `filePath` for new writes after `fromOffset`. Uses {@link watchFile}\n * (poll-based, 250ms) so it works identically across platforms (including\n * WSL where inotify on /home sometimes misses events). Stops cleanly on\n * SIGINT / SIGTERM. Returns a promise that resolves only on shutdown.\n */\nasync function followLog(filePath: string, fromOffset: number): Promise<void> {\n let currentOffset = fromOffset;\n let stopped = false;\n\n const readNew = (): void => {\n try {\n const stat = statSync(filePath);\n // Handle log rotation: if the file shrank, start over from the\n // beginning of the new file.\n if (stat.size < currentOffset) {\n currentOffset = 0;\n }\n if (stat.size === currentOffset) return;\n const fd = openSync(filePath, \"r\");\n try {\n const len = stat.size - currentOffset;\n const buf = Buffer.alloc(len);\n readSync(fd, buf, 0, len, currentOffset);\n const text = buf.toString(\"utf8\");\n // Strip the trailing newline so we don't emit a blank line.\n const trimmed = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n if (trimmed.length > 0) line(trimmed);\n currentOffset = stat.size;\n } finally {\n closeSync(fd);\n }\n } catch (err) {\n errorLine(`log read failed: ${errMsg(err)}`);\n }\n };\n\n watchFile(filePath, { interval: 250 }, () => {\n if (!stopped) readNew();\n });\n\n await new Promise<void>((resolve) => {\n const shutdown = (): void => {\n stopped = true;\n unwatchFile(filePath);\n resolve();\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n}\n\n/** Count the number of `\\n` bytes in a buffer. Faster than toString+split. */\nfunction countNewlines(buf: Buffer): number {\n let n = 0;\n for (let i = 0; i < buf.length; i++) {\n if (buf[i] === 0x0a) n++;\n }\n return n;\n}\n","/**\n * `wotw install-hook` — add a SessionStart hook to the user's Claude Code\n * settings.json that runs the bootstrap script, which in turn ensures a\n * daemon is running before any Claude session begins.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { atomicWriteSync } from \"../../utils/fs.js\";\nimport { dirname, join } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\nimport { fail, info, success } from \"../output.js\";\nimport { fileURLToPath } from \"node:url\";\nimport { resolve } from \"node:path\";\n\ninterface InstallHookOptions {\n scope?: \"user\" | \"project\";\n dryRun?: boolean;\n}\n\n/**\n * Attach the `install-hook` subcommand.\n */\nexport function registerInstallHookCommand(program: Command): void {\n program\n .command(\"install-hook\")\n .description(\"Install a Claude Code SessionStart hook that boots the daemon\")\n .option(\"--scope <scope>\", \"Where to install the hook: user | project\", \"user\")\n .option(\"--dry-run\", \"Print what would be written without modifying files\")\n .action(async (opts: InstallHookOptions) => {\n await runInstallHook(opts);\n });\n}\n\n/**\n * Implementation.\n */\nexport async function runInstallHook(opts: InstallHookOptions): Promise<void> {\n const settingsPath = resolveSettingsPath(opts.scope ?? \"user\");\n const bootstrapScript = resolveBootstrapScript();\n\n const hookCommand =\n platform() === \"win32\"\n ? `powershell -ExecutionPolicy Bypass -File \"${bootstrapScript.ps1}\"`\n : `bash \"${bootstrapScript.sh}\"`;\n\n interface HookEntry {\n matcher?: string;\n hooks: Array<{ type: \"command\"; command: string }>;\n }\n interface Settings {\n hooks?: {\n SessionStart?: HookEntry[];\n };\n [key: string]: unknown;\n }\n\n let existing: Settings = {};\n if (existsSync(settingsPath)) {\n try {\n existing = JSON.parse(readFileSync(settingsPath, \"utf8\")) as Settings;\n } catch (err) {\n fail(`Could not parse existing settings at ${settingsPath}: ${errMsg(err)}`);\n process.exitCode = 1;\n return;\n }\n }\n\n existing.hooks ??= {};\n existing.hooks.SessionStart ??= [];\n\n // Avoid duplicate install\n const already = existing.hooks.SessionStart.some((entry) =>\n entry.hooks.some((h) => h.command === hookCommand),\n );\n if (already) {\n info(\"SessionStart hook already installed.\");\n return;\n }\n\n existing.hooks.SessionStart.push({\n matcher: \"*\",\n hooks: [{ type: \"command\", command: hookCommand }],\n });\n\n if (opts.dryRun) {\n info(`Would write to ${settingsPath}:`);\n info(JSON.stringify(existing, null, 2));\n return;\n }\n\n mkdirSync(dirname(settingsPath), { recursive: true });\n // Review item 63: atomicWriteSync stages a tmp file then renames so a\n // Ctrl-C between the truncate and the body-write can't leave the\n // user's global Claude Code settings.json half-written.\n atomicWriteSync(settingsPath, JSON.stringify(existing, null, 2));\n success(`Installed SessionStart hook at ${settingsPath}`);\n info(`Hook command: ${hookCommand}`);\n}\n\n/** Resolve the Claude Code settings.json path for the chosen scope. */\nfunction resolveSettingsPath(scope: \"user\" | \"project\"): string {\n if (scope === \"project\") {\n return join(process.cwd(), \".claude\", \"settings.json\");\n }\n return join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** Locate the bootstrap scripts bundled in templates/. */\nfunction resolveBootstrapScript(): { sh: string; ps1: string } {\n const here = fileURLToPath(import.meta.url);\n const candidates = [\n resolve(here, \"..\", \"..\", \"..\", \"templates\"),\n resolve(here, \"..\", \"..\", \"..\", \"..\", \"templates\"),\n ];\n for (const c of candidates) {\n if (existsSync(join(c, \"ensure-running.sh\"))) {\n return {\n sh: join(c, \"ensure-running.sh\"),\n ps1: join(c, \"ensure-running.ps1\"),\n };\n }\n }\n // Fall back to the first candidate even if files don't exist yet — templates\n // are written in Phase 5 but the install-hook command still resolves paths.\n const base = candidates[0] as string;\n return {\n sh: join(base, \"ensure-running.sh\"),\n ps1: join(base, \"ensure-running.ps1\"),\n };\n}\n","/**\n * `wotw uninstall-hook` — remove the SessionStart hook installed by\n * `wotw install-hook`.\n */\nimport type { Command } from \"commander\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { info, success, fail } from \"../output.js\";\nimport { atomicWriteSync } from \"../../utils/fs.js\";\n\ninterface UninstallHookOptions {\n scope?: \"user\" | \"project\";\n}\n\n/**\n * Attach the `uninstall-hook` subcommand.\n */\nexport function registerUninstallHookCommand(program: Command): void {\n program\n .command(\"uninstall-hook\")\n .description(\"Remove the Claude Code SessionStart hook installed by wotw\")\n .option(\"--scope <scope>\", \"Where the hook was installed: user | project\", \"user\")\n .action(async (opts: UninstallHookOptions) => {\n await runUninstallHook(opts);\n });\n}\n\nexport async function runUninstallHook(opts: UninstallHookOptions): Promise<void> {\n const scope = opts.scope ?? \"user\";\n const settingsPath =\n scope === \"project\"\n ? join(process.cwd(), \".claude\", \"settings.json\")\n : join(homedir(), \".claude\", \"settings.json\");\n\n if (!existsSync(settingsPath)) {\n info(\"No settings.json found. Nothing to remove.\");\n return;\n }\n\n interface HookEntry {\n matcher?: string;\n hooks: Array<{ type: string; command: string }>;\n }\n interface Settings {\n hooks?: { SessionStart?: HookEntry[] };\n [key: string]: unknown;\n }\n\n // Review item 64: malformed settings.json must not throw unhandled —\n // give the user a clear \"file is corrupt, fix it manually\" message\n // instead of a stack trace.\n let parsed: Settings;\n try {\n parsed = JSON.parse(readFileSync(settingsPath, \"utf8\")) as Settings;\n } catch (err) {\n fail(\n `Failed to parse ${settingsPath}: ${err instanceof Error ? err.message : String(err)}. ` +\n `Fix or remove the file by hand, then re-run \\`wotw uninstall-hook\\`.`,\n );\n return;\n }\n if (!parsed.hooks?.SessionStart || parsed.hooks.SessionStart.length === 0) {\n info(\"No SessionStart hooks to remove.\");\n return;\n }\n\n const before = parsed.hooks.SessionStart.length;\n parsed.hooks.SessionStart = parsed.hooks.SessionStart.filter((entry) => {\n const filtered = entry.hooks.filter((h) => !isWotwHook(h.command));\n entry.hooks = filtered;\n return entry.hooks.length > 0;\n });\n const after = parsed.hooks.SessionStart.length;\n\n // Review item 63: atomic write so Ctrl-C between truncate + body-write\n // can't corrupt the user's global Claude Code settings.json.\n atomicWriteSync(settingsPath, JSON.stringify(parsed, null, 2));\n success(`Removed ${before - after} wotw SessionStart hook entries from ${settingsPath}`);\n}\n\nfunction isWotwHook(command: string): boolean {\n return command.includes(\"ensure-running.sh\") || command.includes(\"ensure-running.ps1\");\n}\n","/**\n * `wotw serve` — standalone MCP server. Does not watch files; only serves the\n * existing wiki to Claude Code sessions over HTTP.\n *\n * The full MCP server is implemented in `src/server/index.ts` and is started\n * automatically by `wotw start`. This standalone serve command is reserved for\n * future use as a lightweight server-only mode without the watcher/daemon.\n */\nimport type { Command } from \"commander\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { info } from \"../output.js\";\n\ninterface ServeOptions {\n port?: string;\n host?: string;\n}\n\n/**\n * Attach the `serve` subcommand.\n */\nexport function registerServeCommand(program: Command): void {\n program\n .command(\"serve\")\n .description(\"Start a standalone MCP server (no watcher, no daemon)\")\n .option(\"-p, --port <port>\", \"Port to bind (overrides config)\")\n .option(\"-h, --host <host>\", \"Host to bind (overrides config)\")\n .action(async (opts: ServeOptions) => {\n await runServe(opts);\n });\n}\n\nexport async function runServe(opts: ServeOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const host = opts.host ?? config.server.host;\n const port = opts.port ? Number(opts.port) : config.server.port;\n\n info(`The standalone MCP server is not yet available as a separate command.`);\n info(\n `Use 'wotw start' to launch the full daemon (watcher + MCP server) at http://${host}:${port}/mcp.`,\n );\n}\n","/**\n * `wotw synthesize` — trigger a compounding synthesis pass on the running\n * daemon. The daemon does the actual work (so we get concurrency safety,\n * budget tracking, and provenance all for free); the CLI is a thin\n * trigger + result formatter.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { checkDaemonAlive } from \"../../daemon/lifecycle.js\";\nimport { callMcpTool } from \"./lib/mcp-client.js\";\nimport { chalk, fail, info, keyValueTable, line, success, warn } from \"../output.js\";\n\ninterface SynthesizeOptions {\n json?: boolean;\n}\n\ninterface ClusterSummary {\n tag: string;\n pages: string[];\n synthesisPath: string | null;\n skipped: boolean;\n reason?: string;\n}\n\ninterface SynthesizeResult {\n skipped: boolean;\n skip_reason: string | null;\n clusters: ClusterSummary[];\n pages_written: number;\n cost_usd: number;\n git_sha: string | null;\n duration_ms: number;\n}\n\n/**\n * Attach the `synthesize` subcommand.\n */\nexport function registerSynthesizeCommand(program: Command): void {\n program\n .command(\"synthesize\")\n .description(\"Run a compounding synthesis pass over the wiki\")\n .option(\"--json\", \"Emit JSON instead of pretty output\")\n .action(async (opts: SynthesizeOptions) => {\n await runSynthesize(opts);\n });\n}\n\nexport async function runSynthesize(opts: SynthesizeOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const status = checkDaemonAlive(config.daemon.pid_file);\n\n if (!status.alive) {\n fail(\"No daemon is running. Start one with `wotw start`.\");\n process.exitCode = 1;\n return;\n }\n\n info(\"Running synthesis pass (this may take a while)...\");\n try {\n const result = await callMcpTool({\n host: config.server.host,\n port: config.server.port,\n authToken: config.server.auth_token,\n tool: \"synthesize\",\n args: {},\n // Synthesis can take several minutes for large wikis. 10m is generous.\n timeoutMs: 10 * 60 * 1000,\n });\n const payload = extractPayload(result);\n if (!payload) {\n fail(\"Synthesis returned no payload.\");\n process.exitCode = 1;\n return;\n }\n if (opts.json) {\n line(JSON.stringify(payload, null, 2));\n return;\n }\n printSynthesizeResult(payload);\n } catch (err) {\n fail(`Synthesis failed: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n}\n\nfunction printSynthesizeResult(result: SynthesizeResult): void {\n line(\"\");\n if (result.skipped) {\n warn(`Skipped: ${result.skip_reason ?? \"unknown reason\"}`);\n return;\n }\n line(\n keyValueTable([\n [\"clusters examined\", String(result.clusters.length)],\n [\"pages written\", String(result.pages_written)],\n [\"cost\", `$${result.cost_usd.toFixed(4)}`],\n [\"git sha\", result.git_sha ?? \"—\"],\n [\"duration\", `${(result.duration_ms / 1000).toFixed(1)}s`],\n ]),\n );\n line(\"\");\n if (result.clusters.length === 0) {\n info(\"No clusters met the threshold.\");\n return;\n }\n for (const c of result.clusters) {\n if (c.skipped) {\n line(\n ` ${chalk.dim(\"○\")} ${chalk.bold(c.tag.padEnd(20))} ${chalk.dim(`${c.pages.length} pages`)} — ${chalk.dim(c.reason ?? \"skipped\")}`,\n );\n } else {\n line(\n ` ${chalk.green(\"●\")} ${chalk.bold(c.tag.padEnd(20))} ${chalk.dim(`${c.pages.length} pages`)} → ${chalk.cyan(c.synthesisPath ?? \"\")}`,\n );\n }\n }\n line(\"\");\n if (result.pages_written > 0) {\n success(`Wrote ${result.pages_written} synthesis page(s).`);\n } else {\n info(\"No new synthesis pages written.\");\n }\n}\n\ninterface McpContentBlock {\n type?: string;\n text?: string;\n}\n\n/** Extract the synthesize payload from the MCP tool result envelope. */\nfunction extractPayload(result: unknown): SynthesizeResult | null {\n const r = result as { content?: McpContentBlock[] };\n const text = r?.content?.find((c) => c.type === \"text\")?.text;\n if (!text) return null;\n try {\n return JSON.parse(text) as SynthesizeResult;\n } catch {\n return null;\n }\n}\n","/**\n * `wotw search <terms>` — offline full-text search over wiki content.\n *\n * Unlike `wotw query` (which sends a question to the daemon's LLM for\n * RAG-style answering), `wotw search` runs locally using MiniSearch. No\n * running daemon required — it loads the wiki directly from disk.\n *\n * Results include page title, matching snippet, relevance score, and path.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { loadAllPages } from \"../../ingestion/wiki-writer.js\";\nimport { WikiSearch } from \"../../wiki/search.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { chalk, fail, info, line, warn } from \"../output.js\";\n\ninterface SearchOptions {\n top?: string;\n json?: boolean;\n open?: boolean;\n domain?: string;\n scope?: string;\n}\n\n/**\n * Attach the `search` subcommand.\n */\nexport function registerSearchCommand(program: Command): void {\n program\n .command(\"search <terms...>\")\n .description(\"Full-text search over wiki content (offline, no daemon needed)\")\n .option(\"--top <n>\", \"Maximum number of results (default 10)\", \"10\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--open\", \"Open the top result in Obsidian\")\n .option(\"--domain <domain>\", \"Filter results by knowledge domain\")\n .option(\"--scope <scope>\", \"Filter results by project/context scope\")\n .action(async (terms: string[], opts: SearchOptions) => {\n await runSearch(terms.join(\" \"), opts);\n });\n}\n\nexport async function runSearch(query: string, opts: SearchOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const wikiRoot = config.wiki_root;\n const wikiDir = join(wikiRoot, \"wiki\");\n\n if (!existsSync(wikiDir)) {\n warn(\"No wiki directory found. Run 'wotw init' first.\");\n return;\n }\n\n const store = new WikiStore({ wikiRoot });\n const search = new WikiSearch();\n const pages = await loadAllPages(store);\n\n if (pages.length === 0) {\n info(\"Wiki is empty. Drop files into raw/ and run 'wotw ingest'.\");\n return;\n }\n\n search.rebuild(pages);\n const limit = Math.max(1, Math.min(100, Number(opts.top) || 10));\n const filters =\n opts.domain || opts.scope ? { domain: opts.domain, scope: opts.scope } : undefined;\n const hits = search.search(query, limit, filters);\n\n if (hits.length === 0) {\n info(`No results for \"${query}\".`);\n return;\n }\n\n if (opts.json === true) {\n line(\n JSON.stringify(\n hits.map((h) => ({\n title: h.title,\n category: h.category,\n path: store.relativePath(h.path),\n score: Number(h.score.toFixed(4)),\n snippet: h.snippet,\n })),\n null,\n 2,\n ),\n );\n return;\n }\n\n info(`${hits.length} result(s) for \"${query}\":`);\n line(\"\");\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i]!;\n const rank = chalk.dim(`${i + 1}.`);\n const score = chalk.cyan(`[${h.score.toFixed(1)}]`);\n const relPath = store.relativePath(h.path);\n line(` ${rank} ${h.title} ${score} ${chalk.dim(relPath)}`);\n if (h.snippet) {\n line(` ${chalk.dim(h.snippet.slice(0, 120))}`);\n }\n }\n\n if (opts.open === true && hits.length > 0) {\n const topHit = hits[0]!;\n const relPath = store.relativePath(topHit.path);\n try {\n const { openInObsidian } = await import(\"../lib/vault-detect.js\");\n const ok = await openInObsidian(wikiRoot);\n if (!ok) {\n fail(`Could not open ${relPath} in Obsidian.`);\n }\n } catch {\n fail(`Could not open ${relPath} in Obsidian.`);\n }\n }\n}\n","/**\n * `wotw stale` — surface wiki pages needing review.\n *\n * Wraps the health scoring system from `src/wiki/health.ts`. A page is\n * considered stale when its staleness factor score falls below a threshold\n * derived from the `--since` duration and the config's staleness table.\n *\n * With `--dashboard`, generates a Dataview-compatible dashboard file inside\n * the wiki root (only if the Dataview plugin is detected in the vault).\n */\nimport type { Command } from \"commander\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { loadAllPages } from \"../../ingestion/wiki-writer.js\";\nimport { ProvenanceChain } from \"../../provenance/chain.js\";\nimport { computeHealthReport, type PageHealthScore } from \"../../wiki/health.js\";\nimport { WikiSearch } from \"../../wiki/search.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { chalk, info, line, success, warn } from \"../output.js\";\n\ninterface StaleOptions {\n since?: string;\n json?: boolean;\n dashboard?: boolean;\n}\n\ninterface StaleEntry {\n title: string;\n path: string;\n staleness_score: number;\n overall_score: number;\n}\n\n/**\n * Attach the `stale` subcommand.\n */\nexport function registerStaleCommand(program: Command): void {\n program\n .command(\"stale\")\n .description(\"List wiki pages needing fresh sources\")\n .option(\"--since <threshold>\", \"Staleness threshold (e.g. 14d, 2w)\", \"30d\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--dashboard\", \"Generate a Dataview dashboard file\")\n .action(async (opts: StaleOptions) => {\n await runStale(opts);\n });\n}\n\n/**\n * Parse a duration string like \"14d\", \"2w\" into days.\n */\nexport function parseDuration(input: string): number {\n const match = /^(\\d+)(d|w)$/i.exec(input.trim());\n if (!match) {\n throw new Error(`Invalid duration \"${input}\". Use Nd for days or Nw for weeks.`);\n }\n const value = Number(match[1]);\n const unit = match[2]!.toLowerCase();\n return unit === \"w\" ? value * 7 : value;\n}\n\n/**\n * Convert a duration in days to the staleness score threshold using the\n * config's staleness table. Pages with a staleness score BELOW the\n * returned value are considered stale for that duration.\n */\nexport function scoreThresholdForDuration(\n days: number,\n thresholds: number[],\n scores: number[],\n): number {\n for (let i = 0; i < thresholds.length; i++) {\n if (days <= thresholds[i]!) {\n return scores[i] ?? 100;\n }\n }\n return scores[scores.length - 1] ?? 0;\n}\n\nexport async function runStale(opts: StaleOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const wikiRoot = config.wiki_root;\n const wikiDir = join(wikiRoot, \"wiki\");\n\n if (!existsSync(wikiDir)) {\n warn(\"No wiki directory found. Run 'wotw init' first.\");\n return;\n }\n\n const store = new WikiStore({ wikiRoot });\n const search = new WikiSearch();\n const allPages = await loadAllPages(store);\n search.rebuild(allPages);\n\n let chain: ProvenanceChain | null = null;\n if (config.provenance.enabled && existsSync(config.provenance.chain_file)) {\n chain = new ProvenanceChain({ path: config.provenance.chain_file });\n await chain.init();\n }\n\n const report = await computeHealthReport(store, chain, search, { config });\n\n const sinceDays = parseDuration(opts.since ?? \"30d\");\n const cutoffScore = scoreThresholdForDuration(\n sinceDays,\n config.health.staleness_thresholds,\n config.health.staleness_scores,\n );\n\n // Build a title map for display.\n const titleByPath = new Map<string, string>();\n for (const p of allPages) {\n titleByPath.set(store.relativePath(p.path), p.frontmatter.title);\n }\n\n const staleEntries: StaleEntry[] = report.scores\n .filter((s: PageHealthScore) => s.factors.staleness < cutoffScore)\n .sort((a: PageHealthScore, b: PageHealthScore) => a.factors.staleness - b.factors.staleness)\n .map((s: PageHealthScore) => ({\n title: titleByPath.get(s.page) ?? s.page,\n path: s.page,\n staleness_score: s.factors.staleness,\n overall_score: s.score,\n }));\n\n if (opts.json === true) {\n line(JSON.stringify(staleEntries, null, 2));\n return;\n }\n\n if (staleEntries.length === 0) {\n success(\"No stale pages found.\");\n return;\n }\n\n info(`${staleEntries.length} stale page(s) (threshold: ${opts.since ?? \"30d\"}):`);\n line(\"\");\n for (const entry of staleEntries) {\n const score = chalk.yellow(`${entry.staleness_score}`);\n line(` ${score} ${entry.title} ${chalk.dim(entry.path)}`);\n }\n\n // Dashboard generation.\n if (opts.dashboard === true) {\n generateDashboard(wikiRoot);\n }\n}\n\n/** Exported for testing. */\nexport function generateDashboard(wikiRoot: string): void {\n const dataviewDir = join(wikiRoot, \".obsidian\", \"plugins\", \"dataview\");\n if (!existsSync(dataviewDir)) {\n info(\"Dataview plugin not detected — skipping dashboard generation.\");\n return;\n }\n\n const dashboardPath = join(wikiRoot, \"wiki\", \"Stale Dashboard.md\");\n if (existsSync(dashboardPath)) {\n info(\"Stale Dashboard.md already exists — skipping.\");\n return;\n }\n\n const content = `---\ntitle: Stale Pages Dashboard\n---\n\n# Stale Pages Dashboard\n\nPages not confirmed by any source in the last 30 days.\n\n\\`\\`\\`dataview\nTABLE last_confirmed, source_count, superseded_by\nFROM \"wiki\"\nWHERE last_confirmed < date(today) - dur(30 days)\nSORT last_confirmed ASC\n\\`\\`\\`\n`;\n\n writeFileSync(dashboardPath, content);\n success(\"Generated Stale Dashboard.md with Dataview query.\");\n}\n","/**\n * `wotw user` — administer multi-user tokens.\n *\n * Subcommands:\n * wotw user add <name> — issue a new token (prints to stdout)\n * wotw user list — list active users\n * wotw user revoke <name> — revoke all tokens for a user\n *\n * This command operates directly on the on-disk token store under the\n * configured `multi_user.workspaces_dir`. It does NOT require the daemon\n * to be running — by design. You'll typically provision a token once,\n * bake it into a client config, and never touch it again.\n */\nimport type { Command } from \"commander\";\nimport { errMsg } from \"../../utils/errors.js\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { TokenStore } from \"../../multi-user/token-store.js\";\nimport { chalk, fail, info, line, success, warn } from \"../output.js\";\n\nexport function registerUserCommand(program: Command): void {\n const user = program.command(\"user\").description(\"Manage multi-user authentication tokens\");\n\n user\n .command(\"add\")\n .description(\"Issue a new token for a user (prints the token)\")\n .argument(\"<name>\", \"User name\")\n .action(async (name: string) => {\n await runUserAdd(name);\n });\n\n user\n .command(\"list\")\n .description(\"List active users and token creation times\")\n .option(\"--json\", \"Emit JSON\")\n .action(async (opts: { json?: boolean }) => {\n await runUserList(opts);\n });\n\n user\n .command(\"revoke\")\n .description(\"Revoke all tokens for a user\")\n .argument(\"<name>\", \"User name\")\n .action(async (name: string) => {\n await runUserRevoke(name);\n });\n}\n\nasync function runUserAdd(name: string): Promise<void> {\n const store = await openStore();\n if (!store) return;\n try {\n const token = store.addUser(name);\n success(`Issued token for ${chalk.bold(name)}.`);\n line(\"\");\n line(chalk.dim(\"Token (save this — it will not be shown again):\"));\n line(` ${chalk.yellow(token)}`);\n line(\"\");\n info(\"Configure clients with `Authorization: Bearer <token>` to authenticate.\");\n } catch (err) {\n fail(`Failed to add user: ${errMsg(err)}`);\n process.exitCode = 1;\n }\n}\n\nasync function runUserList(opts: { json?: boolean }): Promise<void> {\n const store = await openStore();\n if (!store) return;\n const users = store.listUsers();\n if (opts.json) {\n line(JSON.stringify(users, null, 2));\n return;\n }\n if (users.length === 0) {\n info(\"No users configured.\");\n info(\"Add one with `wotw user add <name>`.\");\n return;\n }\n line(\"\");\n line(chalk.bold(`Active users (${users.length}):`));\n line(\"\");\n for (const u of users) {\n line(` ${chalk.green(\"●\")} ${chalk.bold(u.user.padEnd(20))} ${chalk.dim(u.created)}`);\n }\n line(\"\");\n}\n\nasync function runUserRevoke(name: string): Promise<void> {\n const store = await openStore();\n if (!store) return;\n const n = store.revokeUser(name);\n if (n === 0) {\n warn(`No tokens found for ${name}.`);\n return;\n }\n success(`Revoked ${n} token(s) for ${chalk.bold(name)}.`);\n}\n\n/**\n * Load config, check multi_user is enabled, and return a TokenStore.\n * Returns null if multi_user is disabled (with a helpful error).\n */\nasync function openStore(): Promise<TokenStore | null> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n if (!config.multi_user.enabled) {\n fail(\"Multi-user mode is not enabled in config (multi_user.enabled = false).\");\n info(\"Set `multi_user.enabled: true` in your wotw.config.yaml to enable it.\");\n process.exitCode = 1;\n return null;\n }\n const store = new TokenStore({ workspacesDir: config.multi_user.workspaces_dir });\n store.load();\n return store;\n}\n","/**\n * `wotw approve <filename>` — move a candidate page from candidates/ to wiki/.\n * `wotw approve --all` — approve all candidates at once.\n *\n * On approval, the page is parsed, its category is read from frontmatter,\n * and it is placed into the corresponding wiki/<category>/ directory. The\n * search index is rebuilt and a provenance record is appended.\n */\nimport type { Command } from \"commander\";\nimport { existsSync, readFileSync, renameSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { loadAllPages } from \"../../ingestion/wiki-writer.js\";\nimport { ProvenanceChain } from \"../../provenance/chain.js\";\nimport { sha256Hex } from \"../../provenance/hash.js\";\nimport { readTextOrNullAsync } from \"../../utils/fs.js\";\nimport { parsePage } from \"../../wiki/page.js\";\nimport { WikiSearch } from \"../../wiki/search.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { IndexManager } from \"../../wiki/index-manager.js\";\nimport { commitWikiChanges } from \"../../ingestion/git-committer.js\";\nimport { fail, info, line, success } from \"../output.js\";\n\ninterface ApproveOptions {\n all?: boolean;\n}\n\nexport function registerApproveCommand(program: Command): void {\n program\n .command(\"approve [filename]\")\n .description(\"Approve a candidate page and move it to wiki/\")\n .option(\"-a, --all\", \"Approve all pending candidates\")\n .action(async (filename: string | undefined, opts: ApproveOptions) => {\n await runApprove(filename, opts);\n });\n}\n\nexport async function runApprove(\n filename: string | undefined,\n opts: ApproveOptions,\n): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const store = new WikiStore({ wikiRoot: config.wiki_root });\n\n if (!existsSync(store.candidatesDir)) {\n fail(\"No candidates/ directory. Run 'wotw init' first.\");\n process.exitCode = 1;\n return;\n }\n\n const candidates = store.listCandidates();\n\n if (opts.all === true) {\n if (candidates.length === 0) {\n info(\"No pending candidates.\");\n return;\n }\n let approved = 0;\n for (const absPath of candidates) {\n const ok = await approveOne(absPath, store, config);\n if (ok) approved++;\n }\n success(`Approved ${approved} candidate(s).`);\n // Rebuild index + search.\n await rebuildAfterApprove(store, config);\n return;\n }\n\n if (!filename) {\n fail(\"Specify a filename or use --all.\");\n process.exitCode = 1;\n return;\n }\n\n // Accept all of: `sample-1.md`, `sample-1`, `candidates/sample-1.md`,\n // `./candidates/sample-1.md`. The `wotw candidates` listing and the\n // `wotw audit` output both show paths with a `candidates/` prefix, so a\n // user who copy-pastes one would otherwise hit \"Candidate not found\"\n // because we'd join candidatesDir + candidates/... (PASS-023 dogfood\n // finding #24). Strip a leading candidates/ segment before joining.\n const normalized = filename.replace(/^\\.\\//, \"\").replace(/^candidates\\//, \"\");\n const target = join(\n store.candidatesDir,\n normalized.endsWith(\".md\") ? normalized : `${normalized}.md`,\n );\n if (!existsSync(target)) {\n fail(`Candidate not found: ${basename(target)}`);\n line(` Available: ${candidates.map((c) => basename(c)).join(\", \") || \"(none)\"}`);\n process.exitCode = 1;\n return;\n }\n\n const ok = await approveOne(target, store, config);\n if (ok) {\n success(`Approved: ${basename(target)}`);\n await rebuildAfterApprove(store, config);\n }\n}\n\nexport async function approveOne(\n absPath: string,\n store: WikiStore,\n config: ReturnType<typeof resolveConfigPaths>,\n): Promise<boolean> {\n const raw = await readTextOrNullAsync(absPath);\n if (raw === null) return false;\n\n const page = parsePage(absPath, raw);\n const destPath = store.pathFor(page.frontmatter.category, page.frontmatter.title);\n page.path = destPath;\n\n // Check if a newer version of this page already exists in wiki.\n const existing = await store.readPage(destPath);\n if (existing && existing.frontmatter.updated > page.frontmatter.updated) {\n const { getLogger } = await import(\"../../utils/logger.js\");\n getLogger(\"approve\").warn(\n {\n slug: page.frontmatter.title,\n existing: existing.frontmatter.updated,\n candidate: page.frontmatter.updated,\n },\n \"candidate is older than current wiki page — skipping to prevent regression\",\n );\n return false;\n }\n\n await store.writePage(page);\n // Remove from candidates.\n try {\n renameSync(absPath, `${absPath}.approved`);\n // Delete the .approved marker — the page is now in wiki/.\n if (existsSync(`${absPath}.approved`)) {\n const { rmSync } = await import(\"node:fs\");\n rmSync(`${absPath}.approved`, { force: true });\n }\n } catch {\n // Best-effort cleanup.\n }\n\n // Provenance record.\n if (config.provenance.enabled) {\n try {\n // Review item 44: ad-hoc chain construction would race the daemon\n // for seq numbers if both are writing concurrently. The approve\n // CLI is for the daemon-not-running case (interactive review);\n // when the daemon is up, prefer the MCP `approve_candidate` tool.\n // We still construct a chain here for the CLI-only codepath, but\n // (1) thread tenant_id through (item 43) so the record's canonical\n // hash matches what the daemon's verifier expects, (2) compute\n // real hashes for wiki_file_hashes_after, (3) surface errors via\n // a console.warn rather than the pre-fix silent swallow.\n const chain = new ProvenanceChain({\n path: config.provenance.chain_file,\n tenantId:\n config.hosted.enabled && config.hosted.tenant_id ? config.hosted.tenant_id : undefined,\n });\n await chain.init();\n const wikiFileHashesAfter: Record<string, string> = {};\n try {\n const writtenRaw = readFileSync(destPath, \"utf8\");\n wikiFileHashesAfter[store.relativePath(destPath)] = sha256Hex(writtenRaw);\n } catch {\n // File-read failure here is real but non-fatal for the record;\n // the hash will simply be absent for this entry.\n }\n await chain.append({\n type: \"ingest\",\n source_files: page.frontmatter.sources,\n source_hashes: page.frontmatter.sources.map(() => \"approved\"),\n prompt_hash: sha256Hex(\"approve\"),\n model_id: \"user\",\n response_hash: sha256Hex(raw),\n wiki_files_written: [store.relativePath(destPath)],\n wiki_file_hashes_after: wikiFileHashesAfter,\n metadata: { approved_from: \"candidates\" },\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // eslint-disable-next-line no-console\n console.warn(`[approve] provenance append failed: ${msg}`);\n }\n }\n\n return true;\n}\n\nasync function rebuildAfterApprove(\n store: WikiStore,\n config: ReturnType<typeof resolveConfigPaths>,\n): Promise<void> {\n const search = new WikiSearch();\n const allPages = await loadAllPages(store);\n search.rebuild(allPages);\n const indexManager = new IndexManager(store);\n await indexManager.rebuild(allPages);\n\n // Commit.\n try {\n await commitWikiChanges({\n wikiRoot: config.wiki_root,\n paths: allPages.map((p) => p.path),\n operationId: `approve-${Date.now()}`,\n operation: \"ingest\",\n metadata: { action: \"approve\" },\n });\n } catch {\n // Commit failure is non-fatal for CLI.\n }\n}\n","/**\n * `wotw reject <filename>` — reject a candidate page and move it to candidates/rejected/.\n *\n * Optionally accepts a `--reason` flag that is stored in the rejected page's\n * frontmatter so the next compile can incorporate the feedback.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { readTextOrNullAsync } from \"../../utils/fs.js\";\nimport { parsePage, serializePage } from \"../../wiki/page.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { chalk, fail, info, line, success } from \"../output.js\";\n\ninterface RejectOptions {\n reason?: string;\n}\n\nexport function registerRejectCommand(program: Command): void {\n program\n .command(\"reject <filename>\")\n .description(\"Reject a candidate page and move it to candidates/rejected/\")\n .option(\"-r, --reason <text>\", \"Reason for rejection (stored in frontmatter)\")\n .action(async (filename: string, opts: RejectOptions) => {\n await runReject(filename, opts);\n });\n}\n\nexport async function runReject(filename: string, opts: RejectOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const store = new WikiStore({ wikiRoot: config.wiki_root });\n\n if (!existsSync(store.candidatesDir)) {\n fail(\"No candidates/ directory. Run 'wotw init' first.\");\n process.exitCode = 1;\n return;\n }\n\n const candidates = store.listCandidates();\n // Accept a leading candidates/ prefix (matches the listing + audit output),\n // same normalization as `wotw approve` (PASS-023 dogfood finding #24).\n const normalized = filename.replace(/^\\.\\//, \"\").replace(/^candidates\\//, \"\");\n const target = join(\n store.candidatesDir,\n normalized.endsWith(\".md\") ? normalized : `${normalized}.md`,\n );\n\n if (!existsSync(target)) {\n fail(`Candidate not found: ${basename(target)}`);\n line(` Available: ${candidates.map((c) => basename(c)).join(\", \") || \"(none)\"}`);\n process.exitCode = 1;\n return;\n }\n\n const raw = await readTextOrNullAsync(target);\n if (raw === null) {\n fail(`Could not read ${basename(target)}.`);\n process.exitCode = 1;\n return;\n }\n\n // Parse, inject rejection metadata, and write to rejected/.\n const page = parsePage(target, raw);\n page.frontmatter.rejected_at = new Date().toISOString();\n if (opts.reason) {\n page.frontmatter.rejection_note = opts.reason;\n }\n\n const rejectedPath = join(store.rejectedDir, basename(target));\n page.path = rejectedPath;\n\n const { mkdirSync, writeFileSync } = await import(\"node:fs\");\n mkdirSync(store.rejectedDir, { recursive: true });\n writeFileSync(rejectedPath, serializePage(page), \"utf8\");\n\n // Remove the original candidate.\n try {\n const { rmSync } = await import(\"node:fs\");\n rmSync(target, { force: true });\n } catch {\n // Best-effort cleanup.\n }\n\n success(`Rejected: ${basename(target)}`);\n if (opts.reason) {\n line(` Reason: ${chalk.dim(opts.reason)}`);\n }\n info(\"Rejection context will be included in the next compile for this topic.\");\n}\n","/**\n * `wotw candidates` — list all pending and rejected candidate pages.\n *\n * Shows a quick-glance table of every file sitting in candidates/\n * (pending review) and candidates/rejected/ (already rejected with\n * optional notes).\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { readTextOrNullAsync } from \"../../utils/fs.js\";\nimport { parsePage } from \"../../wiki/page.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { chalk, fail, info, line } from \"../output.js\";\n\ninterface CandidatesOptions {\n json?: boolean;\n}\n\nexport function registerCandidatesCommand(program: Command): void {\n program\n .command(\"candidates\")\n .description(\"List pending and rejected candidate pages\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: CandidatesOptions) => {\n await runCandidates(opts);\n });\n}\n\nexport async function runCandidates(opts: CandidatesOptions): Promise<void> {\n const loaded = await loadConfig();\n const config = resolveConfigPaths(loaded.config);\n const store = new WikiStore({ wikiRoot: config.wiki_root });\n\n if (!existsSync(store.candidatesDir)) {\n fail(\"No candidates/ directory. Run 'wotw init' first.\");\n process.exitCode = 1;\n return;\n }\n\n const pending = store.listCandidates();\n const rejected = store.listRejected();\n\n if (pending.length === 0 && rejected.length === 0) {\n info(\"No candidates (pending or rejected).\");\n return;\n }\n\n if (opts.json === true) {\n const entries: Array<{\n file: string;\n status: \"pending\" | \"rejected\";\n title?: string;\n category?: string;\n rejected_at?: string;\n rejection_note?: string;\n }> = [];\n\n for (const absPath of pending) {\n const raw = await readTextOrNullAsync(absPath);\n if (!raw) continue;\n const page = parsePage(absPath, raw);\n entries.push({\n file: basename(absPath),\n status: \"pending\",\n title: page.frontmatter.title,\n category: page.frontmatter.category,\n });\n }\n\n for (const absPath of rejected) {\n const raw = await readTextOrNullAsync(absPath);\n if (!raw) continue;\n const page = parsePage(absPath, raw);\n entries.push({\n file: basename(absPath),\n status: \"rejected\",\n title: page.frontmatter.title,\n category: page.frontmatter.category,\n rejected_at: page.frontmatter.rejected_at,\n rejection_note: page.frontmatter.rejection_note,\n });\n }\n\n line(JSON.stringify(entries, null, 2));\n return;\n }\n\n // Human-readable output.\n if (pending.length > 0) {\n info(`${pending.length} pending candidate(s):`);\n line(\"\");\n for (const absPath of pending) {\n const raw = await readTextOrNullAsync(absPath);\n if (!raw) continue;\n const page = parsePage(absPath, raw);\n const name = basename(absPath);\n const cat = chalk.cyan(`[${page.frontmatter.category}]`);\n line(` ${name} ${page.frontmatter.title} ${cat}`);\n }\n line(\"\");\n }\n\n if (rejected.length > 0) {\n info(`${rejected.length} rejected candidate(s):`);\n line(\"\");\n for (const absPath of rejected) {\n const raw = await readTextOrNullAsync(absPath);\n if (!raw) continue;\n const page = parsePage(absPath, raw);\n const name = basename(absPath);\n const cat = chalk.cyan(`[${page.frontmatter.category}]`);\n const note = page.frontmatter.rejection_note\n ? chalk.dim(` — ${page.frontmatter.rejection_note}`)\n : \"\";\n line(` ${name} ${page.frontmatter.title} ${cat}${note}`);\n }\n line(\"\");\n }\n}\n","/**\n * `wotw facts <subcommand>` — Pass B fact-layer CLI.\n *\n * Today the only subcommand is `reindex`: walk every wiki page, run\n * fact extraction on each, and rebuild the SQLite fact store from\n * scratch. Designed for the \"I just enabled fact_extraction on an\n * existing wiki\" migration path. Prompts for confirmation when running\n * against a metered provider (since extraction can amplify per-tenant\n * cost), or just runs when the runtime is cost-free (Ollama / Claude\n * Code CLI).\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { loadAllPages } from \"../../ingestion/wiki-writer.js\";\nimport { CostTracker } from \"../../ingestion/cost-tracker.js\";\nimport { WikiStore } from \"../../wiki/store.js\";\nimport { resolveExecutionMode } from \"../../ingestion/execution-mode.js\";\nimport { FactStore } from \"../../facts/store.js\";\nimport { FactIndex } from \"../../facts/index-manager.js\";\nimport { extractFactsFromPage, isExtractionActive } from \"../../facts/extractor.js\";\nimport { chalk, fail, info, line, success, warn } from \"../output.js\";\n\ninterface ReindexOptions {\n yes?: boolean;\n json?: boolean;\n}\n\nexport function registerFactsCommand(program: Command): void {\n const factsCmd = program\n .command(\"facts\")\n .description(\"Manage the fact-extraction layer (Pass B)\");\n\n factsCmd\n .command(\"reindex\")\n .description(\"Extract atomic facts + synthetic questions for every wiki page\")\n .option(\"-y, --yes\", \"Skip confirmation prompts (non-interactive mode)\")\n .option(\"--json\", \"Emit a JSON summary on completion (CI-friendly)\")\n .action(async (opts: ReindexOptions) => {\n await runReindex(opts);\n });\n}\n\nasync function runReindex(opts: ReindexOptions): Promise<void> {\n const { config: rawConfig } = await loadConfig();\n const config = resolveConfigPaths(rawConfig, process.cwd());\n if (!existsSync(config.wiki_root)) {\n fail(`wiki_root does not exist: ${config.wiki_root}`);\n process.exit(1);\n }\n\n const resolved = resolveExecutionMode(config);\n const runtimeMode = resolved.mode;\n const active = isExtractionActive(config, runtimeMode);\n if (!active.active) {\n fail(`fact extraction is not active: ${active.reason}`);\n info(\n \"Set fact_extraction.enabled=true or fact_extraction.force_enabled=true in wotw.config.yaml to override.\",\n );\n process.exit(1);\n }\n\n const store = new WikiStore({ wikiRoot: config.wiki_root });\n const pages = await loadAllPages(store);\n if (pages.length === 0) {\n warn(\"no wiki pages to index — nothing to do\");\n return;\n }\n\n // Cost preflight for metered providers (cli + ollama report cost 0).\n if (runtimeMode === \"api\" && config.llm.provider !== \"ollama\" && !opts.yes) {\n info(\n `About to extract facts for ${pages.length} page(s) on metered provider ${config.llm.provider}.`,\n );\n info(\"Re-run with --yes once you have confirmed the cost is acceptable.\");\n process.exit(0);\n }\n\n const factsDbPath = join(config.wiki_root, \".wotw\", \"facts.db\");\n const factStore = new FactStore({ path: factsDbPath });\n const factIndex = new FactIndex();\n const costTracker = new CostTracker({\n trackFile: config.cost.track_file,\n maxDailyUsd: config.cost.max_daily_usd,\n maxPerIngestUsd: config.cost.max_per_ingest_usd,\n maxPerQueryUsd: config.cost.max_per_query_usd,\n });\n\n line();\n info(`reindexing ${pages.length} page(s) (runtime=${runtimeMode}, reason: ${active.reason})`);\n let totalFacts = 0;\n let totalQuestions = 0;\n let totalCost = 0;\n let failures = 0;\n let pageIdx = 0;\n\n for (const page of pages) {\n pageIdx += 1;\n const relPath = relative(config.wiki_root, page.path) || page.path;\n process.stdout.write(`[${pageIdx}/${pages.length}] ${chalk.gray(relPath.slice(0, 80))} ... `);\n let extraction;\n try {\n extraction = await extractFactsFromPage({\n config,\n runtimeMode,\n wikiPageId: relPath,\n pageBody: page.body,\n title: page.frontmatter.title,\n costTracker,\n });\n } catch (err) {\n process.stdout.write(chalk.red(\"err\\n\"));\n warn(` ${err instanceof Error ? err.message.slice(0, 200) : \"unknown error\"} — skipping`);\n failures += 1;\n continue;\n }\n if (!extraction.ran) {\n process.stdout.write(chalk.gray(\"skip\\n\"));\n continue;\n }\n if (extraction.facts.length === 0) {\n process.stdout.write(chalk.gray(\"(empty)\\n\"));\n continue;\n }\n factStore.supersedeByWikiPage(relPath);\n let pageFacts = 0;\n let pageQuestions = 0;\n for (const f of extraction.facts) {\n try {\n const { id } = factStore.insertFact({\n wiki_page_id: relPath,\n entity: f.entity,\n statement: f.statement,\n });\n const questions = factStore.insertQuestions(id, f.questions);\n const fact = factStore.getFact(id);\n if (fact) factIndex.add(fact, questions);\n pageFacts += 1;\n pageQuestions += questions.length;\n } catch {\n // Hash collision (very rare) — skip.\n }\n }\n totalFacts += pageFacts;\n totalQuestions += pageQuestions;\n totalCost += extraction.costUsd;\n process.stdout.write(\n chalk.green(\n `${pageFacts} facts · ${pageQuestions} questions · $${extraction.costUsd.toFixed(4)}\\n`,\n ),\n );\n }\n\n line();\n success(\n `reindex complete — ${totalFacts} facts, ${totalQuestions} questions across ${pages.length - failures} pages (${failures} failed)`,\n );\n success(`total cost: $${totalCost.toFixed(4)}`);\n factStore.close();\n\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n pages: pages.length,\n facts: totalFacts,\n questions: totalQuestions,\n failures,\n cost_usd: Number(totalCost.toFixed(6)),\n },\n null,\n 2,\n )}\\n`,\n );\n }\n}\n","/**\n * `wotw keys <subcommand>` — workspace key management CLI (G5 closure,\n * Pass 018, v0.8.2).\n *\n * Subcommands:\n * - `rotate` — rotate the workspace's active DEK. Atomically: generates\n * a new active DEK, transitions the previous active to 'rotating'.\n * New chain appends sign under the new DEK; verify recognizes records\n * signed by either DEK during the overlap window.\n * - `list` — show all DEKs for the workspace with their states.\n *\n * Requires `WOTW_WORKSPACE_KEK` set in env and hosted mode with a\n * tenant_id. Out of hosted mode there's no workspace concept and the\n * daemon uses the v0.8.1 single-key fallback (no rotation needed).\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { readKekFromEnv } from \"../../keys/envelope.js\";\nimport { KeyStore } from \"../../keys/store.js\";\nimport { chalk, fail, info, line, success } from \"../output.js\";\n\ninterface KeysCommandOptions {\n json?: boolean;\n}\n\nexport function registerKeysCommand(program: Command): void {\n const keysCmd = program\n .command(\"keys\")\n .description(\"Manage workspace HMAC signing keys (G5 attestation)\");\n\n keysCmd\n .command(\"rotate\")\n .description(\"Rotate the workspace's active DEK (overlap window for verify)\")\n .option(\"--json\", \"Emit a JSON summary on completion\")\n .action(async (opts: KeysCommandOptions) => {\n await runRotate(opts);\n });\n\n keysCmd\n .command(\"list\")\n .description(\"List all DEKs for the workspace with their states\")\n .option(\"--json\", \"Emit JSON instead of a table\")\n .action(async (opts: KeysCommandOptions) => {\n await runList(opts);\n });\n}\n\nasync function loadKeyStore(): Promise<{ store: KeyStore; workspaceId: string } | null> {\n const { config: rawConfig } = await loadConfig();\n const config = resolveConfigPaths(rawConfig, process.cwd());\n if (!existsSync(config.wiki_root)) {\n fail(`wiki_root does not exist: ${config.wiki_root}`);\n process.exit(1);\n }\n if (!config.hosted.enabled || !config.hosted.tenant_id) {\n fail(\"workspace keys are only available in hosted mode with a tenant_id\");\n info(\"Set hosted.enabled=true + hosted.tenant_id in wotw.config.yaml.\");\n return null;\n }\n const workspaceId = config.hosted.tenant_id;\n let kek: Buffer;\n try {\n kek = readKekFromEnv();\n } catch (err) {\n fail(`failed to read WOTW_WORKSPACE_KEK: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n const store = new KeyStore({ path: `${config.wiki_root}/.wotw/keys.db`, kek });\n return { store, workspaceId };\n}\n\nasync function runRotate(opts: KeysCommandOptions): Promise<void> {\n const handle = await loadKeyStore();\n if (!handle) process.exit(1);\n const { store, workspaceId } = handle;\n const result = store.rotate(workspaceId);\n store.close();\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n workspace_id: workspaceId,\n previous_key_id: result.previous?.key_id ?? null,\n current_key_id: result.current.key_id,\n })}\\n`,\n );\n return;\n }\n success(`Rotated workspace DEK for ${chalk.bold(workspaceId)}.`);\n if (result.previous) {\n line(` previous: ${result.previous.key_id} → ${chalk.yellow(\"rotating\")} (verify-only)`);\n }\n line(` current: ${result.current.key_id} → ${chalk.green(\"active\")}`);\n info(\"Records appended from now on are signed under the new DEK.\");\n info(\n \"Run `wotw keys list` to inspect, or call `KeyStore.archive(previous_key_id)` after the overlap window.\",\n );\n}\n\nasync function runList(opts: KeysCommandOptions): Promise<void> {\n const handle = await loadKeyStore();\n if (!handle) process.exit(1);\n const { store, workspaceId } = handle;\n const rows = store.listAll(workspaceId);\n const counts = store.countByState(workspaceId);\n store.close();\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({\n workspace_id: workspaceId,\n counts,\n keys: rows.map((r) => ({\n key_id: r.key_id,\n key_state: r.key_state,\n created_at: r.created_at,\n rotated_at: r.rotated_at,\n revoked_at: r.revoked_at,\n })),\n })}\\n`,\n );\n return;\n }\n line(chalk.bold(`Workspace keys for ${workspaceId}:`));\n line(\n ` ${chalk.green(\"active\")}: ${counts.active} ${chalk.yellow(\"rotating\")}: ${counts.rotating} ${chalk.gray(\"archived\")}: ${counts.archived} ${chalk.red(\"revoked\")}: ${counts.revoked}`,\n );\n if (rows.length === 0) {\n line(\" (no keys provisioned yet)\");\n return;\n }\n line(\"\");\n for (const r of rows) {\n const stateColor =\n r.key_state === \"active\"\n ? chalk.green\n : r.key_state === \"rotating\"\n ? chalk.yellow\n : r.key_state === \"revoked\"\n ? chalk.red\n : chalk.gray;\n line(` ${r.key_id} ${stateColor(r.key_state.padEnd(8))} created=${r.created_at}`);\n if (r.rotated_at) line(` rotated_at=${r.rotated_at}`);\n if (r.revoked_at) line(` revoked_at=${r.revoked_at}`);\n }\n}\n","/**\n * `wotw workspace <subcommand>` — workspace-level operations (G5\n * substrate, PASS-019 Parts B+C).\n *\n * Subcommands:\n * - `rotate-kek --confirm` — re-encrypt every non-revoked DEK under\n * a new KEK (PASS-019 Part B). Reads the new KEK from env var\n * `WOTW_WORKSPACE_KEK_NEW` (same encoding as WOTW_WORKSPACE_KEK:\n * base64 or hex of 32 bytes). Refuses without --confirm because\n * KEK rotation is operator-driven and irreversible without a\n * matching rollback step. Runbook: `docs/policies/kek-rotation.md`.\n * - `archive-overlapped [--overlap-hours N]` — manual trigger for the\n * auto-archive sweep (PASS-019 Part C). Useful for operators who\n * want to force-archive rotating DEKs immediately after a\n * successful production rollout without waiting for the hourly\n * cron. Default overlap is `WOTW_DEK_OVERLAP_HOURS` env (default 24).\n *\n * Requires hosted mode + tenant_id (workspace concept). Out of hosted\n * mode the daemon uses the v0.8.1 single-key fallback and these\n * commands have nothing to act on.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { loadConfig, resolveConfigPaths } from \"../../daemon/config.js\";\nimport { parseKek, readKekFromEnv } from \"../../keys/envelope.js\";\nimport { KeyStore } from \"../../keys/store.js\";\nimport { chalk, fail, info, line, success } from \"../output.js\";\n\ninterface RotateKekOptions {\n confirm?: boolean;\n json?: boolean;\n}\n\ninterface ArchiveOverlappedOptions {\n overlapHours?: string;\n json?: boolean;\n}\n\nexport function registerWorkspaceCommand(program: Command): void {\n const wsCmd = program\n .command(\"workspace\")\n .description(\"Workspace-level operations (G5 attestation substrate)\");\n\n wsCmd\n .command(\"rotate-kek\")\n .description(\n \"Re-encrypt every non-revoked DEK under a new KEK (set WOTW_WORKSPACE_KEK_NEW first)\",\n )\n .option(\"--confirm\", \"Required: explicit confirmation, KEK rotation is operator-driven\")\n .option(\"--json\", \"Emit a JSON summary on completion\")\n .action(async (opts: RotateKekOptions) => {\n await runRotateKek(opts);\n });\n\n wsCmd\n .command(\"archive-overlapped\")\n .description(\"Archive every 'rotating' DEK past the overlap window (manual cron trigger)\")\n .option(\n \"--overlap-hours <n>\",\n \"Override the overlap window in hours (default WOTW_DEK_OVERLAP_HOURS or 24)\",\n )\n .option(\"--json\", \"Emit JSON\")\n .action(async (opts: ArchiveOverlappedOptions) => {\n await runArchiveOverlapped(opts);\n });\n}\n\ninterface WorkspaceHandle {\n store: KeyStore;\n workspaceId: string;\n}\n\nasync function loadWorkspace(): Promise<WorkspaceHandle | null> {\n const { config: rawConfig } = await loadConfig();\n const config = resolveConfigPaths(rawConfig, process.cwd());\n if (!existsSync(config.wiki_root)) {\n fail(`wiki_root does not exist: ${config.wiki_root}`);\n process.exit(1);\n }\n if (!config.hosted.enabled || !config.hosted.tenant_id) {\n fail(\"workspace ops require hosted mode with a tenant_id\");\n info(\"Set hosted.enabled=true + hosted.tenant_id in wotw.config.yaml.\");\n return null;\n }\n const workspaceId = config.hosted.tenant_id;\n let kek: Buffer;\n try {\n kek = readKekFromEnv();\n } catch (err) {\n fail(`failed to read WOTW_WORKSPACE_KEK: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n const store = new KeyStore({ path: `${config.wiki_root}/.wotw/keys.db`, kek });\n return { store, workspaceId };\n}\n\nasync function runRotateKek(opts: RotateKekOptions): Promise<void> {\n if (!opts.confirm) {\n fail(\"KEK rotation requires --confirm\");\n info(\"See docs/policies/kek-rotation.md for the operator runbook.\");\n process.exit(1);\n }\n const newKekRaw = process.env.WOTW_WORKSPACE_KEK_NEW;\n if (!newKekRaw) {\n fail(\"WOTW_WORKSPACE_KEK_NEW is not set in environment\");\n info(\n \"Generate a fresh 32-byte KEK out-of-band (e.g. `openssl rand -base64 32`), set it via Fly secrets as WOTW_WORKSPACE_KEK_NEW alongside the existing WOTW_WORKSPACE_KEK, restart the daemon, then re-run this command.\",\n );\n process.exit(1);\n }\n let newKek: Buffer;\n try {\n newKek = parseKek(newKekRaw);\n } catch (err) {\n fail(\n `failed to parse WOTW_WORKSPACE_KEK_NEW: ${err instanceof Error ? err.message : String(err)}`,\n );\n process.exit(1);\n }\n const handle = await loadWorkspace();\n if (!handle) process.exit(1);\n const { store, workspaceId } = handle;\n const result = store.rotateKek(newKek);\n store.close();\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n workspace_id: workspaceId,\n rotated: result.rotated,\n })}\\n`,\n );\n return;\n }\n success(\n `Rotated KEK for ${chalk.bold(workspaceId)} — ${result.rotated} DEK row(s) re-encrypted.`,\n );\n info(\"Next steps (operator):\");\n line(\n \" 1. Verify daemon still serves /healthz and chain verifies (run `wotw status` + the cloud-side verify probe).\",\n );\n line(\n \" 2. Swap the Fly secret: set WOTW_WORKSPACE_KEK to the new KEK, unset WOTW_WORKSPACE_KEK_NEW.\",\n );\n line(\" 3. Restart the daemon — it should re-open keys.db cleanly under the new KEK.\");\n info(\n \"If anything misbehaves, roll back by setting WOTW_WORKSPACE_KEK back to the old value (daemon must still be running under both for that to work — see runbook).\",\n );\n}\n\nasync function runArchiveOverlapped(opts: ArchiveOverlappedOptions): Promise<void> {\n const handle = await loadWorkspace();\n if (!handle) process.exit(1);\n const { store, workspaceId } = handle;\n const overlapHours = resolveOverlapHours(opts.overlapHours);\n const overlapMs = overlapHours * 3600 * 1000;\n const archived = store.archiveOverlapped(workspaceId, overlapMs);\n store.close();\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n workspace_id: workspaceId,\n overlap_hours: overlapHours,\n archived_count: archived.length,\n archived_key_ids: archived,\n })}\\n`,\n );\n return;\n }\n if (archived.length === 0) {\n success(`No DEKs past the ${overlapHours}h overlap window for ${chalk.bold(workspaceId)}.`);\n return;\n }\n success(`Archived ${archived.length} DEK(s) past the ${overlapHours}h overlap window:`);\n for (const k of archived) line(` ${k}`);\n}\n\nfunction resolveOverlapHours(flagValue?: string): number {\n if (flagValue !== undefined) {\n const n = Number(flagValue);\n if (!Number.isFinite(n) || n <= 0) {\n fail(`--overlap-hours must be a positive number (got \"${flagValue}\")`);\n process.exit(1);\n }\n return n;\n }\n const envValue = process.env.WOTW_DEK_OVERLAP_HOURS;\n if (envValue) {\n const n = Number(envValue);\n if (Number.isFinite(n) && n > 0) return n;\n }\n return 24;\n}\n"],"mappings":"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","names":["text","path","require","platform","stdout","path","keyEnv","mkdirSync","dirname","path","path","existsSync","text","existsSync","readFileSync","statSync","homedir","platform","dirname","join","resolve","path","p","existsSync","readFileSync","writeFileSync","dirname","readFileSync","dirname","text","line","readFile","dirname","text","line","path","readdirSync","statSync","basename","join","resolve","query","p","basename","isAbsolute","join","relative","resolve","sep","p","relative","p","existsSync","join","relative","query","p","appendFileSync","existsSync","readFileSync","dirname","query","line","createHash","readFileSync","readFile","path","p","readFile","dirname","line","text","resolve","p","resolve","relative","p","spawn","readdirSync","statSync","join","stdout","stderr","resolve","success","p","path","success","p","PRICING","DEFAULT_PRICING","text","normalizeFinishReason","PRICING","DEFAULT_PRICING","text","normalizeFinishReason","PRICING","DEFAULT_PRICING","text","p","normalizeFinishReason","text","result","isAbsolute","resolve","text","p","relative","resolve","sep","p","reconcileWrittenPages","existsSync","readdirSync","statSync","join","p","existsSync","readFileSync","join","info","join","p","store_exports","createHash","dirname","init_store","MiniSearch","init_index_manager","query","text","randomBytes","store_exports","Database","randomUUID","dirname","SCHEMA_VERSION","SCHEMA_SQL","init_store","row","path","resolve","computeZeroHitRate","MS_PER_HOUR","readFileSync","readdirSync","join","p","text","path","resolve","relative","resolve","mkdir","dirname","outcome","p","path","path","basename","p","readFileSync","readdirSync","join","path","p","p","resolve","text","MAX_PAGE_BODY_BYTES","text","line","Anthropic","GoogleGenerativeAI","text","MAX_PAGE_BODY_BYTES","text","DEFAULT_K","MAX_PAGE_BODY_BYTES","clampPositive","top","truncated","statements","used","isAbsolute","relative","resolve","sep","z","query","VALID_CATEGORIES","p","path","text","computeHealthReport","computeZeroHitRate","join","text","path","timingSafeEqual","randomUUID","mkdir","dirname","join","resolve","sep","timingSafeEqual","p","join","relative","text","p","mkdirSync","writeFileSync","cloudSinkFromEnv","readKekFromEnv","KeyStore","FactStore","FactIndex","isExtractionActive","Sentry","existsSync","mkdirSync","readFileSync","statSync","writeFileSync","basename","isAbsolute","join","resolve","fileURLToPath","existsSync","join","isAbsolute","resolve","basename","custom","statSync","readFileSync","path","writeFileSync","mkdirSync","fileURLToPath","existsSync","resolve","fileURLToPath","fileURLToPath","resolve","existsSync","existsSync","readdirSync","readFileSync","statSync","join","matter","join","WikiStore","WikiSearch","loadAllPages","computeHealthReport","p","computeZeroHitRate","existsSync","readdirSync","statSync","readFileSync","matter","text","text","existsSync","relative","resolve","existsSync","relative","resolve","existsSync","statSync","existsSync","stat","statSync","text","resolve","existsSync","mkdirSync","readFileSync","dirname","join","homedir","platform","fileURLToPath","resolve","platform","existsSync","readFileSync","mkdirSync","dirname","join","homedir","fileURLToPath","resolve","existsSync","readFileSync","homedir","join","join","homedir","existsSync","readFileSync","text","existsSync","join","query","join","existsSync","openInObsidian","existsSync","writeFileSync","join","join","existsSync","p","writeFileSync","existsSync","readFileSync","renameSync","basename","join","existsSync","ok","join","basename","getLogger","renameSync","rmSync","readFileSync","p","existsSync","basename","join","existsSync","join","basename","mkdirSync","writeFileSync","rmSync","existsSync","basename","existsSync","basename","note","init_store","init_index_manager","existsSync","join","relative","existsSync","join","relative","init_store","existsSync","existsSync","init_store","existsSync","existsSync","main"]}
|