@noir-ai/daemon 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context-seam.ts","../src/http.ts","../src/lifecycle.ts","../src/memory-seam.ts","../src/server.ts","../src/status.ts","../src/store-seam.ts","../src/workflow-seam.ts","../src/ensure.ts","../src/stdio.ts"],"sourcesContent":["import { ContextEngine, type EmbedderConfig } from '@noir-ai/context';\nimport type { ProjectId } from '@noir-ai/core';\nimport type { Store } from '@noir-ai/store';\n\n/**\n * Build the daemon's {@link ContextEngine} from its already-open store handle\n * + the project `root` + `projectId` + a resolved {@link EmbedderConfig}.\n *\n * One engine per serve lifecycle — constructed once alongside the store (see\n * {@link openStoreForDaemon}) and the workflow engine (see\n * {@link buildWorkflowEngine}), and reused across every HTTP request, exactly\n * as those handles are. The engine resolves its embedder once (a lazy local\n * model loads at most once per lifecycle) and then owns the indexer (the only\n * context writer) + the retriever (the only context reader) over the SAME\n * injected handle — it never opens a second connection, so the daemon's\n * single-writer discipline is preserved (blueprint D6: in-process, no sidecar,\n * canonical `ProjectId`).\n *\n * Degraded story (mirrors the store + the engine's own contract): pass the\n * store's `storeDegraded` flag so the engine's persistent `degraded` field is\n * honest — `context_status` then reports `degraded:true` for a read-only\n * (daemon-down) handle, and `context_index` short-circuits with a clear error\n * envelope instead of letting the first write throw mid-run. A `kind:'none'`\n * embedder on a WRITABLE store is NOT blocking here: docs still index without\n * vectors (the indexer's tested degraded path), so only the read-only case is\n * fenced off at the tool boundary.\n */\nexport function buildContextEngine(\n store: Store,\n root: string,\n projectId: ProjectId,\n embedderCfg: EmbedderConfig,\n storeDegraded?: boolean,\n): ContextEngine {\n return new ContextEngine({ store, root, projectId, embedderCfg, storeDegraded });\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport {\n localhostHostValidation,\n localhostOriginValidation,\n NodeStreamableHTTPServerTransport,\n} from '@modelcontextprotocol/node';\nimport { createEmbedFn, resolveEmbedderConfig } from '@noir-ai/context';\nimport type { ProjectInfo } from '@noir-ai/core';\nimport { resolveMemoryConfig } from '@noir-ai/memory';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { buildContextEngine } from './context-seam.js';\nimport { clearDaemonRecord, type DaemonRecord, writeDaemonRecord } from './lifecycle.js';\nimport { buildMemoryEngine, resolveConsolidationCapability } from './memory-seam.js';\nimport { createNoirServer } from './server.js';\nimport { openStoreForDaemon } from './store-seam.js';\nimport { buildWorkflowEngine } from './workflow-seam.js';\n\nexport interface StartHttpOptions {\n project: ProjectInfo;\n port?: number;\n idleTimeoutSec: number;\n}\n\nexport interface RunningDaemon {\n port: number;\n pid: number;\n startedAt: number;\n stop: () => Promise<void>;\n}\n\nexport async function startHttpServer(opts: StartHttpOptions): Promise<RunningDaemon> {\n const startedAt = Date.now();\n const pid = process.pid;\n const validateHost = localhostHostValidation();\n const validateOrigin = localhostOriginValidation();\n let lastActivity = Date.now();\n let idleTimer: NodeJS.Timeout | undefined = setInterval(() => {\n if (Date.now() - lastActivity > opts.idleTimeoutSec * 1000) void shutdown();\n }, 10_000);\n\n // The daemon is the single writer: open the store ONCE per serve lifecycle\n // and reuse the same handle across every HTTP request. The stateless\n // Streamable HTTP model builds a fresh McpServer per request, but they all\n // share this one store handle — no per-request re-open, no second writer.\n const daemonStore = await openStoreForDaemon(opts.project.id, opts.project.root).catch(\n () => undefined,\n );\n // One engine per lifecycle, built from the shared store handle — reused\n // across every request, exactly like the store.\n const engine = daemonStore\n ? buildWorkflowEngine(daemonStore.store, opts.project.root, opts.project.id)\n : undefined;\n // One context engine per lifecycle, built from the same shared store handle +\n // the resolved embedder config — reused across every request, exactly like the\n // store + engine. The daemon owns ONE embedder: the config is resolved once\n // (`resolveEmbedderConfig`) and the `EmbedFn` materialized once\n // (`createEmbedFn`); the same `EmbedFn` is handed to the memory engine below.\n // The context engine still takes the `EmbedderConfig` (its own contract) and\n // resolves its embedder internally from the SAME config — for `kind:'local'`\n // the ONNX pipeline is module-cached, so the two resolutions share one loaded\n // model. The store's `degraded` flag threads through so `context_status`/\n // `memory_save` are honest under a read-only handle and writes short-circuit.\n const embedderCfg = resolveEmbedderConfig(opts.project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const context = daemonStore\n ? buildContextEngine(\n daemonStore.store,\n opts.project.root,\n opts.project.id,\n embedderCfg,\n daemonStore.degraded,\n )\n : undefined;\n // One memory engine per lifecycle, built from the same shared store handle +\n // the SAME `EmbedFn` already materialized for S6 (the daemon owns one\n // embedder; memory takes `{store, embed, ...}` — no embedder duplication).\n // Consolidation is OPT-IN + provider-explicit (D5/D6/DS-6 — NEVER a silent\n // paid call, the Agent-Memory anti-pattern §9). The master switch is the\n // user's `memory.consolidation.enabled`; only when it is true does the\n // model-derived provider+model even get considered. `resolveMemoryConfig` is\n // the pure core→memory bridge; resolved once and passed to buildMemoryEngine\n // so the engine's config reflects the user's `memory:` consent exactly. The\n // `memory_consolidate` tool is registered only when the gate resolves.\n const modelCfg = resolveModelConfig(opts.project.config.model);\n const resolvedMemory = resolveMemoryConfig(opts.project.config.memory);\n const memoryConsolidation = resolveConsolidationCapability(resolvedMemory, modelCfg) !== null;\n const memory = daemonStore\n ? buildMemoryEngine(\n daemonStore.store,\n opts.project.root,\n opts.project.id,\n embed,\n modelCfg,\n daemonStore.degraded,\n resolvedMemory,\n )\n : undefined;\n\n const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n lastActivity = Date.now();\n if (req.method === 'GET' && req.url === '/health') {\n res.writeHead(200, { 'content-type': 'application/json' });\n res.end(\n JSON.stringify({ ok: true, pid, uptimeSec: Math.floor((Date.now() - startedAt) / 1000) }),\n );\n return;\n }\n if (!validateHost(req, res) || !validateOrigin(req, res)) return;\n if (req.url === '/mcp') {\n const server = createNoirServer({\n project: opts.project,\n transport: 'streamable-http',\n daemon: true,\n pid,\n startedAt,\n ...(daemonStore\n ? {\n store: daemonStore.store,\n dbPath: daemonStore.dbPath,\n storeDegraded: daemonStore.degraded,\n }\n : {}),\n ...(engine ? { engine } : {}),\n ...(context ? { context } : {}),\n ...(memory ? { memory, memoryConsolidation } : {}),\n });\n const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined });\n await server.connect(transport);\n await transport.handleRequest(req, res);\n return;\n }\n res.writeHead(404).end('not found');\n });\n\n const port: number = await new Promise((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(opts.port ?? 0, '127.0.0.1', () => {\n const addr = httpServer.address();\n httpServer.removeListener('error', reject);\n resolve(typeof addr === 'object' && addr ? addr.port : 0);\n });\n });\n\n const rec: DaemonRecord = { pid, port, startedAt };\n writeDaemonRecord(rec);\n\n async function shutdown(): Promise<void> {\n if (idleTimer) {\n clearInterval(idleTimer);\n idleTimer = undefined;\n }\n await new Promise<void>((r) => httpServer.close(() => r()));\n await daemonStore?.store.close().catch(() => undefined);\n clearDaemonRecord();\n }\n\n for (const sig of ['SIGTERM', 'SIGINT'] as const) {\n process.once(sig, () => void shutdown().then(() => process.exit(0)));\n }\n\n return { port, pid, startedAt, stop: shutdown };\n}\n","import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport interface DaemonRecord {\n pid: number;\n port: number;\n startedAt: number;\n}\n\nexport function noirHome(): string {\n return join(homedir(), '.noir');\n}\n\nexport function daemonJsonPath(): string {\n // NOIR_DAEMON_JSON override lets tests point each file's worker at an\n // isolated temp path (vitest file-parallelism would otherwise race on the\n // single global ~/.noir/daemon.json). Production leaves this unset.\n return process.env.NOIR_DAEMON_JSON ?? join(noirHome(), 'daemon.json');\n}\n\nexport function readDaemonRecord(): DaemonRecord | null {\n try {\n const raw = readFileSync(daemonJsonPath(), 'utf8');\n const rec = JSON.parse(raw) as DaemonRecord;\n if (typeof rec.pid === 'number' && typeof rec.port === 'number') return rec;\n return null;\n } catch {\n return null;\n }\n}\n\nexport function writeDaemonRecord(rec: DaemonRecord): void {\n mkdirSync(noirHome(), { recursive: true });\n writeFileSync(daemonJsonPath(), `${JSON.stringify(rec)}\\n`, 'utf8');\n}\n\nexport function clearDaemonRecord(): void {\n if (existsSync(daemonJsonPath())) rmSync(daemonJsonPath(), { force: true });\n}\n\nexport function pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import type { EmbedFn } from '@noir-ai/context';\nimport type { ProjectId } from '@noir-ai/core';\nimport {\n createMemoryEngine,\n type MemoryConfig,\n type MemoryEngine,\n type MemoryModel,\n} from '@noir-ai/memory';\nimport { complete, type ResolvedModelConfig } from '@noir-ai/model';\nimport type { Store } from '@noir-ai/store';\n\n/**\n * Derive the consolidation provider + model id from a resolved MODEL config —\n * the MODEL-DERIVED FALLBACK used ONLY when the user enabled consolidation\n * under `memory:` but did NOT name a provider there (see\n * {@link resolveConsolidationCapability}). Blueprint D5/D6 — provider-EXPLICIT,\n * never env-inferred.\n *\n * The provider is resolved ONLY from an explicit opt-in: the `consolidate` tier\n * key, falling back to `defaultProvider`. Env-var presence is NEVER consulted\n * (DS-6) — `ANTHROPIC_API_KEY` being set for another tool does NOT activate\n * consolidation here. A provider is \"consolidation-capable\" only when that key\n * resolves to a configured provider block that itself declares a `model` id;\n * otherwise this returns `null`.\n *\n * NOTE: this function ALONE is NOT the consolidation gate — it ignores the\n * user's `memory.consolidation.enabled` master switch. The AND of that switch\n * with this derivation (and with the memory-block provider) lives in\n * {@link resolveConsolidationCapability}, which is what the daemon wiring +\n * {@link buildMemoryEngine} actually consult to decide tool registration.\n */\nexport function resolveMemoryConsolidation(\n modelCfg: ResolvedModelConfig | undefined,\n): { provider: string; model: string } | null {\n if (!modelCfg) return null;\n // Explicit opt-in only: tier override first, then the configured fallback.\n const providerKey = modelCfg.tiers.consolidate ?? modelCfg.defaultProvider;\n if (!providerKey) return null;\n const block = modelCfg.providers[providerKey];\n if (!block || !block.model) return null;\n return { provider: providerKey, model: block.model };\n}\n\n/**\n * The consolidation capability gate — the SINGLE source of truth both\n * {@link buildMemoryEngine} and the daemon wiring (stdio/http) consult to decide\n * (a) whether the `memory_consolidate` MCP tool registers and (b) whether the\n * engine's `consolidate` can run. Returns the usable `{provider, model}` when\n * consolidation is ON, or `null` when it must be fully off.\n *\n * The gate is the AND of ALL three (blueprint D6 / §9 — NEVER a silent paid\n * call, the Agent-Memory anti-pattern):\n * (a) `resolvedMemory.consolidation.enabled === true` — the user's EXPLICIT\n * master switch under `memory:`. This is the LOAD-BEARING gate: a config\n * with no `memory:` block (or `enabled:false`) resolves to `null`\n * REGARDLESS of `model.defaultProvider`, so a `model:` block set for\n * summarize/title/draft does NOT silently enable a paid consolidation call.\n * (b) a usable provider+model — prefer the one named under `memory:` (with its\n * own `model` id); fall back to the MODEL-derived derivation\n * ({@link resolveMemoryConsolidation}) ONLY when the user enabled\n * consolidation but did NOT name a provider under `memory:`.\n * (c) the resolved pair has both a `provider` and a `model` id.\n *\n * `resolvedMemory` is the output of `resolveMemoryConfig(config.memory)` — the\n * pure core→memory bridge (no env inference, no cycle). Pure projection — reads\n * NO env, holds NO secrets, never throws. Whether the provider is actually\n * CALLABLE at runtime (key present, network up) is decided inside `complete()`\n * (S8) — here we only decide config-time capability.\n */\nexport function resolveConsolidationCapability(\n resolvedMemory: MemoryConfig | undefined,\n modelCfg: ResolvedModelConfig | undefined,\n): { provider: string; model: string } | null {\n const cons = resolvedMemory?.consolidation;\n // (a) The master switch — load-bearing gate. Must be explicitly `true`; an\n // absent OR `false` block disables consolidation regardless of `model:`.\n // This is the line against the §9 \"silent paid consolidation\" leak: a\n // `model.defaultProvider:'anthropic'` set for summarize/title/draft must NOT\n // activate a paid memory consolidation call the user opted out of.\n if (cons?.enabled !== true) return null;\n // (b)+(c) Usable {provider, model}. Prefer the `memory:` block; fall back to\n // the model-derived derivation ONLY when no provider was named under `memory:`.\n if (cons.provider) {\n return cons.model ? { provider: cons.provider, model: cons.model } : null;\n }\n return resolveMemoryConsolidation(modelCfg);\n}\n\n/**\n * Adapt S8's {@link complete} (single-shot, provider-explicit) into the\n * {@link MemoryModel} shape the memory engine consumes. This is the ONLY LLM\n * entry point in the memory layer, and it is reached ONLY after the engine's\n * provider gate passes (DS-6: never a silent paid call). `null` degrades to the\n * engine's `model-unavailable` refusal; `{ok:false}` wraps as the same — both\n * are logged, neither crashes, neither is a surprise bill.\n *\n * Consolidation is free-text PROSE (not structured), so the `value`/`usage`\n * branches of {@link CompleteResult} are intentionally dropped here.\n */\nfunction bindMemoryModel(modelCfg: ResolvedModelConfig): MemoryModel {\n return {\n async complete(req) {\n const result = await complete(\n {\n system: req.system,\n prompt: req.prompt,\n provider: req.provider,\n model: req.model,\n ...(req.maxTokens !== undefined ? { maxTokens: req.maxTokens } : {}),\n ...(req.tier !== undefined ? { tier: req.tier } : {}),\n },\n modelCfg,\n );\n if (result === null) return null;\n if (!result.ok) return { ok: false, reason: result.reason };\n return { ok: true, text: result.text };\n },\n };\n}\n\n/**\n * Build the daemon's {@link MemoryEngine} from its already-open store handle +\n * the project `root` + `projectId` + the SAME `EmbedFn` the daemon resolved once\n * for S6 (the daemon owns one embedder; memory takes `{store, embed, ...}`, no\n * embedder duplication — plan §Architecture).\n *\n * One engine per serve lifecycle — constructed once alongside the store (see\n * {@link openStoreForDaemon}), the workflow engine (see\n * {@link buildWorkflowEngine}), and the context engine (see\n * {@link buildContextEngine}), and reused across every HTTP request, exactly as\n * those handles are. The engine — like the context indexer — is the ONLY thing\n * that writes `source:'memory'` rows through the injected handle; it never opens\n * a second connection, so the daemon's single-writer discipline is preserved\n * (blueprint D6: in-process, no sidecar, canonical `ProjectId`).\n *\n * Consolidation is OPT-IN + provider-explicit (DS-6 / §9 — NEVER a silent paid\n * call). The capability gate is {@link resolveConsolidationCapability} — the AND\n * of the user's `memory.consolidation.enabled` master switch (the load-bearing\n * gate), a usable provider+model (preferring the `memory:` block, falling back\n * to the model-derived derivation when enabled but no provider named under\n * `memory:`), and `modelCfg` being present to bind S8's `complete`. When the\n * gate resolves a `{provider, model}`:\n * - S8's {@link complete} is bound as the engine's model injection, AND\n * - the runtime gate `config.consolidation` is set so `consolidate` can run.\n * When the gate is `null` — most importantly when `enabled === false`, regardless\n * of `model.defaultProvider` — NO model is wired and `engine.consolidate`\n * self-refuses (`'no-provider'`) WITHOUT calling the model. The daemon registers\n * the `memory_consolidate` MCP tool only in the capable case.\n *\n * `resolvedMemory` is the output of `resolveMemoryConfig(config.memory)` — the\n * pure core→memory bridge. Passing it here means the engine's `config` reflects\n * the user's `memory:` consent exactly (never a hardcoded `enabled:true`).\n *\n * Degraded story (mirrors the store + the context engine): pass the store's\n * `storeDegraded` flag so the engine's persistent `degraded` field is honest —\n * `memory_save` / `memory_forget` then refuse with a clear envelope (the engine\n * throws upfront on a read-only handle) while reads (`memory_recall` /\n * `memory_search` / `memory_sessions`) keep working off the same handle.\n */\nexport function buildMemoryEngine(\n store: Store,\n root: string,\n projectId: ProjectId,\n embed: EmbedFn,\n modelCfg?: ResolvedModelConfig,\n storeDegraded?: boolean,\n resolvedMemory?: MemoryConfig,\n): MemoryEngine {\n // Consolidation capability gate (DS-6 / §9). The AND of the user's master\n // switch + a usable provider+model. `null` ⇒ consolidation fully OFF: no model\n // wired, `memory_consolidate` not registered, and `engine.consolidate` refuses\n // `'no-provider'` WITHOUT a model call — regardless of `model.defaultProvider`.\n const cons = resolveConsolidationCapability(resolvedMemory, modelCfg);\n if (!cons) {\n // Pass resolvedMemory through so the engine's config reflects the user's\n // `memory:` block (enabled:false ⇒ runConsolidation refuses 'no-provider').\n return createMemoryEngine({\n store,\n root,\n projectId,\n embed,\n storeDegraded,\n ...(resolvedMemory ? { config: resolvedMemory } : {}),\n });\n }\n // Consolidation-capable. Bind S8 `complete` as the sole LLM entry point when\n // modelCfg is available; open the runtime gate so `consolidate` can run. If\n // modelCfg is absent (provider named under `memory:` but no `model:` block to\n // bind S8), the model stays unset and `consolidate` refuses `model-unavailable`.\n const model = modelCfg ? bindMemoryModel(modelCfg) : undefined;\n const config: MemoryConfig = {\n consolidation: { enabled: true, provider: cons.provider, model: cons.model },\n };\n return createMemoryEngine({\n store,\n root,\n projectId,\n embed,\n ...(model ? { model } : {}),\n config,\n storeDegraded,\n });\n}\n","import { McpServer } from '@modelcontextprotocol/server';\nimport type { ContextEngine } from '@noir-ai/context';\nimport type { ProjectInfo } from '@noir-ai/core';\nimport { NOIR_VERSION } from '@noir-ai/core';\nimport type { MemoryEngine } from '@noir-ai/memory';\nimport type { Store } from '@noir-ai/store';\nimport type {\n AdvanceOpts,\n GateResult,\n Mode,\n Phase,\n WorkflowEngine,\n WorkflowState,\n} from '@noir-ai/workflow';\nimport { PHASES } from '@noir-ai/workflow';\nimport { z } from 'zod';\nimport { buildStatus, type Transport } from './status.js';\n\n/** Gate phases in lifecycle order (spec → plan → verify), used by {@link nextGateAfter}. */\nconst GATE_PHASES: readonly Phase[] = ['spec', 'plan', 'verify'] as const;\n\nexport interface ServerContext {\n project: ProjectInfo;\n transport: Transport;\n daemon: boolean;\n pid?: number;\n startedAt?: number;\n /**\n * Optional store handle. When present, the `store_status` tool is\n * registered. The daemon is the single writer; stdio/HTTP serve paths open\n * the store once per serve lifecycle and reuse the same handle.\n */\n store?: Store;\n /** Filesystem path to the store DB (reported by `store_status`). */\n dbPath?: string;\n /** True when the store was opened read-only (writable open failed). */\n storeDegraded?: boolean;\n /**\n * Optional workflow engine. When present, the `workflow_status` and\n * `checkpoint` tools are registered. Built once per serve lifecycle from the\n * same store handle (see {@link buildWorkflowEngine}) and reused across HTTP\n * requests, mirroring the store.\n */\n engine?: WorkflowEngine;\n /**\n * Optional context engine. When present, the `context_search`,\n * `context_index`, and `context_status` tools are registered. Built once per\n * serve lifecycle from the same store handle (see `buildContextEngine` in\n * `./context-seam.js`) and reused across HTTP requests, mirroring the store\n * + engine. The engine — through its indexer — is the only thing that writes\n * context rows, so the daemon's single-writer discipline is preserved.\n */\n context?: ContextEngine;\n /**\n * Optional memory engine. When present, the `memory_save`, `memory_recall`,\n * `memory_search`, `memory_sessions`, and `memory_forget` tools are\n * registered. Built once per serve lifecycle from the same store handle + the\n * SAME `EmbedFn` already resolved for S6 (see `buildMemoryEngine` in\n * `./memory-seam.js`) and reused across HTTP requests, mirroring the store +\n * engine + context. The engine is the only thing that writes `source:memory`\n * rows through the injected handle, so the daemon's single-writer discipline\n * is preserved (blueprint D6: in-process, no sidecar, canonical `ProjectId`).\n */\n memory?: MemoryEngine;\n /**\n * Whether the {@link memory} engine is consolidation-capable — i.e. the user\n * opted in (`memory.consolidation.enabled === true`) AND a usable provider+model\n * resolved (see `resolveConsolidationCapability` in `./memory-seam.js`, the AND\n * of the master switch + the provider derivation). Only when this is true is the\n * `memory_consolidate` tool registered: consolidation is OPT-IN +\n * provider-explicit (blueprint D5/D6/DS-6 / §9), NEVER a silent paid call — a\n * `model:` block set for summarize/title/draft does NOT flip this when the user\n * left `memory.consolidation.enabled` false. The engine's `consolidate`\n * self-refuses (`no-provider`/`model-unavailable`) when this flag is false, so\n * the flag + the engine agree by construction.\n */\n memoryConsolidation?: boolean;\n}\n\n/** JSON returned by the `store_status` tool. */\nexport interface StoreStatus {\n ok: boolean;\n projectId: string;\n docCount: number;\n vecCount: number;\n dbPath: string | null;\n degraded: boolean;\n}\n\n/**\n * Build the `store_status` payload from a store handle.\n *\n * `docCount`/`vecCount` come straight from the live SQLite connection (the\n * daemon's single writer handle) via the Store's own `countDocs`/`countVecs`\n * methods, so they reflect indexed data immediately after `indexDoc`/\n * `upsertVec` — no caching, no stale reads, no `__db` coupling.\n */\nexport function buildStoreStatus(store: Store, dbPath?: string, degraded = false): StoreStatus {\n return {\n ok: true,\n projectId: store.projectId,\n docCount: store.countDocs(),\n vecCount: store.countVecs(),\n dbPath: dbPath ?? null,\n degraded,\n };\n}\n\n/** JSON returned by the `workflow_status` / `checkpoint` tools. */\nexport interface WorkflowStatus {\n ok: boolean;\n taskId: string;\n phase: Phase;\n state: WorkflowState;\n /** Next gate-phase ahead of the current phase (null past verify, or blocked). */\n nextGate: Phase | null;\n mode: Mode;\n /** In-process view of the observable gate audit (Noir §9.1). */\n history: GateResult[];\n updatedAt: number;\n /** Mirrors `store_status`: true when the store is a read-only fallback. */\n degraded: boolean;\n}\n\n/**\n * The next gate-phase (spec / plan / verify) strictly ahead of `phase` in the\n * lifecycle. Once a gate has fired it lives in `history`; this points at the one\n * still to come. Returns `null` past `verify` (nothing left to gate).\n */\nexport function nextGateAfter(phase: Phase): Phase | null {\n const cur = PHASES.indexOf(phase);\n for (const p of GATE_PHASES) {\n if (PHASES.indexOf(p) > cur) return p;\n }\n return null;\n}\n\n/**\n * Build the `workflow_status` payload from an engine + taskId.\n *\n * Reads the persisted `TaskState` straight from the store KV (a live read off\n * the daemon's single handle — no cache). `nextGate` is `null` for `blocked` /\n * `abandoned` tasks (no forward gate applies). Returns `null` for an unknown\n * task so the tool handler can emit a clear not-found envelope.\n */\nexport function buildWorkflowStatus(\n engine: WorkflowEngine,\n taskId: string,\n degraded = false,\n): WorkflowStatus | null {\n const task = engine.status(taskId);\n if (!task) return null;\n const stopped = task.state === 'blocked' || task.state === 'abandoned';\n return {\n ok: true,\n taskId: task.taskId,\n phase: task.phase,\n state: task.state,\n nextGate: stopped ? null : nextGateAfter(task.phase),\n mode: task.mode,\n history: task.history,\n updatedAt: task.updatedAt,\n degraded,\n };\n}\n\n/** Wrap a JSON-serializable value as a single-text-block MCP tool result. */\nfunction textResult(obj: unknown): { content: [{ type: 'text'; text: string }] } {\n return { content: [{ type: 'text', text: JSON.stringify(obj, null, 2) }] };\n}\n\n/** Coerce a thrown value into a readable message for error envelopes. */\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport function createNoirServer(ctx: ServerContext): McpServer {\n const server = new McpServer({ name: 'noir', version: NOIR_VERSION });\n\n server.registerTool(\n 'host_status',\n {\n description:\n \"Report Noir's runtime status: project id/name, host CLI, transport, and daemon state.\",\n // Empty ZodRawShape => no input parameters (MCP SDK v2 registerTool overload 2).\n inputSchema: {},\n },\n async () => {\n const status = buildStatus(ctx.project, ctx);\n return { content: [{ type: 'text' as const, text: JSON.stringify(status, null, 2) }] };\n },\n );\n\n // The store is optional: stdio/HTTP only inject it when openStoreForDaemon\n // succeeded. When present, surface counts/health via `store_status`.\n if (ctx.store) {\n const store = ctx.store;\n const dbPath = ctx.dbPath;\n const degraded = ctx.storeDegraded === true;\n server.registerTool(\n 'store_status',\n {\n description:\n \"Report the Noir embedded store's health: project id, document and vector counts, DB path, and degraded state.\",\n inputSchema: {},\n },\n async () => {\n const payload = buildStoreStatus(store, dbPath, degraded);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],\n };\n },\n );\n }\n\n // The workflow engine is optional: stdio/HTTP inject it alongside the store\n // (same lifecycle). When present, surface the SDD task state via\n // `workflow_status` and flush / read checkpoints via `checkpoint`.\n // `host_status` / `store_status` above are unchanged.\n if (ctx.engine) {\n const engine = ctx.engine;\n const degraded = ctx.storeDegraded === true;\n\n server.registerTool(\n 'workflow_status',\n {\n description:\n \"Report the active Noir SDD task's phase, state, the next gate ahead, mode, and observable gate history. Omit taskId to read the active task.\",\n inputSchema: {\n taskId: z\n .string()\n .optional()\n .describe('Task id; defaults to the active task (workflow:active).'),\n },\n },\n async ({ taskId }) => {\n const id = taskId ?? engine.activeTaskId();\n if (!id) return textResult({ ok: false, error: 'no active task' });\n const payload = buildWorkflowStatus(engine, id, degraded);\n if (!payload) return textResult({ ok: false, taskId: id, error: 'unknown task' });\n return textResult(payload);\n },\n );\n\n server.registerTool(\n 'checkpoint',\n {\n description:\n 'Checkpoint a Noir SDD task: `save` flushes the in-flight state to the store KV; `restore` reads it back. Omit taskId to target the active task.',\n inputSchema: {\n action: z\n .enum(['save', 'restore'])\n .describe(\"'save' flushes state; 'restore' returns the in-flight task state.\"),\n taskId: z\n .string()\n .optional()\n .describe('Task id; defaults to the active task (workflow:active).'),\n },\n },\n async ({ action, taskId }) => {\n const id = taskId ?? engine.activeTaskId();\n if (!id) return textResult({ ok: false, error: 'no active task' });\n if (action === 'save') {\n try {\n await engine.checkpoint(id);\n } catch (err) {\n // Degraded (read-only store): surface clearly, never crash.\n return textResult({\n ok: false,\n action: 'save',\n taskId: id,\n degraded: true,\n error: errorMessage(err),\n });\n }\n }\n const payload = buildWorkflowStatus(engine, id, degraded);\n if (!payload) return textResult({ ok: false, taskId: id, error: 'unknown task' });\n return textResult({ action, ...payload });\n },\n );\n\n // `workflow_start` / `workflow_advance` expose the engine's writes that the\n // S9 `task new` / `task advance` CLI commands drive (previously those were\n // honest \"not exposed\" stubs). Both are writes, so a read-only (daemon-down)\n // store fences them off up front with a clear envelope (mirrors\n // `context_index` / `memory_save`). The result reuses buildWorkflowStatus so\n // the wire shape matches `workflow_status` (taskId/phase/state/nextGate/...).\n server.registerTool(\n 'workflow_start',\n {\n description:\n 'Start a Noir SDD task at draft/intake and make it the active task (workflow:active). Re-starting an existing taskId overwrites it (the KV is the source of truth, not a journal). Defaults to full mode.',\n inputSchema: {\n taskId: z.string().min(1).describe('Stable task handle (re-starting overwrites).'),\n slug: z.string().min(1).describe('Human-readable slug, e.g. \"add-login\".'),\n mode: z.enum(['full', 'quick']).optional().describe(\"Mode: 'full' (default) or 'quick'.\"),\n },\n },\n async ({ taskId, slug, mode }) => {\n if (degraded) {\n return textResult({\n ok: false,\n degraded: true,\n error: 'store is read-only (daemon down) — workflow_start is unavailable',\n });\n }\n try {\n const resolvedMode: Mode = mode ?? 'full';\n await engine.startTask(taskId, slug, resolvedMode);\n const payload = buildWorkflowStatus(engine, taskId, degraded);\n if (!payload) return textResult({ ok: false, taskId, error: 'unknown task' });\n return textResult(payload);\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'workflow_advance',\n {\n description:\n 'Advance a Noir SDD task to its next phase, or jump with `to`. At a gate-landing state (entering specified/planned/done) a gate is recorded — approved by default, forced (with reason) via `force`, or skipped via `skip`. Omit taskId to target the active task. `force` and `skip` are mutually exclusive.',\n inputSchema: {\n taskId: z\n .string()\n .optional()\n .describe('Task id; defaults to the active task (workflow:active).'),\n force: z\n .object({ reason: z.string().min(1) })\n .optional()\n .describe(\n 'Pass the next gate without satisfying its criteria; requires a non-empty reason. Mutually exclusive with skip.',\n ),\n to: z\n .enum(['intake', 'clarify', 'spec', 'plan', 'execute', 'verify', 'document'])\n .optional()\n .describe('Jump directly to a phase, bypassing the FSM.'),\n skip: z\n .boolean()\n .optional()\n .describe(\n \"Quick-mode: record the landing gate as 'skipped' instead of 'approved'. Mutually exclusive with force.\",\n ),\n },\n },\n async ({ taskId, force, to, skip }) => {\n if (degraded) {\n return textResult({\n ok: false,\n degraded: true,\n error: 'store is read-only (daemon down) — workflow_advance is unavailable',\n });\n }\n try {\n const id = taskId ?? engine.activeTaskId();\n if (!id) return textResult({ ok: false, error: 'no active task' });\n const opts: AdvanceOpts = {};\n if (force) opts.force = { reason: force.reason };\n if (to) opts.to = to;\n if (skip) opts.skip = true;\n await engine.advance(id, opts);\n const payload = buildWorkflowStatus(engine, id, degraded);\n if (!payload) return textResult({ ok: false, taskId: id, error: 'unknown task' });\n return textResult(payload);\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n }\n\n // The context engine is optional: stdio/HTTP inject it alongside the store +\n // workflow engine (same lifecycle, same single store handle). When present,\n // expose Noir's hybrid retrieval (BM25 ∪ cosine-kNN fused by RRF) via three\n // tools — `context_search`, `context_index`, `context_status` (spec F9/F10/F11).\n // `host_status` / `store_status` / `workflow_status` above are unchanged.\n if (ctx.context) {\n const context = ctx.context;\n const storeDegraded = ctx.storeDegraded === true;\n\n server.registerTool(\n 'context_search',\n {\n description:\n 'Hybrid search over the Noir context index: BM25 ∪ cosine-kNN fused by Reciprocal Rank Fusion (k=60), packed into a token budget with window-extracted snippets (never truncated). Returns ranked hits with path, snippet, and score.',\n inputSchema: {\n query: z\n .string()\n .describe('Natural-language or identifier query (e.g. \"ContextEngine\").'),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Max hits requested from each leg before fusion (default 10).'),\n budgetTokens: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Token budget for the packed result set (default 4096).'),\n // Singular `source` (not the spec's plural `sources`): both store\n // primitives (SearchFtOpts.source / VecOpts.source) and the engine's\n // SearchOptions.source take a single string, so a plural array is not\n // honor-able here. Spec F9 source-filtering surfaces as one bucket.\n source: z\n .string()\n .optional()\n .describe('Restrict both legs to a single source bucket (e.g. \"docs\", \"codebase\").'),\n },\n },\n async ({ query, limit, budgetTokens, source }) => {\n try {\n const result = await context.search(query, { limit, budgetTokens, source });\n return textResult({ ok: true, ...result });\n } catch (err) {\n // Never crash the daemon: surface a degraded envelope (spec F12).\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n // No `watch` param is exposed here on purpose: watch mode (spec F5, daemon\n // --watch via chokidar) is deferred and the engine's IndexPathOptions has\n // no watch field. Accepting it now would silently no-op; if exposed later,\n // return an explicit `ok:false, error:'watch not implemented (F5)'` rather\n // than ignoring the flag. (spec F10 lists watch, but it is not wired yet.)\n server.registerTool(\n 'context_index',\n {\n description:\n 'Incrementally index files/directories into the Noir context store (SHA-256 content-hash; unchanged files are skipped). Indexes docs + 384-dim vectors into the existing tables (no schema migration). Omit paths to index the project root.',\n inputSchema: {\n paths: z\n .array(z.string())\n .optional()\n .describe('Files/directories to index (repo-relative or absolute); defaults to [\".\"].'),\n },\n },\n async ({ paths }) => {\n // Read-only (daemon-down) store: indexing is a write, so fence it off\n // up front with a clear envelope rather than letting the first write\n // throw partway through (spec F12 / AC-5).\n if (storeDegraded) {\n return textResult({\n ok: false,\n degraded: true,\n error: 'store is read-only (daemon down) — context_index is unavailable',\n });\n }\n try {\n const result = await context.indexPaths(paths && paths.length > 0 ? paths : ['.']);\n return textResult({ ok: true, ...result });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'context_status',\n {\n description:\n \"Report the Noir context index's health: project id, document + vector counts, indexed file count, the active embedder (kind/model/dim), and degraded state.\",\n inputSchema: {},\n },\n async () => {\n try {\n // Live read off the single writer handle — no cache (mirrors\n // buildStoreStatus). docCount/vecCount/indexedFiles reflect indexed\n // data immediately after context_index.\n return textResult(context.status());\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n }\n\n // The memory engine is optional: stdio/HTTP inject it alongside the store +\n // workflow + context engines (same lifecycle, same single store handle, the\n // SAME EmbedFn already resolved for S6). When present, expose Noir's\n // cross-session memory — append-only observations stored on top of the store\n // (FTS5 + vec0 + KV) — via five tools (spec §8): `memory_save`,\n // `memory_recall` (hybrid BM25 ∪ kNN + RRF, hydrated to FULL content),\n // `memory_search` (BM25-only instant), `memory_sessions`, `memory_forget`.\n // `host_status` / `store_status` / `workflow_status` / `context_*` above are\n // unchanged. `memory_save` / `memory_forget` are writes, so a read-only\n // (daemon-down) handle fences them off up front (mirrors `context_index`).\n if (ctx.memory) {\n const memory = ctx.memory;\n const storeDegraded = ctx.storeDegraded === true;\n\n server.registerTool(\n 'memory_save',\n {\n description:\n 'Persist a cross-session memory observation (pattern / preference / architecture / bug / workflow / fact / decision). Stored locally on top of the Noir store (FTS5 + vectors + KV) — never truncated, never sent to an LLM. Returns the full saved observation.',\n inputSchema: {\n content: z\n .string()\n .min(1)\n .describe('The insight to remember (full text; never truncated).'),\n // Open enum (DS-3): unknown types are accepted + stored, so this is a\n // free-form string (with the known values described) rather than a\n // closed zod enum that would reject forward-compatible types.\n type: z\n .string()\n .optional()\n .describe(\n 'Observation type — pattern | preference | architecture | bug | workflow | fact | decision. Unknown values are accepted.',\n ),\n concepts: z\n .array(z.string())\n .optional()\n .describe('User tags (no auto-LLM tagging in v1 — explicit only).'),\n files: z.array(z.string()).optional().describe('Repo-relative paths mentioned.'),\n importance: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe('Salience 0..1 (defaults to 0.5).'),\n sessionId: z.string().optional().describe('Host session id (recorded when known).'),\n },\n },\n async (input) => {\n // Read-only (daemon-down) store: a save is a write — fence it off up\n // front with a clear envelope rather than letting the engine throw\n // mid-run (mirrors `context_index`).\n if (storeDegraded) {\n return textResult({\n ok: false,\n degraded: true,\n error: 'store is read-only (daemon down) — memory_save is unavailable',\n });\n }\n try {\n const observation = await memory.save(input);\n return textResult({ ok: true, id: observation.id, observation });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'memory_recall',\n {\n description:\n 'Hybrid recall over cross-session memory: BM25 ∪ cosine-kNN fused by Reciprocal Rank Fusion (k=60) scoped to source:\"memory\", plus a cheap entity-boost. Returns ranked observations with FULL content (hydrated from the authoritative KV row — never the truncated FTS snippet). Degrades to BM25-only when the embedder is unavailable.',\n inputSchema: {\n query: z.string().describe('Natural-language or identifier query.'),\n limit: z.number().int().positive().optional().describe('Max results (default 10).'),\n type: z.string().optional().describe('Filter to a single observation type.'),\n sessionId: z.string().optional().describe('Filter to a single host session.'),\n },\n },\n async ({ query, limit, type, sessionId }) => {\n try {\n const results = await memory.recall(query, { limit, type, sessionId });\n return textResult({ ok: true, results });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'memory_search',\n {\n description:\n 'Instant BM25-only lookup over cross-session memory (no embedding cost). Returns ranked observations with FULL content, scoped to source:\"memory\". Use memory_recall for the hybrid (vector + BM25) path.',\n inputSchema: {\n query: z.string().describe('Natural-language or identifier query.'),\n limit: z.number().int().positive().optional().describe('Max results (default 10).'),\n },\n },\n async ({ query, limit }) => {\n try {\n const hits = await memory.search(query, { limit });\n return textResult({ ok: true, hits });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'memory_sessions',\n {\n description:\n \"List per-session memory rollups (session id, observation count, most-recent timestamp) for this project's cross-session memory.\",\n inputSchema: {},\n },\n async () => {\n try {\n return textResult({ ok: true, sessions: memory.sessions() });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n server.registerTool(\n 'memory_forget',\n {\n description:\n 'Remove observations from cross-session memory: deletes the authoritative KV row + best-effort FTS/vector purge. Returns the count actually removed.',\n inputSchema: {\n ids: z.array(z.string()).min(1).describe('Observation ids to remove.'),\n },\n },\n async ({ ids }) => {\n if (storeDegraded) {\n return textResult({\n ok: false,\n degraded: true,\n error: 'store is read-only (daemon down) — memory_forget is unavailable',\n });\n }\n try {\n const result = memory.forget(ids);\n return textResult({ ok: true, ...result });\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n\n // Consolidation is OPT-IN + provider-explicit (blueprint D5/D6/DS-6 / §9):\n // the tool is registered ONLY when the daemon wired a consolidation-capable\n // engine — i.e. the user set `memory.consolidation.enabled: true` AND a usable\n // provider+model resolved (see `resolveConsolidationCapability`). The engine's\n // `consolidate` self-refuses (`no-provider`/`model-unavailable`) and logs the\n // miss otherwise — never a crash, never a silent paid call. A `model:` block\n // set for summarize/title/draft does NOT register this tool when the user\n // opted out under `memory:` (the Agent-Memory anti-pattern, §9).\n if (ctx.memoryConsolidation === true) {\n server.registerTool(\n 'memory_consolidate',\n {\n description:\n 'Explicitly consolidate recent memory observations into ONE derived lesson (append-only; originals are never mutated). Provider-gated: refuses + logs if no provider is configured — NEVER a silent paid call. Emits a type:\"lesson\" observation with provenance.',\n inputSchema: {\n types: z\n .array(z.string())\n .optional()\n .describe('Restrict candidates to these observation types.'),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Cap on candidate observations.'),\n },\n },\n async ({ types, limit }) => {\n try {\n const result = await memory.consolidate?.({ types, limit });\n if (result === undefined) {\n // Defensive: the gate (`ctx.memoryConsolidation`) and the engine's\n // `consolidate` presence agree by construction; if they ever\n // diverge, surface a clear refusal instead of a crash.\n return textResult({\n ok: false,\n degraded: true,\n error: 'consolidation is not wired on this engine',\n });\n }\n return textResult(result);\n } catch (err) {\n return textResult({ ok: false, degraded: true, error: errorMessage(err) });\n }\n },\n );\n }\n }\n\n return server;\n}\n","import type { ProjectInfo } from '@noir-ai/core';\nimport { NOIR_VERSION } from '@noir-ai/core';\n\nexport type Transport = 'stdio' | 'streamable-http';\n\nexport interface HostStatus {\n noir: string;\n project: { id: string; name: string };\n host: string;\n transport: Transport;\n daemon: boolean;\n pid?: number;\n uptimeSec?: number;\n}\n\nexport interface StatusContext {\n transport: Transport;\n daemon: boolean;\n pid?: number;\n startedAt?: number;\n}\n\nexport function buildStatus(project: ProjectInfo, ctx: StatusContext): HostStatus {\n const status: HostStatus = {\n noir: NOIR_VERSION,\n project: { id: project.id, name: project.name },\n host: project.config.host,\n transport: ctx.transport,\n daemon: ctx.daemon,\n };\n if (ctx.pid !== undefined) status.pid = ctx.pid;\n if (ctx.startedAt !== undefined) {\n status.uptimeSec = Math.max(0, Math.floor((Date.now() - ctx.startedAt) / 1000));\n }\n return status;\n}\n","import { type ProjectId, paths } from '@noir-ai/core';\nimport { openStore, type Store } from '@noir-ai/store';\n\n/**\n * A store handle opened by the daemon for its own lifecycle.\n *\n * - `store` — the single writer/reader handle (the daemon is the only writer).\n * - `dbPath` — absolute path to `<root>/.noir/store/<projectId>.db`; reported by\n * `store_status`.\n * - `degraded` — `true` when the writable open failed and we fell back to a\n * read-only handle. In degraded mode writes throw\n * `\"store is read-only (daemon down)\"`, but reads (FTS, kNN, counts) keep\n * working, so the MCP surface still reports accurate state.\n */\nexport interface DaemonStore {\n store: Store;\n dbPath: string;\n degraded: boolean;\n}\n\n/**\n * Open the project's store for the daemon — the single writer.\n *\n * Tries a read-write open first (creating the DB + migrating schema if\n * needed). On any failure (DB locked by another writer, permissions, transient\n * FS error) it falls back to a read-only open so the daemon still has an\n * accurate read handle — the FS-fallback / degraded story. The caller threads\n * `degraded` into `ServerContext` so `store_status` can surface it.\n */\nexport async function openStoreForDaemon(projectId: ProjectId, root: string): Promise<DaemonStore> {\n const dbPath = paths.storeDb(root, projectId);\n try {\n const store = await openStore({ projectId, root });\n return { store, dbPath, degraded: false };\n } catch {\n // FS-fallback: read-only. If the DB file doesn't exist yet this will also\n // throw; that propagates to the caller, which treats \"no store\" as\n // \"omit the tool\" rather than crashing the daemon.\n const store = await openStore({ projectId, root, readonly: true });\n return { store, dbPath, degraded: true };\n }\n}\n","import type { ProjectId } from '@noir-ai/core';\nimport type { Store } from '@noir-ai/store';\nimport { WorkflowEngine } from '@noir-ai/workflow';\n\n/**\n * Build the daemon's {@link WorkflowEngine} from its already-open store handle\n * + the project `root` + `projectId`.\n *\n * One engine per serve lifecycle — constructed once alongside the store (see\n * {@link openStoreForDaemon}) and reused across every HTTP request, exactly as\n * the store handle is. The engine is a thin orchestrator over the store KV, so\n * it inherits the daemon's single-writer discipline for free.\n *\n * Degraded story: the engine itself is mode-agnostic; when the underlying store\n * was opened read-only (`storeDegraded === true`), `workflow_status` still works\n * (a pure KV read via {@link Store.getState}) while `checkpoint { action:'save' }`\n * throws `\"store is read-only (daemon down)\"` — the tool handler catches that and\n * surfaces a clear JSON error instead of crashing the daemon.\n */\nexport function buildWorkflowEngine(\n store: Store,\n root: string,\n projectId: ProjectId,\n): WorkflowEngine {\n return new WorkflowEngine(store, root, projectId);\n}\n","import type { ProjectInfo } from '@noir-ai/core';\nimport { startHttpServer } from './http.js';\nimport { clearDaemonRecord, pidAlive, readDaemonRecord } from './lifecycle.js';\n\nexport interface EnsureResult {\n port: number;\n url: string;\n started: boolean;\n /**\n * Stops the daemon THIS call started. When `started` is true this closes the\n * in-process http server, clears `daemon.json`, and clears the idle timer.\n * When `started` is false (a healthy daemon was reused) this is a no-op: the\n * reused daemon is owned by whichever process started it (in tests that pid is\n * `process.pid`, so killing it would be fatal) and must not be torn down by a\n * mere consumer.\n */\n stop: () => Promise<void>;\n}\n\nasync function isHealthy(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/health`);\n return res.status === 200;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDaemonRunning(opts: {\n project: ProjectInfo;\n idleTimeoutSec: number;\n}): Promise<EnsureResult> {\n const rec = readDaemonRecord();\n if (rec) {\n if (pidAlive(rec.pid) && (await isHealthy(rec.port))) {\n return {\n port: rec.port,\n url: `http://127.0.0.1:${rec.port}/mcp`,\n started: false,\n stop: async () => {\n /* no-op: reused daemon is owned elsewhere */\n },\n };\n }\n clearDaemonRecord(); // stale — pid dead or /health failed\n }\n const running = await startHttpServer({\n project: opts.project,\n idleTimeoutSec: opts.idleTimeoutSec,\n });\n return {\n port: running.port,\n url: `http://127.0.0.1:${running.port}/mcp`,\n started: true,\n stop: running.stop,\n };\n}\n","import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';\nimport { createEmbedFn, resolveEmbedderConfig } from '@noir-ai/context';\nimport { resolveMemoryConfig } from '@noir-ai/memory';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { buildContextEngine } from './context-seam.js';\nimport { buildMemoryEngine, resolveConsolidationCapability } from './memory-seam.js';\nimport { createNoirServer, type ServerContext } from './server.js';\nimport { openStoreForDaemon } from './store-seam.js';\nimport { buildWorkflowEngine } from './workflow-seam.js';\n\nexport async function startStdioServer(ctx: ServerContext): Promise<void> {\n // The daemon is the single writer: open the store once for this stdio serve\n // lifecycle and hold it open for the life of the process (the transport\n // services requests after `connect` resolves, until stdin closes). If the\n // store can't be opened at all, omit it — `store_status` isn't\n // registered and host_status still works. Process exit reclaims the handle.\n const daemonStore = await openStoreForDaemon(ctx.project.id, ctx.project.root).catch(\n () => undefined,\n );\n // One engine per serve lifecycle, built from the same store handle.\n const engine = daemonStore\n ? buildWorkflowEngine(daemonStore.store, ctx.project.root, ctx.project.id)\n : undefined;\n // The daemon owns ONE embedder. Resolve the config once (`resolveEmbedderConfig`\n // is the core→context bridge — no cycle) and materialize the `EmbedFn` once\n // (`createEmbedFn`); the same `EmbedFn` is handed to the memory engine below.\n // The context engine still takes the `EmbedderConfig` (its own contract) and\n // resolves its embedder internally from the SAME config — for `kind:'local'`\n // the ONNX pipeline is module-cached, so the two resolutions share one loaded\n // model (no duplicate download, no second native handle). The store's\n // `degraded` flag threads through so `context_status`/`memory_save` are honest\n // under a read-only (daemon-down) handle and writes short-circuit clearly.\n const embedderCfg = resolveEmbedderConfig(ctx.project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const context = daemonStore\n ? buildContextEngine(\n daemonStore.store,\n ctx.project.root,\n ctx.project.id,\n embedderCfg,\n daemonStore.degraded,\n )\n : undefined;\n // Consolidation is OPT-IN + provider-explicit (D5/D6/DS-6 — NEVER a silent\n // paid call, the Agent-Memory anti-pattern §9). The master switch is the\n // user's `memory.consolidation.enabled`; only when it is true does the\n // model-derived provider+model even get considered. `resolveMemoryConfig` is\n // the pure core→memory bridge (no env inference, no cycle); resolved once and\n // passed to buildMemoryEngine so the engine's config reflects the user's\n // `memory:` consent exactly. The `memory_consolidate` tool is registered only\n // when the gate resolves; the engine's `consolidate` self-refuses otherwise.\n const modelCfg = resolveModelConfig(ctx.project.config.model);\n const resolvedMemory = resolveMemoryConfig(ctx.project.config.memory);\n const memoryConsolidation = resolveConsolidationCapability(resolvedMemory, modelCfg) !== null;\n const memory = daemonStore\n ? buildMemoryEngine(\n daemonStore.store,\n ctx.project.root,\n ctx.project.id,\n embed,\n modelCfg,\n daemonStore.degraded,\n resolvedMemory,\n )\n : undefined;\n const server = createNoirServer({\n ...ctx,\n ...(daemonStore\n ? {\n store: daemonStore.store,\n dbPath: daemonStore.dbPath,\n storeDegraded: daemonStore.degraded,\n }\n : {}),\n ...(engine ? { engine } : {}),\n ...(context ? { context } : {}),\n ...(memory ? { memory, memoryConsolidation } : {}),\n });\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":["NOIR_VERSION","NOIR_VERSION","paths","createEmbedFn","resolveEmbedderConfig","resolveMemoryConfig","resolveModelConfig","resolveEmbedderConfig","createEmbedFn","resolveModelConfig","resolveMemoryConfig"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@noir-ai/daemon",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "Noir daemon — the runtime authority: owns the store write handle and exposes the single Noir MCP server (stdio + Streamable HTTP).",
5
+ "license": "MIT",
6
+ "author": "agaaaptr",
7
+ "homepage": "https://github.com/agaaaptr/noir#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/agaaaptr/noir.git",
11
+ "directory": "packages/daemon"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/agaaaptr/noir/issues"
15
+ },
16
+ "keywords": [
17
+ "noir",
18
+ "daemon",
19
+ "mcp",
20
+ "server",
21
+ "stdio",
22
+ "http",
23
+ "lifecycle"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "provenance": true
31
+ },
32
+ "type": "module",
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js"
39
+ }
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md"
44
+ ],
45
+ "dependencies": {
46
+ "@modelcontextprotocol/server": "^2.0.0-beta.5",
47
+ "@modelcontextprotocol/node": "^2.0.0-beta.5",
48
+ "zod": "^4.2.0",
49
+ "@noir-ai/context": "1.0.0-beta.1",
50
+ "@noir-ai/memory": "1.0.0-beta.1",
51
+ "@noir-ai/model": "1.0.0-beta.1",
52
+ "@noir-ai/store": "1.0.0-beta.1",
53
+ "@noir-ai/workflow": "1.0.0-beta.1",
54
+ "@noir-ai/core": "1.0.0-beta.1"
55
+ },
56
+ "devDependencies": {
57
+ "@modelcontextprotocol/client": "^2.0.0-beta.5",
58
+ "@types/node": "^26.1.1"
59
+ },
60
+ "scripts": {
61
+ "build": "tsup",
62
+ "typecheck": "tsc --noEmit"
63
+ }
64
+ }