@noir-ai/daemon 1.3.0-beta.6 → 1.4.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.
- package/dist/index.d.ts +6 -6
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -245,7 +245,7 @@ declare function resolveToken(svc: IntegrationService, opts: {
|
|
|
245
245
|
integration?: string;
|
|
246
246
|
envVar?: string;
|
|
247
247
|
}, env?: NodeJS.ProcessEnv): TokenResolution;
|
|
248
|
-
/** One executed-write audit record
|
|
248
|
+
/** One executed-write audit record. Append-only JSONL — one line
|
|
249
249
|
* per executed write. Dry-runs are NOT audited (no write happened). */
|
|
250
250
|
interface IntegrationAuditEntry {
|
|
251
251
|
/** Fixed discriminator so a future `.noir/audit/` reader can filter mixed
|
|
@@ -272,7 +272,7 @@ interface IntegrationAuditEntry {
|
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
274
274
|
* Append one integration audit record to `.noir/audit/integration-<short>.jsonl`
|
|
275
|
-
* (the SAME `.noir/audit/` dir as the
|
|
275
|
+
* (the SAME `.noir/audit/` dir as the gate export — X-OQ2 resolved: REUSE).
|
|
276
276
|
* Append-only JSONL so concurrent/sequential writes don't clobber each other and
|
|
277
277
|
* the file grows linearly with executed writes. Creates the dir if missing.
|
|
278
278
|
*
|
|
@@ -305,7 +305,7 @@ declare function pidAlive(pid: number): boolean;
|
|
|
305
305
|
*
|
|
306
306
|
* The provider is resolved ONLY from an explicit opt-in: the `consolidate` tier
|
|
307
307
|
* key, falling back to `defaultProvider`. Env-var presence is NEVER consulted
|
|
308
|
-
*
|
|
308
|
+
* — `ANTHROPIC_API_KEY` being set for another tool does NOT activate
|
|
309
309
|
* consolidation here. A provider is "consolidation-capable" only when that key
|
|
310
310
|
* resolves to a configured provider block that itself declares a `model` id;
|
|
311
311
|
* otherwise this returns `null`.
|
|
@@ -365,7 +365,7 @@ declare function resolveConsolidationCapability(resolvedMemory: MemoryConfig | u
|
|
|
365
365
|
* a second connection, so the daemon's single-writer discipline is preserved
|
|
366
366
|
* (blueprint D6: in-process, no sidecar, canonical `ProjectId`).
|
|
367
367
|
*
|
|
368
|
-
* Consolidation is OPT-IN + provider-explicit (
|
|
368
|
+
* Consolidation is OPT-IN + provider-explicit (§9 — NEVER a silent paid
|
|
369
369
|
* call). The capability gate is {@link resolveConsolidationCapability} — the AND
|
|
370
370
|
* of the user's `memory.consolidation.enabled` master switch (the load-bearing
|
|
371
371
|
* gate), a usable provider+model (preferring the `memory:` block, falling back
|
|
@@ -461,7 +461,7 @@ interface ServerContext {
|
|
|
461
461
|
* resolved (see `resolveConsolidationCapability` in `./memory-seam.js`, the AND
|
|
462
462
|
* of the master switch + the provider derivation). Only when this is true is the
|
|
463
463
|
* `memory_consolidate` tool registered: consolidation is OPT-IN +
|
|
464
|
-
* provider-explicit (blueprint D5/D6
|
|
464
|
+
* provider-explicit (blueprint D5/D6 / §9), NEVER a silent paid call — a
|
|
465
465
|
* `model:` block set for summarize/title/draft does NOT flip this when the user
|
|
466
466
|
* left `memory.consolidation.enabled` false. The engine's `consolidate`
|
|
467
467
|
* self-refuses (`no-provider`/`model-unavailable`) when this flag is false, so
|
|
@@ -469,7 +469,7 @@ interface ServerContext {
|
|
|
469
469
|
*/
|
|
470
470
|
memoryConsolidation?: boolean;
|
|
471
471
|
/**
|
|
472
|
-
* Optional integration service
|
|
472
|
+
* Optional integration service. Built once per serve
|
|
473
473
|
* lifecycle from the discovered `integration.json` declarations
|
|
474
474
|
* (@noir-ai/skills `discoverIntegrations`) merged with the user's
|
|
475
475
|
* `integrations:<name>` config block. When present, the `integrations_auth`
|
package/dist/index.js
CHANGED
|
@@ -847,7 +847,7 @@ function createNoirServer(ctx) {
|
|
|
847
847
|
description: "Persist a cross-session memory observation (pattern / preference / architecture / bug / workflow / fact / decision). Stored locally on top of the Noir store (FTS5 + vectors + KV) \u2014 never truncated, never sent to an LLM. Returns the full saved observation.",
|
|
848
848
|
inputSchema: {
|
|
849
849
|
content: z.string().min(1).describe("The insight to remember (full text; never truncated)."),
|
|
850
|
-
// Open enum
|
|
850
|
+
// Open enum: unknown types are accepted + stored, so this is a
|
|
851
851
|
// free-form string (with the known values described) rather than a
|
|
852
852
|
// closed zod enum that would reject forward-compatible types.
|
|
853
853
|
type: z.string().optional().describe(
|
|
@@ -1021,7 +1021,7 @@ function createNoirServer(ctx) {
|
|
|
1021
1021
|
"comment",
|
|
1022
1022
|
"batch",
|
|
1023
1023
|
// `task:`-prefixed aliases emitted by the LOCKED noir-clickup
|
|
1024
|
-
// SKILL.md
|
|
1024
|
+
// SKILL.md; normalized to the short form internally.
|
|
1025
1025
|
"task:set-status",
|
|
1026
1026
|
"task:create-subtask",
|
|
1027
1027
|
"task:comment",
|
|
@@ -1030,7 +1030,7 @@ function createNoirServer(ctx) {
|
|
|
1030
1030
|
"Write op. Short form ('status'|'subtask'|'comment'|'batch') or the task:-prefixed form the skill emits."
|
|
1031
1031
|
),
|
|
1032
1032
|
// Flat op-specific fields (matches the LOCKED skill's call shape;
|
|
1033
|
-
// the
|
|
1033
|
+
// the `payload` nesting is honored by also accepting a payload
|
|
1034
1034
|
// object — the handler reads both).
|
|
1035
1035
|
taskId: z.string().optional().describe("status/comment: the ClickUp task id."),
|
|
1036
1036
|
status: z.string().optional().describe("status: the new status value; subtask: optional new-subtask status."),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clickup-write.ts","../src/context-seam.ts","../src/http.ts","../src/integration-seam.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":["// Slice X — `noir.clickup_write` gated-write-proxy (X-T3). The daemon-side\n// companion to the `noir-clickup` skill's write flows (2/3/4/5). SECURITY-CRITICAL.\n//\n// The proxy owns EXACTLY four responsibilities, and ONLY these:\n// 1. ALLOWLISTED URL construction — URLs are built from `op` + `payload` + the\n// workspace binding (`teamId`/`listId`). A caller-supplied `url` is NEVER\n// honored; ClickUp ids are validated against a strict charset so a task id\n// can't smuggle a path segment. This is the prompt-injection defense: a\n// malicious task field can become JSON DATA in a POST body, but it can NEVER\n// redirect the proxy to a different endpoint.\n// 2. DRY-RUN → CONFIRM gate — unless `confirm === true`, the proxy computes the\n// exact would-be request(s) and returns a PREVIEW without any network call.\n// `fetch` is not even touched. Only an explicit `confirm:true` (surfaced to\n// the host's tool-approval gate) reaches the network.\n// 3. EXECUTE — on confirm, POST/PUT to ClickUp API v2 with the `pk_<token>`\n// auth header (NO Bearer), concurrency cap 4 on batch, 429 backoff keyed on\n// `X-RateLimit-Reset` (await until reset, retry once — never blind-retry).\n// 4. AUDIT — every EXECUTED write appends a record to `.noir/audit/` via the\n// seam's `writeIntegrationAudit`. Dry-runs are NOT audited.\n//\n// The token is resolved by the caller (the tool handler, via `resolveToken`) and\n// passed in ONLY for the execute path; the dry-run path never sees it. The token\n// NEVER appears in previews (redacted `pk_***`), results, or audit entries.\n//\n// Doctrine: graceful degradation (no-token ⇒ refuse, no fetch; no-config ⇒\n// refuse; never a crash); NO silent writes (confirm gate is HARD); allowlist is\n// the only source of URLs.\n\n// ClickUp API v2 base. Constant — the proxy never accepts a base URL from input.\nconst CLICKUP_BASE = 'https://api.clickup.com/api/v2';\n\n/** Strict charset for ClickUp ids (task/list/space/team). ClickUp task ids are\n * alphanumeric (`abc123`) or numeric; custom ids look like `CU-42`. Allow\n * letters, digits, underscore, hyphen only — rejects `/`, `?`, `#`, spaces, and\n * anything else that could mutate the path/query. */\nconst ID_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nfunction assertId(value: string, field: string): string {\n if (typeof value !== 'string' || !ID_PATTERN.test(value)) {\n throw new InvalidOp(`invalid ${field}: ${JSON.stringify(value)} (must match ${ID_PATTERN})`);\n }\n return value;\n}\n\n/** Op-shaped error thrown when input violates the contract (bad id, missing\n * required field, unknown op). The handler maps it to `{ok:false, reason:'invalid-op'}`. */\nexport class InvalidOp extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidOp';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Op vocabulary (X-T3 spec resolution). The proxy accepts BOTH the short verbs\n// (`status` | `subtask` | `comment` | `batch`) from the X-T3 spec AND the\n// `task:`-prefixed verbs (`task:set-status` | `task:create-subtask` |\n// `task:comment` | `task:batch-create`) that the LOCKED `noir-clickup` SKILL.md\n// documents. The skill is X-T1+T2-locked (cannot be edited), so the tool MUST\n// honor the verbs the skill emits; the short forms are accepted as aliases for\n// ergonomics + spec alignment. Normalized to the short form internally.\n// ---------------------------------------------------------------------------\nconst OP_ALIASES: Record<string, string> = {\n 'task:set-status': 'status',\n 'task:create-subtask': 'subtask',\n 'task:comment': 'comment',\n 'task:batch-create': 'batch',\n};\nexport type ClickUpOp = 'status' | 'subtask' | 'comment' | 'batch';\nexport function normalizeOp(op: string): ClickUpOp {\n const resolved = OP_ALIASES[op] ?? op;\n if (\n resolved !== 'status' &&\n resolved !== 'subtask' &&\n resolved !== 'comment' &&\n resolved !== 'batch'\n ) {\n throw new InvalidOp(`unknown op: ${JSON.stringify(op)}`);\n }\n return resolved;\n}\n\n/** Workspace binding the proxy resolves listId/teamId from (config > payload). */\nexport interface ClickUpBinding {\n teamId?: string;\n listId?: string;\n}\n\n/** A constructed HTTP request against an allowlisted endpoint. The auth header\n * is ABSENT here — it is added at execute time only; the dry-run preview shows\n * a redacted placeholder so the token never leaves the execute path. */\nexport interface BuiltRequest {\n method: 'GET' | 'POST' | 'PUT';\n /** Full allowlisted URL (constructed from op + payload + binding). */\n url: string;\n /** JSON body (already stringified-safe as an object). */\n body?: Record<string, unknown>;\n /** Stable descriptor for the audit `target` field (e.g. `task/abc123`). */\n target: string;\n}\n\n/** A single preview/result row. */\nexport interface RequestRow {\n method: string;\n url: string;\n body?: Record<string, unknown>;\n /** Redacted auth header for the preview (always `pk_***`). */\n auth: 'pk_***';\n target: string;\n}\n\n/** Resolved ClickUp workspace binding (config > payload). */\nfunction resolveBinding(payload: Record<string, unknown>, binding: ClickUpBinding): ClickUpBinding {\n const listId =\n (typeof payload.listId === 'string' ? payload.listId : undefined) ?? binding.listId;\n const teamId =\n (typeof payload.teamId === 'string' ? payload.teamId : undefined) ?? binding.teamId;\n const resolved: ClickUpBinding = {};\n if (listId !== undefined) resolved.listId = assertId(listId, 'listId');\n if (teamId !== undefined) resolved.teamId = assertId(teamId, 'teamId');\n return resolved;\n}\n\n// ---------------------------------------------------------------------------\n// Request builders — one per op. Each returns the request(s) the op renders.\n// URLs come ONLY from these templates; a caller-supplied `url` is ignored.\n// ---------------------------------------------------------------------------\nfunction buildStatus(payload: Record<string, unknown>): BuiltRequest {\n const taskId = assertId(String(payload.taskId ?? ''), 'taskId');\n const status = payload.status;\n if (typeof status !== 'string' || status.length === 0) {\n throw new InvalidOp('status op requires payload.status (non-empty string)');\n }\n return {\n method: 'PUT',\n url: `${CLICKUP_BASE}/task/${taskId}`,\n body: { status },\n target: `task/${taskId}`,\n };\n}\n\nfunction buildSubtask(payload: Record<string, unknown>, binding: ClickUpBinding): BuiltRequest[] {\n const resolved = resolveBinding(payload, binding);\n if (!resolved.listId)\n throw new InvalidOp(\n 'subtask op requires a listId (payload.listId or integrations.clickup.listId)',\n );\n const parentTaskId = assertId(String(payload.parentTaskId ?? ''), 'parentTaskId');\n const name = payload.name;\n if (typeof name !== 'string' || name.length === 0) {\n throw new InvalidOp('subtask op requires payload.name (non-empty string)');\n }\n const create: BuiltRequest = {\n method: 'POST',\n url: `${CLICKUP_BASE}/list/${resolved.listId}/task`,\n body: { name, parent: parentTaskId },\n target: `list/${resolved.listId}/task (parent ${parentTaskId})`,\n };\n const reqs = [create];\n // Optional follow-up: set the new subtask's status once it exists. The taskId\n // is the ClickUp-returned id; the proxy fills it from the create response at\n // execute time. At dry-run time, the preview shows the would-be request with\n // a placeholder id + a `__dependsOn` marker so the host sees the dependency.\n const status =\n typeof payload.status === 'string' && payload.status.length > 0 ? payload.status : undefined;\n if (status !== undefined) {\n reqs.push({\n method: 'PUT',\n url: `${CLICKUP_BASE}/task/__sub_id__`,\n body: { status },\n target: `task/<new subtask> (status)`,\n });\n }\n return reqs;\n}\n\nfunction buildComment(payload: Record<string, unknown>): BuiltRequest {\n const taskId = assertId(String(payload.taskId ?? ''), 'taskId');\n const commentText = payload.commentText;\n if (typeof commentText !== 'string' || commentText.length === 0) {\n throw new InvalidOp('comment op requires payload.commentText (non-empty string)');\n }\n const notifyAll = typeof payload.notifyAll === 'boolean' ? payload.notifyAll : false;\n const body: Record<string, unknown> = { comment_text: commentText, notify_all: notifyAll };\n // assignee is the ClickUp user id (integer) and OPTIONAL.\n if (payload.assigneeId !== undefined && payload.assigneeId !== null) {\n const assignee =\n typeof payload.assigneeId === 'number' ? payload.assigneeId : Number(payload.assigneeId);\n if (!Number.isFinite(assignee))\n throw new InvalidOp('comment op payload.assigneeId must be a number');\n body.assignee = assignee;\n }\n return {\n method: 'POST',\n url: `${CLICKUP_BASE}/task/${taskId}/comment`,\n body,\n target: `task/${taskId}/comment`,\n };\n}\n\n/** A normalized task for the batch op (H2 markdown + the `tasks[]` array both\n * reduce to this). All fields are JSON DATA in the POST body — adversary text\n * in a task title becomes the `name` string, never an executable instruction. */\nexport interface NormalizedTask {\n name: string;\n description?: string;\n tags?: string[];\n assignees?: number[];\n status?: string;\n}\n\n/** Parse H2-per-task markdown into normalized tasks. Each `## <title>` opens a\n * task; body lines until the next `## ` are the description, EXCEPT lines that\n * match `- key: value` which are folded into structured fields (assignees/tags).\n * Tolerant: a missing leading H2 still yields the body as one task (named from\n * the first non-empty line) so a slightly-malformed template still previews. */\nexport function parseH2Tasks(markdown: string): NormalizedTask[] {\n const lines = markdown.split(/\\r?\\n/);\n const tasks: NormalizedTask[] = [];\n let cur: NormalizedTask | null = null;\n const desc: string[] = [];\n for (const raw of lines) {\n const line = raw;\n const h2 = /^##\\s+(.+?)\\s*$/.exec(line);\n if (h2) {\n const title = h2[1];\n if (title !== undefined) {\n if (cur) cur.description = desc.join('\\n').trim() || undefined;\n cur = { name: title.trim() };\n desc.length = 0;\n tasks.push(cur);\n continue;\n }\n }\n if (!cur) {\n // Pre-H2 content: skip bare blank lines; a stray line becomes a task so\n // the template still previews (graceful, not a crash).\n if (line.trim().length === 0) continue;\n cur = { name: line.trim() };\n desc.length = 0;\n tasks.push(cur);\n continue;\n }\n const meta = /^-\\s+([A-Za-z_]+)\\s*:\\s*(.+?)\\s*$/.exec(line);\n if (meta) {\n const keyRaw = meta[1];\n const valRaw = meta[2];\n if (keyRaw !== undefined && valRaw !== undefined) {\n const key = keyRaw.toLowerCase();\n const val = valRaw;\n if (key === 'tag' || key === 'tags') {\n cur.tags = [\n ...(cur.tags ?? []),\n ...val\n .split(',')\n .map((t) => t.trim())\n .filter((t) => t.length > 0),\n ];\n } else if (key === 'assignee' || key === 'assignees') {\n const ids = val\n .split(',')\n .map((t) => t.trim())\n .map((t) => Number(t))\n .filter((n) => Number.isFinite(n));\n cur.assignees = [...(cur.assignees ?? []), ...ids];\n } else if (key === 'status') {\n cur.status = val;\n } else {\n // Unknown meta key: keep as a description line (don't lose information).\n desc.push(line);\n }\n } else {\n desc.push(line);\n }\n } else {\n desc.push(line);\n }\n }\n if (cur) cur.description = desc.join('\\n').trim() || undefined;\n // Drop tasks that ended up with an empty name (e.g. a trailing `## ` with no title).\n return tasks.filter((t) => t.name.length > 0);\n}\n\nfunction normalizeBatchInput(payload: Record<string, unknown>): NormalizedTask[] {\n if (Array.isArray(payload.tasks)) {\n return payload.tasks.map((t, i) => {\n if (typeof t !== 'object' || t === null)\n throw new InvalidOp(`batch payload.tasks[${i}] must be an object`);\n const obj = t as Record<string, unknown>;\n const name = obj.name;\n if (typeof name !== 'string' || name.length === 0)\n throw new InvalidOp(`batch payload.tasks[${i}].name required`);\n const out: NormalizedTask = { name };\n if (typeof obj.description === 'string') out.description = obj.description;\n if (Array.isArray(obj.tags)) out.tags = obj.tags.map((s) => String(s));\n if (Array.isArray(obj.assignees))\n out.assignees = obj.assignees.map((n) => Number(n)).filter((n) => Number.isFinite(n));\n if (typeof obj.status === 'string') out.status = obj.status;\n return out;\n });\n }\n if (typeof payload.markdown === 'string') {\n return parseH2Tasks(payload.markdown);\n }\n throw new InvalidOp('batch op requires payload.tasks[] OR payload.markdown (H2-per-task)');\n}\n\nfunction buildBatch(payload: Record<string, unknown>, binding: ClickUpBinding): BuiltRequest[] {\n const resolved = resolveBinding(payload, binding);\n if (!resolved.listId)\n throw new InvalidOp(\n 'batch op requires a listId (payload.listId or integrations.clickup.listId)',\n );\n const tasks = normalizeBatchInput(payload);\n if (tasks.length === 0)\n throw new InvalidOp('batch op yielded no tasks (empty markdown / tasks[])');\n return tasks.map((t, i) => {\n const body: Record<string, unknown> = { name: t.name };\n if (t.description !== undefined) body.description = t.description;\n if (t.tags !== undefined && t.tags.length > 0) body.tags = t.tags;\n // ClickUp `POST /list/{list_id}/task` takes the PLURAL `assignees: number[]`\n // (see references/clickup-api.md). The singular `assignee` key is silently\n // ignored → tasks created with NO assignees while audit reports success\n // (silent data loss). Only the comment endpoint uses singular `assignee`.\n if (t.assignees !== undefined && t.assignees.length > 0) body.assignees = t.assignees;\n return {\n method: 'POST' as const,\n url: `${CLICKUP_BASE}/list/${resolved.listId}/task`,\n body,\n target: `list/${resolved.listId}/task (#${i + 1} ${t.name.slice(0, 40)})`,\n };\n });\n}\n\n/** Build ALL requests an op renders, WITHOUT touching the network. Used by both\n * the dry-run preview (no auth) and the execute path (auth added). Throws\n * `InvalidOp` on a contract violation (bad id, missing field, unknown op). */\nexport function buildRequests(\n opRaw: string,\n payload: Record<string, unknown>,\n binding: ClickUpBinding,\n): { op: ClickUpOp; requests: BuiltRequest[] } {\n const op = normalizeOp(opRaw);\n switch (op) {\n case 'status':\n return { op, requests: [buildStatus(payload)] };\n case 'subtask':\n return { op, requests: buildSubtask(payload, binding) };\n case 'comment':\n return { op, requests: [buildComment(payload)] };\n case 'batch':\n return { op, requests: buildBatch(payload, binding) };\n }\n}\n\n/** Render a dry-run preview row set. NO auth header, NO network — the token\n * never enters this path. */\nexport function previewRows(requests: BuiltRequest[]): RequestRow[] {\n return requests.map((r) => ({\n method: r.method,\n url: r.url,\n ...(r.body !== undefined ? { body: r.body } : {}),\n auth: 'pk_***',\n target: r.target,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Execute path. Reached ONLY when `confirm === true`. The token is added here\n// (and ONLY here) as `Authorization: pk_<token>` (NO Bearer — ClickUp v2 rejects\n// Bearer). 429 backoff reads `X-RateLimit-Reset` and awaits until reset, then\n// retries ONCE per request.\n// ---------------------------------------------------------------------------\n\n/** Minimal fetch shape the proxy needs. Bound to global `fetch` in production;\n * a spy in tests (cassette). */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface ExecResult {\n target: string;\n method: string;\n url: string;\n httpStatus: number;\n success: boolean;\n /** ClickUp's parsed JSON body (or `{}` when absent). NEVER contains the token\n * (ClickUp never echoes it). */\n response: Record<string, unknown>;\n /** Present when a 429 backoff fired on this request. */\n rateLimitedWaitMs?: number;\n /** For `subtask` with a status follow-up: the new subtask id resolved from the\n * create response so the host can reference it. */\n newTaskId?: string;\n /** Error message on a non-2xx / network failure (NEVER includes the token). */\n error?: string;\n}\n\n/** Build the outbound auth+content-type headers. The token is added here and\n * ONLY here (as `Authorization: pk_<token>`, NO Bearer — ClickUp v2 rejects\n * Bearer). Despite the historical name, this function does NOT redact — it\n * attaches the RAW token; the dry-run preview never calls it (previews show\n * the redacted `pk_***` placeholder instead), so the live token only travels\n * on the execute path's outbound request. */\nfunction authHeaders(token: string): Record<string, string> {\n return {\n 'content-type': 'application/json',\n authorization: `pk_${token}`,\n };\n}\n\n/** Read `X-RateLimit-Reset` (epoch seconds) and compute the wait until reset.\n * Caps at a sane maximum so a malformed far-future header can't pin the daemon. */\nfunction rateLimitWaitMs(res: Response): number | undefined {\n const reset = res.headers.get('X-RateLimit-Reset');\n if (reset === null) return undefined;\n const epochSec = Number(reset);\n if (!Number.isFinite(epochSec)) return undefined;\n const waitMs = epochSec * 1000 - Date.now();\n // Clamp to [0, 60s]. A negative (past reset) → 0 (retry now). A huge value\n // (malformed) → 60s (don't pin the daemon on a bad header).\n if (waitMs <= 0) return 0;\n if (waitMs > 60_000) return 60_000;\n return waitMs;\n}\n\nasync function sleep(ms: number): Promise<void> {\n if (ms <= 0) return;\n await new Promise<void>((r) => setTimeout(r, ms));\n}\n\n/** Execute a single request with one 429-bounded retry. */\nasync function executeOne(\n req: BuiltRequest,\n token: string,\n fetchImpl: FetchLike,\n): Promise<ExecResult> {\n const init: RequestInit = {\n method: req.method,\n headers: authHeaders(token),\n ...(req.body !== undefined ? { body: JSON.stringify(req.body) } : {}),\n };\n let res: Response;\n try {\n res = await fetchImpl(req.url, init);\n } catch (err) {\n return {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: 0,\n success: false,\n response: {},\n error: err instanceof Error ? err.message : String(err),\n };\n }\n // 429: honor the header literally, await until reset, retry ONCE.\n if (res.status === 429) {\n const waitMs = rateLimitWaitMs(res) ?? 1000;\n await sleep(waitMs);\n try {\n res = await fetchImpl(req.url, init);\n } catch (err) {\n return {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: 429,\n success: false,\n response: {},\n rateLimitedWaitMs: waitMs,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n const final = await toExecResult(req, res);\n return { ...final, rateLimitedWaitMs: waitMs };\n }\n return toExecResult(req, res);\n}\n\nasync function toExecResult(req: BuiltRequest, res: Response): Promise<ExecResult> {\n const success = res.status >= 200 && res.status < 300;\n let parsed: Record<string, unknown> = {};\n try {\n parsed = (await res.json()) as Record<string, unknown>;\n } catch {\n try {\n const text = await res.text();\n parsed = text.length > 0 ? { _raw: text } : {};\n } catch {\n parsed = {};\n }\n }\n const result: ExecResult = {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: res.status,\n success,\n response: parsed,\n };\n if (!success) {\n const errVal = parsed.err;\n result.error = typeof errVal === 'string' ? errVal : `HTTP ${res.status}`;\n }\n return result;\n}\n\n/** Concurrency-limited map (cap 4 per spec: 4-8, conservative). Each item gets\n * at most one in-flight request; the 429 retry is awaited within its slot. */\nasync function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let cursor = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n while (true) {\n const i = cursor++;\n if (i >= items.length) break;\n const item = items[i];\n // `i` is bounded by items.length above; the indexed access is safe.\n if (item !== undefined) results[i] = await fn(item, i);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\n/** Execute an op's requests against ClickUp. The `subtask` op is special: its\n * optional status follow-up needs the create response's `id`, so the two\n * requests run sequentially with the id threaded through. `batch` runs with a\n * concurrency cap of 4 + per-request 429 backoff. */\nexport async function executeOp(\n op: ClickUpOp,\n requests: BuiltRequest[],\n token: string,\n fetchImpl: FetchLike,\n): Promise<ExecResult[]> {\n if (op === 'subtask' && requests.length === 2) {\n const create = requests[0];\n const statusReq = requests[1];\n if (!create || !statusReq) {\n // Defensive: length === 2 guard above makes this unreachable, but\n // noUncheckedIndexedAccess forces the check.\n throw new InvalidOp('subtask op expects exactly two rendered requests');\n }\n const createRes = await executeOne(create, token, fetchImpl);\n if (!createRes.success) return [createRes];\n const newId = typeof createRes.response.id === 'string' ? createRes.response.id : undefined;\n if (!newId) {\n // Create succeeded but no id came back — can't set status. Surface the\n // partial success; the host sees the create worked, the status didn't.\n return [\n createRes,\n {\n target: statusReq.target,\n method: statusReq.method,\n url: statusReq.url,\n httpStatus: 0,\n success: false,\n response: {},\n error: 'subtask created but no id returned; status follow-up skipped',\n },\n ];\n }\n createRes.newTaskId = newId;\n // The response-derived id is substituted into the allowlisted URL template\n // — validate it against the SAME charset as every caller-supplied id so a\n // compromised/buggy ClickUp response can't smuggle a path segment (the\n // module's stated invariant: every id is charset-validated before use in a URL).\n assertId(newId, 'subtask-create-response.id');\n // Rewrite the placeholder URL with the real id and execute.\n const realStatusReq: BuiltRequest = {\n method: statusReq.method,\n url: statusReq.url.replace('__sub_id__', newId),\n target: `task/${newId} (status)`,\n ...(statusReq.body !== undefined ? { body: statusReq.body } : {}),\n };\n const statusRes = await executeOne(realStatusReq, token, fetchImpl);\n return [createRes, statusRes];\n }\n if (op === 'batch') {\n return mapWithConcurrency(requests, 4, (req) => executeOne(req, token, fetchImpl));\n }\n // status / comment / (subtask without status): single request, executed in order.\n const out: ExecResult[] = [];\n for (const req of requests) {\n out.push(await executeOne(req, token, fetchImpl));\n }\n return out;\n}\n","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 { buildIntegrationService } from './integration-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 // Slice X — integration service (X-T3). Built once per serve lifecycle from\n // the discovered declarations + the user's `integrations:` config overlay.\n // Discovery is best-effort; an empty service still registers\n // `integrations_auth` (env-var resolution needs no declaration) and simply\n // skips `noir.clickup_write`. Built unconditionally (no store dependency) so\n // `integrations_auth` works even under a read-only (daemon-down) store.\n const integrations = buildIntegrationService(opts.project.root, opts.project.config.integrations);\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 ...(integrations ? { integrations } : {}),\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","// Slice X — integration runtime seam (X-T3). The daemon-side companion to the\n// shipped `integration.json` declarations (X-T1) + the core `integrations` config\n// block (X-T2). This module is deliberately split into THREE concerns so the\n// security-critical paths can be unit-tested in isolation:\n//\n// 1. `IntegrationService` — the per-serve-lifecycle handle built ONCE from the\n// discovered declarations (@noir-ai/skills `discoverIntegrations`) and held\n// on `ctx.integrations` (mirrors `ctx.store` / `ctx.engine` / `ctx.memory`).\n// Discovery is best-effort: a missing/unreadable pack degrades to an empty\n// service (the `integrations_auth` tool still resolves a caller-supplied\n// `envVar`; `noir.clickup_write` is simply not registered).\n// 2. `resolveToken` — the ONLY place a token value touches process.env. Read\n// at call time, returned to the calling agent, NEVER logged/echoed to\n// stderr/audit bodies (the token travels only in the tool RESULT to the\n// trusted host + the outbound `Authorization` header).\n// 3. `writeIntegrationAudit` — append-only JSONL into the SAME `.noir/audit/`\n// dir as the S4 gate export (X-OQ2 resolved: REUSE, do not invent a new\n// audit location). One executed write ⇒ one line; dry-runs are NOT audited.\n//\n// Doctrine: graceful degradation (no-token ⇒ manual-paste / no-token refuse,\n// never a crash); `.noir/` SOT (audit there); NO silent writes (the confirm gate\n// lives in the proxy module + the tool handler, never here).\n\nimport { appendFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { paths } from '@noir-ai/core';\nimport type { IntegrationDeclaration } from '@noir-ai/skills';\nimport { discoverIntegrations } from '@noir-ai/skills';\n\n/**\n * Per-integration binding resolved at the seam: the shipped declaration (source\n * of truth for `auth.tokenEnv` + `runtime`) merged with the user's\n * `integrations.<name>` config overlay (source of truth for `teamId`/`listId`/\n * `spaceId` + a local `auth.tokenEnv` override / `runtime` downgrade). The\n * declaration is authoritative for `tokenEnv` unless the config explicitly\n * overrides it (a workspace that renamed the env var).\n */\nexport interface IntegrationBinding {\n /** Full declaration name, e.g. `'noir-clickup'`. */\n name: string;\n /** Short name — declaration name with the `noir-` namespace stripped\n * (`'clickup'`). The `noir.clickup_write` tool segment + the documented\n * `integrations.clickup.*` config key both use this form. */\n shortName: string;\n declaration: IntegrationDeclaration;\n /** Resolved token env-var name (config override > declaration). */\n tokenEnv: string;\n /** Effective runtime tier: the user's `integrations.<name>.runtime` overlay\n * wins when set, else the declaration's `runtime`. The daemon gates write-tool\n * registration on THIS (not the declaration's) so a local downgrade to `none`\n * actually takes the write tool off — the tier model: `none` = skill-side\n * reads only, `integrations_auth` still resolves the token. */\n effectiveRuntime: IntegrationDeclaration['runtime'];\n teamId?: string;\n listId?: string;\n spaceId?: string;\n}\n\n/**\n * The per-serve-lifecycle integration handle. Built once (mirrors the store +\n * workflow + memory engines) and reused across every MCP request. Carries the\n * discovered declarations merged with the user config so the tools never re-read\n * the filesystem per call.\n */\nexport interface IntegrationService {\n /** Project root — the anchor for `.noir/audit/` writes. */\n root: string;\n /** Bindings keyed by BOTH the full name (`noir-clickup`) and the short name\n * (`clickup`) so a tool/config using either form resolves the same binding. */\n bindings: Map<string, IntegrationBinding>;\n}\n\n/** Strip the `noir-` namespace prefix from a declaration name. The short form\n * is what the `noir.<short>_write` tool segment + the `integrations.<short>`\n * config key use; the declaration itself always carries the full `noir-` name. */\nexport function shortNameOf(name: string): string {\n return name.replace(/^noir-/, '');\n}\n\n/**\n * Build the integration service for one serve lifecycle. Discovery is\n * best-effort: a missing/unreadable skills pack ⇒ an empty service (the tools\n * degrade gracefully — `integrations_auth` still resolves a caller `envVar`;\n * `noir.clickup_write` is simply not registered). Never throws.\n *\n * @param root project root (`.noir/audit/` anchor)\n * @param configIntegrations the parsed `integrations` block from `.noir/config.yml`\n * (`{ [name]: { auth?, runtime, teamId?, listId?, spaceId? } }`). Keys may use\n * either the full `noir-clickup` or the short `clickup` form.\n */\nexport function buildIntegrationService(\n root: string,\n configIntegrations: Record<\n string,\n {\n auth?: { tokenEnv?: string };\n runtime?: 'none' | 'gated-write-proxy' | 'mcp-stdio' | 'external-mcp';\n teamId?: string;\n listId?: string;\n spaceId?: string;\n }\n > = {},\n): IntegrationService {\n let discovered: IntegrationDeclaration[] = [];\n try {\n discovered = discoverIntegrations().map(\n (s: { declaration: IntegrationDeclaration }) => s.declaration,\n );\n } catch {\n // A malformed shipped declaration would have failed the skills pack's own\n // tests; if we still hit this, degrade to \"no integrations wired\" rather\n // than crashing the daemon.\n discovered = [];\n }\n const bindings = new Map<string, IntegrationBinding>();\n for (const declaration of discovered) {\n // Config lookup tolerates both `noir-clickup` and `clickup` keys (the docs\n // use the short form; the full form is accepted for symmetry).\n const cfg =\n configIntegrations[declaration.name] ?? configIntegrations[shortNameOf(declaration.name)];\n const tokenEnv = cfg?.auth?.tokenEnv ?? declaration.auth.tokenEnv;\n // Effective runtime: the user's config overlay wins when set; otherwise the\n // declaration's tier. This is what the daemon must gate write-tool\n // registration on (NOT the declaration's runtime) — a local downgrade to\n // `none` for a read-only run takes the write tool off without touching the\n // shipped declaration.\n const effectiveRuntime = cfg?.runtime ?? declaration.runtime;\n const binding: IntegrationBinding = {\n name: declaration.name,\n shortName: shortNameOf(declaration.name),\n declaration,\n tokenEnv,\n effectiveRuntime,\n ...(cfg?.teamId !== undefined ? { teamId: cfg.teamId } : {}),\n ...(cfg?.listId !== undefined ? { listId: cfg.listId } : {}),\n ...(cfg?.spaceId !== undefined ? { spaceId: cfg.spaceId } : {}),\n };\n bindings.set(binding.name, binding);\n bindings.set(binding.shortName, binding);\n }\n return { root, bindings };\n}\n\n/** Look up a binding by either the full or short name. */\nexport function findBinding(svc: IntegrationService, name: string): IntegrationBinding | undefined {\n return svc.bindings.get(name);\n}\n\n/** Result of a token resolution attempt. */\nexport type TokenResolution =\n | { ok: true; token: string; envVar: string }\n | { ok: false; reason: 'no-token'; envVar: string }\n | { ok: false; reason: 'unknown-integration'; integration: string };\n\n/**\n * Resolve an integration token VALUE from `process.env` at CALL TIME (never at\n * load time; never cached beyond the call). The token is returned to the calling\n * agent in the tool result (trusted host) + travels in the outbound\n * `Authorization` header — and NOWHERE else (no stderr, no audit body).\n *\n * Two call shapes:\n * - `{ integration: 'noir-clickup' }` → look up `tokenEnv` from the discovered\n * declaration (config override honored), then read `process.env[tokenEnv]`.\n * - `{ envVar: 'CLICKUP_API_TOKEN' }` → read `process.env[envVar]` directly.\n */\nexport function resolveToken(\n svc: IntegrationService,\n opts: { integration?: string; envVar?: string },\n env: NodeJS.ProcessEnv = process.env,\n): TokenResolution {\n const integration = opts.integration;\n const envVar = opts.envVar;\n if (integration !== undefined) {\n const binding = findBinding(svc, integration);\n if (!binding) return { ok: false, reason: 'unknown-integration', integration };\n const name = binding.tokenEnv;\n const value = env[name];\n if (typeof value === 'string' && value.length > 0) {\n return { ok: true, token: value, envVar: name };\n }\n return { ok: false, reason: 'no-token', envVar: name };\n }\n if (envVar !== undefined) {\n const value = env[envVar];\n if (typeof value === 'string' && value.length > 0) {\n return { ok: true, token: value, envVar };\n }\n return { ok: false, reason: 'no-token', envVar };\n }\n // Neither integration nor envVar supplied: synthesize a clear refusal rather\n // than a crash. The skill drives the manual-paste fallback from this.\n return { ok: false, reason: 'no-token', envVar: '' };\n}\n\n/** One executed-write audit record (X-T3, X-OQ2). Append-only JSONL — one line\n * per executed write. Dry-runs are NOT audited (no write happened). */\nexport interface IntegrationAuditEntry {\n /** Fixed discriminator so a future `.noir/audit/` reader can filter mixed\n * gate (`kind:'gate'`-ish) + integration rows. */\n kind: 'integration';\n /** Declaration name, e.g. `'noir-clickup'`. */\n integration: string;\n /** Op verb (normalized short form): `status` | `subtask` | `comment` | `batch`. */\n op: string;\n /** Stable target descriptor (e.g. `'task/abc123'`, `'list/90125/task'`). */\n target: string;\n /** HTTP method on the allowlisted endpoint. */\n method: string;\n /** HTTP status returned by ClickUp (last attempt after 429 backoff). */\n httpStatus: number;\n /** True iff `httpStatus` is 2xx. */\n success: boolean;\n /** Epoch-millis when the write was attempted. */\n timestamp: number;\n /** Present when a 429 backoff fired (the spec calls for recording the wait). */\n rateLimitedWaitMs?: number;\n /** Error message when `success === false` (NEVER includes the token). */\n error?: string;\n}\n\n/**\n * Append one integration audit record to `.noir/audit/integration-<short>.jsonl`\n * (the SAME `.noir/audit/` dir as the S4 gate export — X-OQ2 resolved: REUSE).\n * Append-only JSONL so concurrent/sequential writes don't clobber each other and\n * the file grows linearly with executed writes. Creates the dir if missing.\n *\n * Propagates filesystem errors to the caller — the gated proxy's handler is the\n * best-effort boundary: it catches, sets the result envelope's `audited:false`,\n * and continues (the ClickUp write already happened; an audit failure does NOT\n * roll it back). Keeping the throw honest makes `audited:false` reachable instead\n * of a perpetually-true flag.\n */\nexport function writeIntegrationAudit(\n root: string,\n integrationName: string,\n entry: IntegrationAuditEntry,\n): void {\n // X-OQ2 resolved: REUSE the S4 `.noir/audit/` dir (SOT — no second audit\n // location). Integration writes are append-only + cross-task (not keyed by a\n // Noir task id), so a per-integration JSONL file is the natural shape: one\n // executed write ⇒ one line. The `.jsonl` extension keeps it distinct from\n // the per-task `.json` exports.\n const file = join(paths.auditDir(root), `integration-${shortNameOf(integrationName)}.jsonl`);\n mkdirSync(paths.auditDir(root), { recursive: true });\n appendFileSync(file, `${JSON.stringify(entry)}\\n`, 'utf8');\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?.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 {\n buildRequests,\n type ClickUpOp,\n type ExecResult,\n executeOp,\n normalizeOp,\n previewRows,\n} from './clickup-write.js';\nimport {\n findBinding,\n type IntegrationAuditEntry,\n type IntegrationService,\n resolveToken,\n writeIntegrationAudit,\n} from './integration-seam.js';\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 * Optional integration service (slice X — X-T3). Built once per serve\n * lifecycle from the discovered `integration.json` declarations\n * (@noir-ai/skills `discoverIntegrations`) merged with the user's\n * `integrations:<name>` config block. When present, the `integrations_auth`\n * tool is ALWAYS registered (resolves a token by env var at call time — kills\n * the non-interactive-shell gotcha), and `noir.clickup_write` is registered\n * when a `noir-clickup` declaration with `runtime:'gated-write-proxy'` is\n * discovered. Both tools degrade gracefully (`no-token`/`no-config`) when the\n * binding/env is absent — never a crash. Token values NEVER enter stderr or\n * audit bodies (only the tool RESULT to the trusted host + the outbound\n * `Authorization` header).\n */\n integrations?: IntegrationService;\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 // The integration service (slice X — X-T3) is optional: stdio/HTTP inject it\n // once per serve lifecycle (mirrors the store/engine/memory/context handles).\n // `integrations_auth` is ALWAYS registered when the service is present — it\n // resolves an integration token VALUE from process.env at CALL TIME (never at\n // load time; never cached) and returns it to the calling agent. This kills the\n // non-interactive-shell gotcha (the skill + its MCP tool never read shell env\n // directly; only the daemon's process env matters). When the env value is\n // absent, the tool reports `no-token` and the skill falls back to manual paste.\n //\n // SECURITY: the token travels ONLY in the tool RESULT to the trusted host +\n // the outbound `Authorization` header. It is never written to stderr, never\n // persisted, never echoed into an audit body. `noir.clickup_write` (below) is\n // registered only when a `noir-clickup` declaration with\n // `runtime:'gated-write-proxy'` is discovered, and its dry-run path never even\n // resolves the token.\n if (ctx.integrations) {\n const integrations = ctx.integrations;\n\n server.registerTool(\n 'integrations_auth',\n {\n description:\n \"Resolve an integration token VALUE server-side at call time (kills the non-interactive-shell gotcha). Pass {integration:'noir-clickup'} to resolve tokenEnv from the discovered declaration, or {envVar:'CLICKUP_API_TOKEN'} to name the env var directly. Returns {ok:true,token,envVar} when present, or {ok:false,reason:'no-token',envVar} when absent (the skill then does manual-paste fallback). The token is returned ONLY in this tool result — never logged, never persisted.\",\n inputSchema: {\n integration: z\n .string()\n .optional()\n .describe(\n \"Integration name (e.g. 'noir-clickup' or 'clickup'); resolves tokenEnv from the discovered declaration (config override honored).\",\n ),\n envVar: z\n .string()\n .optional()\n .describe(\n 'Env-var name to read directly (e.g. CLICKUP_API_TOKEN). Used when no integration binding applies.',\n ),\n },\n },\n async ({ integration, envVar }) => {\n if (integration === undefined && envVar === undefined) {\n return textResult({\n ok: false,\n reason: 'invalid-input',\n error: 'pass one of {integration} or {envVar}',\n });\n }\n const resolution = resolveToken(integrations, { integration, envVar });\n return textResult(resolution);\n },\n );\n\n // `noir.clickup_write` — the gated-write-proxy for ClickUp (X-T3, X-OQ1\n // resolved: per-integration tool). Registered ONLY when the shipped\n // `noir-clickup` declaration is discovered AND the EFFECTIVE runtime resolves\n // to `gated-write-proxy`. The effective runtime is the user's\n // `integrations.clickup.runtime` overlay when set, else the declaration's\n // runtime — so a local downgrade to `runtime:'none'` (a read-only run) takes\n // the write tool off without touching the shipped declaration. The tier\n // model: `none` = skill-side reads only; `integrations_auth` still resolves\n // the token regardless (registered above), so the skill's read flows keep\n // working. The proxy constructs URLs ONLY from op + payload + the workspace\n // binding (a caller-supplied `url` is ignored) — the prompt-injection\n // defense. The confirm gate is HARD: unless `confirm === true`, NO `fetch`\n // is made; the tool returns a dry-run preview (method/allowlisted URL/\n // redacted headers/body). Executed writes append to `.noir/audit/`\n // (X-OQ2: REUSE the S4 dir).\n const clickupBinding = findBinding(integrations, 'noir-clickup');\n if (clickupBinding && clickupBinding.effectiveRuntime === 'gated-write-proxy') {\n const binding = clickupBinding;\n const root = integrations.root;\n\n server.registerTool(\n 'noir.clickup_write',\n {\n description:\n 'ClickUp gated-write-proxy: renders a DRY-RUN preview of the exact HTTP request(s) for an op, and ONLY on explicit {confirm:true} executes them server-side with the pk_ token (NO Bearer). Ops: status (PUT /task/{id}), subtask (POST /list/{list_id}/task + optional PUT status), comment (POST /task/{id}/comment), batch (loop POST /list/{list_id}/task, concurrency 4, 429 backoff on X-RateLimit-Reset). URLs are allowlisted — a caller-supplied url is ignored (prompt-injection defense). Executed writes are audited to .noir/audit/.',\n inputSchema: {\n op: z\n .enum([\n 'status',\n 'subtask',\n 'comment',\n 'batch',\n // `task:`-prefixed aliases emitted by the LOCKED noir-clickup\n // SKILL.md (X-T1+T2); normalized to the short form internally.\n 'task:set-status',\n 'task:create-subtask',\n 'task:comment',\n 'task:batch-create',\n ])\n .describe(\n \"Write op. Short form ('status'|'subtask'|'comment'|'batch') or the task:-prefixed form the skill emits.\",\n ),\n // Flat op-specific fields (matches the LOCKED skill's call shape;\n // the X-T3 `payload` nesting is honored by also accepting a payload\n // object — the handler reads both).\n taskId: z.string().optional().describe('status/comment: the ClickUp task id.'),\n status: z\n .string()\n .optional()\n .describe('status: the new status value; subtask: optional new-subtask status.'),\n parentTaskId: z\n .string()\n .optional()\n .describe('subtask: the parent task id (same list).'),\n name: z.string().optional().describe('subtask: the new subtask name.'),\n listId: z\n .string()\n .optional()\n .describe('subtask/batch: overrides integrations.clickup.listId.'),\n teamId: z.string().optional().describe('Optional team-id override (custom-id reads).'),\n commentText: z.string().optional().describe('comment: the comment body.'),\n notifyAll: z.boolean().optional().describe('comment: notify_all flag (default false).'),\n assigneeId: z.number().optional().describe('comment: assignee user id (integer).'),\n tasks: z\n .array(z.record(z.string(), z.unknown()))\n .optional()\n .describe(\n 'batch: normalized tasks array ({name,description?,tags?,assignees?,status?}).',\n ),\n markdown: z\n .string()\n .optional()\n .describe('batch: H2-per-task markdown (## Title \\\\n body \\\\n - tag: x).'),\n payload: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Alternative: a single payload object with any of the fields above.'),\n confirm: z\n .boolean()\n .optional()\n .describe(\n 'HARD confirm gate. Unless true, the tool is DRY-RUN (no fetch). Default false.',\n ),\n dryRun: z\n .boolean()\n .optional()\n .describe('Force a dry-run preview even if confirm is true (safe default wins).'),\n },\n },\n async (input) => {\n // Merge the flat input fields with the optional `payload` object so\n // both the X-T3 `{op,payload}` shape and the LOCKED skill's flat call\n // shape work. Flat fields take precedence (they're the explicit form).\n const payloadRaw: Record<string, unknown> = {\n ...(input.payload ?? {}),\n ...Object.fromEntries(\n Object.entries(input).filter(\n ([k, v]) =>\n k !== 'op' &&\n k !== 'confirm' &&\n k !== 'dryRun' &&\n k !== 'payload' &&\n v !== undefined,\n ),\n ),\n };\n\n // 1. Build the request(s) (dry-run computation; NO network). This\n // validates the op + payload against the allowlist + id charset,\n // throwing InvalidOp on a contract violation.\n let built: ReturnType<typeof buildRequests>;\n try {\n built = buildRequests(input.op, payloadRaw, {\n ...(binding.teamId !== undefined ? { teamId: binding.teamId } : {}),\n ...(binding.listId !== undefined ? { listId: binding.listId } : {}),\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return textResult({ ok: false, reason: 'invalid-op', op: input.op, error: message });\n }\n\n // 2. Resolve the mode. `confirm:true` AND NOT `dryRun:true` ⇒ execute.\n // Everything else ⇒ dry-run (the SAFE default; never execute\n // without explicit confirm).\n const execute = input.confirm === true && input.dryRun !== true;\n\n if (!execute) {\n // Dry-run: NO token resolution, NO fetch. Return the preview.\n return textResult({\n ok: true,\n mode: 'dry-run',\n op: built.op,\n integration: 'noir-clickup',\n preview: previewRows(built.requests),\n note: 'Pass {confirm:true} to execute. No network call was made.',\n });\n }\n\n // 3. Execute path: resolve the token at CALL TIME. No token ⇒ refuse,\n // no fetch (graceful; the skill does manual-paste fallback).\n const resolution = resolveToken(integrations, { integration: 'noir-clickup' });\n if (!resolution.ok) {\n return textResult({\n ok: false,\n reason: resolution.reason,\n // `envVar` is present on the no-token branch; the unknown-integration\n // branch carries `integration` instead. Spread conditionally so the\n // union narrows cleanly.\n ...('envVar' in resolution ? { envVar: resolution.envVar } : {}),\n op: built.op,\n error: 'no token resolved; set the env var or use the manual-paste fallback',\n });\n }\n const token = resolution.token;\n\n // 4. Execute via the daemon's global fetch. The token enters ONLY here\n // (as `Authorization: pk_<token>`, NO Bearer).\n const op: ClickUpOp = built.op;\n let results: ExecResult[];\n try {\n results = await executeOp(op, built.requests, token, (url, init) => fetch(url, init));\n } catch (err) {\n // A throw here is unexpected (executeOne catches its own fetch\n // errors); surface it as a degraded envelope, never a crash.\n const message = err instanceof Error ? err.message : String(err);\n return textResult({ ok: false, reason: 'http-error', op, error: message });\n }\n\n // 5. Audit every EXECUTED request to .noir/audit/integration-clickup.jsonl\n // (X-OQ2: REUSE the S4 dir). Best-effort; the result envelope reports\n // `audited:false` if the write failed.\n let audited = true;\n for (const r of results) {\n const entry: IntegrationAuditEntry = {\n kind: 'integration',\n integration: 'noir-clickup',\n op: normalizeOp(input.op),\n target: r.target,\n method: r.method,\n httpStatus: r.httpStatus,\n success: r.success,\n timestamp: Date.now(),\n ...(r.rateLimitedWaitMs !== undefined\n ? { rateLimitedWaitMs: r.rateLimitedWaitMs }\n : {}),\n ...(r.error !== undefined ? { error: r.error } : {}),\n };\n try {\n writeIntegrationAudit(root, 'noir-clickup', entry);\n } catch {\n audited = false;\n }\n }\n\n // 6. Return the executed result. The token is NEVER included. The\n // overall `ok` reflects whether every request succeeded; per-request\n // detail is in `results[]`.\n const allOk = results.length > 0 && results.every((r) => r.success);\n return textResult({\n ok: allOk,\n mode: 'executed',\n op,\n integration: 'noir-clickup',\n results,\n audited,\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 { buildIntegrationService } from './integration-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 // Slice X — integration service (X-T3). Built once per serve lifecycle (no\n // store dependency) so `integrations_auth` works even under a read-only store.\n const integrations = buildIntegrationService(ctx.project.root, ctx.project.config.integrations);\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 ...(integrations ? { integrations } : {}),\n });\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":["mkdirSync","join","NOIR_VERSION","buildStatus","NOIR_VERSION","buildStatus","paths","paths","createEmbedFn","resolveEmbedderConfig","resolveMemoryConfig","resolveModelConfig","resolveEmbedderConfig","createEmbedFn","resolveModelConfig","resolveMemoryConfig"]}
|
|
1
|
+
{"version":3,"sources":["../src/clickup-write.ts","../src/context-seam.ts","../src/http.ts","../src/integration-seam.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":["// `noir.clickup_write` gated-write-proxy. The daemon-side\n// companion to the `noir-clickup` skill's write flows (2/3/4/5). SECURITY-CRITICAL.\n//\n// The proxy owns EXACTLY four responsibilities, and ONLY these:\n// 1. ALLOWLISTED URL construction — URLs are built from `op` + `payload` + the\n// workspace binding (`teamId`/`listId`). A caller-supplied `url` is NEVER\n// honored; ClickUp ids are validated against a strict charset so a task id\n// can't smuggle a path segment. This is the prompt-injection defense: a\n// malicious task field can become JSON DATA in a POST body, but it can NEVER\n// redirect the proxy to a different endpoint.\n// 2. DRY-RUN → CONFIRM gate — unless `confirm === true`, the proxy computes the\n// exact would-be request(s) and returns a PREVIEW without any network call.\n// `fetch` is not even touched. Only an explicit `confirm:true` (surfaced to\n// the host's tool-approval gate) reaches the network.\n// 3. EXECUTE — on confirm, POST/PUT to ClickUp API v2 with the `pk_<token>`\n// auth header (NO Bearer), concurrency cap 4 on batch, 429 backoff keyed on\n// `X-RateLimit-Reset` (await until reset, retry once — never blind-retry).\n// 4. AUDIT — every EXECUTED write appends a record to `.noir/audit/` via the\n// seam's `writeIntegrationAudit`. Dry-runs are NOT audited.\n//\n// The token is resolved by the caller (the tool handler, via `resolveToken`) and\n// passed in ONLY for the execute path; the dry-run path never sees it. The token\n// NEVER appears in previews (redacted `pk_***`), results, or audit entries.\n//\n// Doctrine: graceful degradation (no-token ⇒ refuse, no fetch; no-config ⇒\n// refuse; never a crash); NO silent writes (confirm gate is HARD); allowlist is\n// the only source of URLs.\n\n// ClickUp API v2 base. Constant — the proxy never accepts a base URL from input.\nconst CLICKUP_BASE = 'https://api.clickup.com/api/v2';\n\n/** Strict charset for ClickUp ids (task/list/space/team). ClickUp task ids are\n * alphanumeric (`abc123`) or numeric; custom ids look like `CU-42`. Allow\n * letters, digits, underscore, hyphen only — rejects `/`, `?`, `#`, spaces, and\n * anything else that could mutate the path/query. */\nconst ID_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nfunction assertId(value: string, field: string): string {\n if (typeof value !== 'string' || !ID_PATTERN.test(value)) {\n throw new InvalidOp(`invalid ${field}: ${JSON.stringify(value)} (must match ${ID_PATTERN})`);\n }\n return value;\n}\n\n/** Op-shaped error thrown when input violates the contract (bad id, missing\n * required field, unknown op). The handler maps it to `{ok:false, reason:'invalid-op'}`. */\nexport class InvalidOp extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidOp';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Op vocabulary (spec resolution). The proxy accepts BOTH the short verbs\n// (`status` | `subtask` | `comment` | `batch`) from the spec AND the\n// `task:`-prefixed verbs (`task:set-status` | `task:create-subtask` |\n// `task:comment` | `task:batch-create`) that the LOCKED `noir-clickup` SKILL.md\n// documents. The skill is locked (cannot be edited), so the tool MUST\n// honor the verbs the skill emits; the short forms are accepted as aliases for\n// ergonomics + spec alignment. Normalized to the short form internally.\n// ---------------------------------------------------------------------------\nconst OP_ALIASES: Record<string, string> = {\n 'task:set-status': 'status',\n 'task:create-subtask': 'subtask',\n 'task:comment': 'comment',\n 'task:batch-create': 'batch',\n};\nexport type ClickUpOp = 'status' | 'subtask' | 'comment' | 'batch';\nexport function normalizeOp(op: string): ClickUpOp {\n const resolved = OP_ALIASES[op] ?? op;\n if (\n resolved !== 'status' &&\n resolved !== 'subtask' &&\n resolved !== 'comment' &&\n resolved !== 'batch'\n ) {\n throw new InvalidOp(`unknown op: ${JSON.stringify(op)}`);\n }\n return resolved;\n}\n\n/** Workspace binding the proxy resolves listId/teamId from (config > payload). */\nexport interface ClickUpBinding {\n teamId?: string;\n listId?: string;\n}\n\n/** A constructed HTTP request against an allowlisted endpoint. The auth header\n * is ABSENT here — it is added at execute time only; the dry-run preview shows\n * a redacted placeholder so the token never leaves the execute path. */\nexport interface BuiltRequest {\n method: 'GET' | 'POST' | 'PUT';\n /** Full allowlisted URL (constructed from op + payload + binding). */\n url: string;\n /** JSON body (already stringified-safe as an object). */\n body?: Record<string, unknown>;\n /** Stable descriptor for the audit `target` field (e.g. `task/abc123`). */\n target: string;\n}\n\n/** A single preview/result row. */\nexport interface RequestRow {\n method: string;\n url: string;\n body?: Record<string, unknown>;\n /** Redacted auth header for the preview (always `pk_***`). */\n auth: 'pk_***';\n target: string;\n}\n\n/** Resolved ClickUp workspace binding (config > payload). */\nfunction resolveBinding(payload: Record<string, unknown>, binding: ClickUpBinding): ClickUpBinding {\n const listId =\n (typeof payload.listId === 'string' ? payload.listId : undefined) ?? binding.listId;\n const teamId =\n (typeof payload.teamId === 'string' ? payload.teamId : undefined) ?? binding.teamId;\n const resolved: ClickUpBinding = {};\n if (listId !== undefined) resolved.listId = assertId(listId, 'listId');\n if (teamId !== undefined) resolved.teamId = assertId(teamId, 'teamId');\n return resolved;\n}\n\n// ---------------------------------------------------------------------------\n// Request builders — one per op. Each returns the request(s) the op renders.\n// URLs come ONLY from these templates; a caller-supplied `url` is ignored.\n// ---------------------------------------------------------------------------\nfunction buildStatus(payload: Record<string, unknown>): BuiltRequest {\n const taskId = assertId(String(payload.taskId ?? ''), 'taskId');\n const status = payload.status;\n if (typeof status !== 'string' || status.length === 0) {\n throw new InvalidOp('status op requires payload.status (non-empty string)');\n }\n return {\n method: 'PUT',\n url: `${CLICKUP_BASE}/task/${taskId}`,\n body: { status },\n target: `task/${taskId}`,\n };\n}\n\nfunction buildSubtask(payload: Record<string, unknown>, binding: ClickUpBinding): BuiltRequest[] {\n const resolved = resolveBinding(payload, binding);\n if (!resolved.listId)\n throw new InvalidOp(\n 'subtask op requires a listId (payload.listId or integrations.clickup.listId)',\n );\n const parentTaskId = assertId(String(payload.parentTaskId ?? ''), 'parentTaskId');\n const name = payload.name;\n if (typeof name !== 'string' || name.length === 0) {\n throw new InvalidOp('subtask op requires payload.name (non-empty string)');\n }\n const create: BuiltRequest = {\n method: 'POST',\n url: `${CLICKUP_BASE}/list/${resolved.listId}/task`,\n body: { name, parent: parentTaskId },\n target: `list/${resolved.listId}/task (parent ${parentTaskId})`,\n };\n const reqs = [create];\n // Optional follow-up: set the new subtask's status once it exists. The taskId\n // is the ClickUp-returned id; the proxy fills it from the create response at\n // execute time. At dry-run time, the preview shows the would-be request with\n // a placeholder id + a `__dependsOn` marker so the host sees the dependency.\n const status =\n typeof payload.status === 'string' && payload.status.length > 0 ? payload.status : undefined;\n if (status !== undefined) {\n reqs.push({\n method: 'PUT',\n url: `${CLICKUP_BASE}/task/__sub_id__`,\n body: { status },\n target: `task/<new subtask> (status)`,\n });\n }\n return reqs;\n}\n\nfunction buildComment(payload: Record<string, unknown>): BuiltRequest {\n const taskId = assertId(String(payload.taskId ?? ''), 'taskId');\n const commentText = payload.commentText;\n if (typeof commentText !== 'string' || commentText.length === 0) {\n throw new InvalidOp('comment op requires payload.commentText (non-empty string)');\n }\n const notifyAll = typeof payload.notifyAll === 'boolean' ? payload.notifyAll : false;\n const body: Record<string, unknown> = { comment_text: commentText, notify_all: notifyAll };\n // assignee is the ClickUp user id (integer) and OPTIONAL.\n if (payload.assigneeId !== undefined && payload.assigneeId !== null) {\n const assignee =\n typeof payload.assigneeId === 'number' ? payload.assigneeId : Number(payload.assigneeId);\n if (!Number.isFinite(assignee))\n throw new InvalidOp('comment op payload.assigneeId must be a number');\n body.assignee = assignee;\n }\n return {\n method: 'POST',\n url: `${CLICKUP_BASE}/task/${taskId}/comment`,\n body,\n target: `task/${taskId}/comment`,\n };\n}\n\n/** A normalized task for the batch op (H2 markdown + the `tasks[]` array both\n * reduce to this). All fields are JSON DATA in the POST body — adversary text\n * in a task title becomes the `name` string, never an executable instruction. */\nexport interface NormalizedTask {\n name: string;\n description?: string;\n tags?: string[];\n assignees?: number[];\n status?: string;\n}\n\n/** Parse H2-per-task markdown into normalized tasks. Each `## <title>` opens a\n * task; body lines until the next `## ` are the description, EXCEPT lines that\n * match `- key: value` which are folded into structured fields (assignees/tags).\n * Tolerant: a missing leading H2 still yields the body as one task (named from\n * the first non-empty line) so a slightly-malformed template still previews. */\nexport function parseH2Tasks(markdown: string): NormalizedTask[] {\n const lines = markdown.split(/\\r?\\n/);\n const tasks: NormalizedTask[] = [];\n let cur: NormalizedTask | null = null;\n const desc: string[] = [];\n for (const raw of lines) {\n const line = raw;\n const h2 = /^##\\s+(.+?)\\s*$/.exec(line);\n if (h2) {\n const title = h2[1];\n if (title !== undefined) {\n if (cur) cur.description = desc.join('\\n').trim() || undefined;\n cur = { name: title.trim() };\n desc.length = 0;\n tasks.push(cur);\n continue;\n }\n }\n if (!cur) {\n // Pre-H2 content: skip bare blank lines; a stray line becomes a task so\n // the template still previews (graceful, not a crash).\n if (line.trim().length === 0) continue;\n cur = { name: line.trim() };\n desc.length = 0;\n tasks.push(cur);\n continue;\n }\n const meta = /^-\\s+([A-Za-z_]+)\\s*:\\s*(.+?)\\s*$/.exec(line);\n if (meta) {\n const keyRaw = meta[1];\n const valRaw = meta[2];\n if (keyRaw !== undefined && valRaw !== undefined) {\n const key = keyRaw.toLowerCase();\n const val = valRaw;\n if (key === 'tag' || key === 'tags') {\n cur.tags = [\n ...(cur.tags ?? []),\n ...val\n .split(',')\n .map((t) => t.trim())\n .filter((t) => t.length > 0),\n ];\n } else if (key === 'assignee' || key === 'assignees') {\n const ids = val\n .split(',')\n .map((t) => t.trim())\n .map((t) => Number(t))\n .filter((n) => Number.isFinite(n));\n cur.assignees = [...(cur.assignees ?? []), ...ids];\n } else if (key === 'status') {\n cur.status = val;\n } else {\n // Unknown meta key: keep as a description line (don't lose information).\n desc.push(line);\n }\n } else {\n desc.push(line);\n }\n } else {\n desc.push(line);\n }\n }\n if (cur) cur.description = desc.join('\\n').trim() || undefined;\n // Drop tasks that ended up with an empty name (e.g. a trailing `## ` with no title).\n return tasks.filter((t) => t.name.length > 0);\n}\n\nfunction normalizeBatchInput(payload: Record<string, unknown>): NormalizedTask[] {\n if (Array.isArray(payload.tasks)) {\n return payload.tasks.map((t, i) => {\n if (typeof t !== 'object' || t === null)\n throw new InvalidOp(`batch payload.tasks[${i}] must be an object`);\n const obj = t as Record<string, unknown>;\n const name = obj.name;\n if (typeof name !== 'string' || name.length === 0)\n throw new InvalidOp(`batch payload.tasks[${i}].name required`);\n const out: NormalizedTask = { name };\n if (typeof obj.description === 'string') out.description = obj.description;\n if (Array.isArray(obj.tags)) out.tags = obj.tags.map((s) => String(s));\n if (Array.isArray(obj.assignees))\n out.assignees = obj.assignees.map((n) => Number(n)).filter((n) => Number.isFinite(n));\n if (typeof obj.status === 'string') out.status = obj.status;\n return out;\n });\n }\n if (typeof payload.markdown === 'string') {\n return parseH2Tasks(payload.markdown);\n }\n throw new InvalidOp('batch op requires payload.tasks[] OR payload.markdown (H2-per-task)');\n}\n\nfunction buildBatch(payload: Record<string, unknown>, binding: ClickUpBinding): BuiltRequest[] {\n const resolved = resolveBinding(payload, binding);\n if (!resolved.listId)\n throw new InvalidOp(\n 'batch op requires a listId (payload.listId or integrations.clickup.listId)',\n );\n const tasks = normalizeBatchInput(payload);\n if (tasks.length === 0)\n throw new InvalidOp('batch op yielded no tasks (empty markdown / tasks[])');\n return tasks.map((t, i) => {\n const body: Record<string, unknown> = { name: t.name };\n if (t.description !== undefined) body.description = t.description;\n if (t.tags !== undefined && t.tags.length > 0) body.tags = t.tags;\n // ClickUp `POST /list/{list_id}/task` takes the PLURAL `assignees: number[]`\n // (see references/clickup-api.md). The singular `assignee` key is silently\n // ignored → tasks created with NO assignees while audit reports success\n // (silent data loss). Only the comment endpoint uses singular `assignee`.\n if (t.assignees !== undefined && t.assignees.length > 0) body.assignees = t.assignees;\n return {\n method: 'POST' as const,\n url: `${CLICKUP_BASE}/list/${resolved.listId}/task`,\n body,\n target: `list/${resolved.listId}/task (#${i + 1} ${t.name.slice(0, 40)})`,\n };\n });\n}\n\n/** Build ALL requests an op renders, WITHOUT touching the network. Used by both\n * the dry-run preview (no auth) and the execute path (auth added). Throws\n * `InvalidOp` on a contract violation (bad id, missing field, unknown op). */\nexport function buildRequests(\n opRaw: string,\n payload: Record<string, unknown>,\n binding: ClickUpBinding,\n): { op: ClickUpOp; requests: BuiltRequest[] } {\n const op = normalizeOp(opRaw);\n switch (op) {\n case 'status':\n return { op, requests: [buildStatus(payload)] };\n case 'subtask':\n return { op, requests: buildSubtask(payload, binding) };\n case 'comment':\n return { op, requests: [buildComment(payload)] };\n case 'batch':\n return { op, requests: buildBatch(payload, binding) };\n }\n}\n\n/** Render a dry-run preview row set. NO auth header, NO network — the token\n * never enters this path. */\nexport function previewRows(requests: BuiltRequest[]): RequestRow[] {\n return requests.map((r) => ({\n method: r.method,\n url: r.url,\n ...(r.body !== undefined ? { body: r.body } : {}),\n auth: 'pk_***',\n target: r.target,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Execute path. Reached ONLY when `confirm === true`. The token is added here\n// (and ONLY here) as `Authorization: pk_<token>` (NO Bearer — ClickUp v2 rejects\n// Bearer). 429 backoff reads `X-RateLimit-Reset` and awaits until reset, then\n// retries ONCE per request.\n// ---------------------------------------------------------------------------\n\n/** Minimal fetch shape the proxy needs. Bound to global `fetch` in production;\n * a spy in tests (cassette). */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface ExecResult {\n target: string;\n method: string;\n url: string;\n httpStatus: number;\n success: boolean;\n /** ClickUp's parsed JSON body (or `{}` when absent). NEVER contains the token\n * (ClickUp never echoes it). */\n response: Record<string, unknown>;\n /** Present when a 429 backoff fired on this request. */\n rateLimitedWaitMs?: number;\n /** For `subtask` with a status follow-up: the new subtask id resolved from the\n * create response so the host can reference it. */\n newTaskId?: string;\n /** Error message on a non-2xx / network failure (NEVER includes the token). */\n error?: string;\n}\n\n/** Build the outbound auth+content-type headers. The token is added here and\n * ONLY here (as `Authorization: pk_<token>`, NO Bearer — ClickUp v2 rejects\n * Bearer). Despite the historical name, this function does NOT redact — it\n * attaches the RAW token; the dry-run preview never calls it (previews show\n * the redacted `pk_***` placeholder instead), so the live token only travels\n * on the execute path's outbound request. */\nfunction authHeaders(token: string): Record<string, string> {\n return {\n 'content-type': 'application/json',\n authorization: `pk_${token}`,\n };\n}\n\n/** Read `X-RateLimit-Reset` (epoch seconds) and compute the wait until reset.\n * Caps at a sane maximum so a malformed far-future header can't pin the daemon. */\nfunction rateLimitWaitMs(res: Response): number | undefined {\n const reset = res.headers.get('X-RateLimit-Reset');\n if (reset === null) return undefined;\n const epochSec = Number(reset);\n if (!Number.isFinite(epochSec)) return undefined;\n const waitMs = epochSec * 1000 - Date.now();\n // Clamp to [0, 60s]. A negative (past reset) → 0 (retry now). A huge value\n // (malformed) → 60s (don't pin the daemon on a bad header).\n if (waitMs <= 0) return 0;\n if (waitMs > 60_000) return 60_000;\n return waitMs;\n}\n\nasync function sleep(ms: number): Promise<void> {\n if (ms <= 0) return;\n await new Promise<void>((r) => setTimeout(r, ms));\n}\n\n/** Execute a single request with one 429-bounded retry. */\nasync function executeOne(\n req: BuiltRequest,\n token: string,\n fetchImpl: FetchLike,\n): Promise<ExecResult> {\n const init: RequestInit = {\n method: req.method,\n headers: authHeaders(token),\n ...(req.body !== undefined ? { body: JSON.stringify(req.body) } : {}),\n };\n let res: Response;\n try {\n res = await fetchImpl(req.url, init);\n } catch (err) {\n return {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: 0,\n success: false,\n response: {},\n error: err instanceof Error ? err.message : String(err),\n };\n }\n // 429: honor the header literally, await until reset, retry ONCE.\n if (res.status === 429) {\n const waitMs = rateLimitWaitMs(res) ?? 1000;\n await sleep(waitMs);\n try {\n res = await fetchImpl(req.url, init);\n } catch (err) {\n return {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: 429,\n success: false,\n response: {},\n rateLimitedWaitMs: waitMs,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n const final = await toExecResult(req, res);\n return { ...final, rateLimitedWaitMs: waitMs };\n }\n return toExecResult(req, res);\n}\n\nasync function toExecResult(req: BuiltRequest, res: Response): Promise<ExecResult> {\n const success = res.status >= 200 && res.status < 300;\n let parsed: Record<string, unknown> = {};\n try {\n parsed = (await res.json()) as Record<string, unknown>;\n } catch {\n try {\n const text = await res.text();\n parsed = text.length > 0 ? { _raw: text } : {};\n } catch {\n parsed = {};\n }\n }\n const result: ExecResult = {\n target: req.target,\n method: req.method,\n url: req.url,\n httpStatus: res.status,\n success,\n response: parsed,\n };\n if (!success) {\n const errVal = parsed.err;\n result.error = typeof errVal === 'string' ? errVal : `HTTP ${res.status}`;\n }\n return result;\n}\n\n/** Concurrency-limited map (cap 4 per spec: 4-8, conservative). Each item gets\n * at most one in-flight request; the 429 retry is awaited within its slot. */\nasync function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let cursor = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n while (true) {\n const i = cursor++;\n if (i >= items.length) break;\n const item = items[i];\n // `i` is bounded by items.length above; the indexed access is safe.\n if (item !== undefined) results[i] = await fn(item, i);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\n/** Execute an op's requests against ClickUp. The `subtask` op is special: its\n * optional status follow-up needs the create response's `id`, so the two\n * requests run sequentially with the id threaded through. `batch` runs with a\n * concurrency cap of 4 + per-request 429 backoff. */\nexport async function executeOp(\n op: ClickUpOp,\n requests: BuiltRequest[],\n token: string,\n fetchImpl: FetchLike,\n): Promise<ExecResult[]> {\n if (op === 'subtask' && requests.length === 2) {\n const create = requests[0];\n const statusReq = requests[1];\n if (!create || !statusReq) {\n // Defensive: length === 2 guard above makes this unreachable, but\n // noUncheckedIndexedAccess forces the check.\n throw new InvalidOp('subtask op expects exactly two rendered requests');\n }\n const createRes = await executeOne(create, token, fetchImpl);\n if (!createRes.success) return [createRes];\n const newId = typeof createRes.response.id === 'string' ? createRes.response.id : undefined;\n if (!newId) {\n // Create succeeded but no id came back — can't set status. Surface the\n // partial success; the host sees the create worked, the status didn't.\n return [\n createRes,\n {\n target: statusReq.target,\n method: statusReq.method,\n url: statusReq.url,\n httpStatus: 0,\n success: false,\n response: {},\n error: 'subtask created but no id returned; status follow-up skipped',\n },\n ];\n }\n createRes.newTaskId = newId;\n // The response-derived id is substituted into the allowlisted URL template\n // — validate it against the SAME charset as every caller-supplied id so a\n // compromised/buggy ClickUp response can't smuggle a path segment (the\n // module's stated invariant: every id is charset-validated before use in a URL).\n assertId(newId, 'subtask-create-response.id');\n // Rewrite the placeholder URL with the real id and execute.\n const realStatusReq: BuiltRequest = {\n method: statusReq.method,\n url: statusReq.url.replace('__sub_id__', newId),\n target: `task/${newId} (status)`,\n ...(statusReq.body !== undefined ? { body: statusReq.body } : {}),\n };\n const statusRes = await executeOne(realStatusReq, token, fetchImpl);\n return [createRes, statusRes];\n }\n if (op === 'batch') {\n return mapWithConcurrency(requests, 4, (req) => executeOne(req, token, fetchImpl));\n }\n // status / comment / (subtask without status): single request, executed in order.\n const out: ExecResult[] = [];\n for (const req of requests) {\n out.push(await executeOne(req, token, fetchImpl));\n }\n return out;\n}\n","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 { buildIntegrationService } from './integration-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 — 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 // Integration service. Built once per serve lifecycle from\n // the discovered declarations + the user's `integrations:` config overlay.\n // Discovery is best-effort; an empty service still registers\n // `integrations_auth` (env-var resolution needs no declaration) and simply\n // skips `noir.clickup_write`. Built unconditionally (no store dependency) so\n // `integrations_auth` works even under a read-only (daemon-down) store.\n const integrations = buildIntegrationService(opts.project.root, opts.project.config.integrations);\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 ...(integrations ? { integrations } : {}),\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","// Integration runtime seam. The daemon-side companion to the\n// shipped `integration.json` declarations + the core `integrations` config\n// block. This module is deliberately split into THREE concerns so the\n// security-critical paths can be unit-tested in isolation:\n//\n// 1. `IntegrationService` — the per-serve-lifecycle handle built ONCE from the\n// discovered declarations (@noir-ai/skills `discoverIntegrations`) and held\n// on `ctx.integrations` (mirrors `ctx.store` / `ctx.engine` / `ctx.memory`).\n// Discovery is best-effort: a missing/unreadable pack degrades to an empty\n// service (the `integrations_auth` tool still resolves a caller-supplied\n// `envVar`; `noir.clickup_write` is simply not registered).\n// 2. `resolveToken` — the ONLY place a token value touches process.env. Read\n// at call time, returned to the calling agent, NEVER logged/echoed to\n// stderr/audit bodies (the token travels only in the tool RESULT to the\n// trusted host + the outbound `Authorization` header).\n// 3. `writeIntegrationAudit` — append-only JSONL into the SAME `.noir/audit/`\n// dir as the gate export (X-OQ2 resolved: REUSE, do not invent a new\n// audit location). One executed write ⇒ one line; dry-runs are NOT audited.\n//\n// Doctrine: graceful degradation (no-token ⇒ manual-paste / no-token refuse,\n// never a crash); `.noir/` SOT (audit there); NO silent writes (the confirm gate\n// lives in the proxy module + the tool handler, never here).\n\nimport { appendFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { paths } from '@noir-ai/core';\nimport type { IntegrationDeclaration } from '@noir-ai/skills';\nimport { discoverIntegrations } from '@noir-ai/skills';\n\n/**\n * Per-integration binding resolved at the seam: the shipped declaration (source\n * of truth for `auth.tokenEnv` + `runtime`) merged with the user's\n * `integrations.<name>` config overlay (source of truth for `teamId`/`listId`/\n * `spaceId` + a local `auth.tokenEnv` override / `runtime` downgrade). The\n * declaration is authoritative for `tokenEnv` unless the config explicitly\n * overrides it (a workspace that renamed the env var).\n */\nexport interface IntegrationBinding {\n /** Full declaration name, e.g. `'noir-clickup'`. */\n name: string;\n /** Short name — declaration name with the `noir-` namespace stripped\n * (`'clickup'`). The `noir.clickup_write` tool segment + the documented\n * `integrations.clickup.*` config key both use this form. */\n shortName: string;\n declaration: IntegrationDeclaration;\n /** Resolved token env-var name (config override > declaration). */\n tokenEnv: string;\n /** Effective runtime tier: the user's `integrations.<name>.runtime` overlay\n * wins when set, else the declaration's `runtime`. The daemon gates write-tool\n * registration on THIS (not the declaration's) so a local downgrade to `none`\n * actually takes the write tool off — the tier model: `none` = skill-side\n * reads only, `integrations_auth` still resolves the token. */\n effectiveRuntime: IntegrationDeclaration['runtime'];\n teamId?: string;\n listId?: string;\n spaceId?: string;\n}\n\n/**\n * The per-serve-lifecycle integration handle. Built once (mirrors the store +\n * workflow + memory engines) and reused across every MCP request. Carries the\n * discovered declarations merged with the user config so the tools never re-read\n * the filesystem per call.\n */\nexport interface IntegrationService {\n /** Project root — the anchor for `.noir/audit/` writes. */\n root: string;\n /** Bindings keyed by BOTH the full name (`noir-clickup`) and the short name\n * (`clickup`) so a tool/config using either form resolves the same binding. */\n bindings: Map<string, IntegrationBinding>;\n}\n\n/** Strip the `noir-` namespace prefix from a declaration name. The short form\n * is what the `noir.<short>_write` tool segment + the `integrations.<short>`\n * config key use; the declaration itself always carries the full `noir-` name. */\nexport function shortNameOf(name: string): string {\n return name.replace(/^noir-/, '');\n}\n\n/**\n * Build the integration service for one serve lifecycle. Discovery is\n * best-effort: a missing/unreadable skills pack ⇒ an empty service (the tools\n * degrade gracefully — `integrations_auth` still resolves a caller `envVar`;\n * `noir.clickup_write` is simply not registered). Never throws.\n *\n * @param root project root (`.noir/audit/` anchor)\n * @param configIntegrations the parsed `integrations` block from `.noir/config.yml`\n * (`{ [name]: { auth?, runtime, teamId?, listId?, spaceId? } }`). Keys may use\n * either the full `noir-clickup` or the short `clickup` form.\n */\nexport function buildIntegrationService(\n root: string,\n configIntegrations: Record<\n string,\n {\n auth?: { tokenEnv?: string };\n runtime?: 'none' | 'gated-write-proxy' | 'mcp-stdio' | 'external-mcp';\n teamId?: string;\n listId?: string;\n spaceId?: string;\n }\n > = {},\n): IntegrationService {\n let discovered: IntegrationDeclaration[] = [];\n try {\n discovered = discoverIntegrations().map(\n (s: { declaration: IntegrationDeclaration }) => s.declaration,\n );\n } catch {\n // A malformed shipped declaration would have failed the skills pack's own\n // tests; if we still hit this, degrade to \"no integrations wired\" rather\n // than crashing the daemon.\n discovered = [];\n }\n const bindings = new Map<string, IntegrationBinding>();\n for (const declaration of discovered) {\n // Config lookup tolerates both `noir-clickup` and `clickup` keys (the docs\n // use the short form; the full form is accepted for symmetry).\n const cfg =\n configIntegrations[declaration.name] ?? configIntegrations[shortNameOf(declaration.name)];\n const tokenEnv = cfg?.auth?.tokenEnv ?? declaration.auth.tokenEnv;\n // Effective runtime: the user's config overlay wins when set; otherwise the\n // declaration's tier. This is what the daemon must gate write-tool\n // registration on (NOT the declaration's runtime) — a local downgrade to\n // `none` for a read-only run takes the write tool off without touching the\n // shipped declaration.\n const effectiveRuntime = cfg?.runtime ?? declaration.runtime;\n const binding: IntegrationBinding = {\n name: declaration.name,\n shortName: shortNameOf(declaration.name),\n declaration,\n tokenEnv,\n effectiveRuntime,\n ...(cfg?.teamId !== undefined ? { teamId: cfg.teamId } : {}),\n ...(cfg?.listId !== undefined ? { listId: cfg.listId } : {}),\n ...(cfg?.spaceId !== undefined ? { spaceId: cfg.spaceId } : {}),\n };\n bindings.set(binding.name, binding);\n bindings.set(binding.shortName, binding);\n }\n return { root, bindings };\n}\n\n/** Look up a binding by either the full or short name. */\nexport function findBinding(svc: IntegrationService, name: string): IntegrationBinding | undefined {\n return svc.bindings.get(name);\n}\n\n/** Result of a token resolution attempt. */\nexport type TokenResolution =\n | { ok: true; token: string; envVar: string }\n | { ok: false; reason: 'no-token'; envVar: string }\n | { ok: false; reason: 'unknown-integration'; integration: string };\n\n/**\n * Resolve an integration token VALUE from `process.env` at CALL TIME (never at\n * load time; never cached beyond the call). The token is returned to the calling\n * agent in the tool result (trusted host) + travels in the outbound\n * `Authorization` header — and NOWHERE else (no stderr, no audit body).\n *\n * Two call shapes:\n * - `{ integration: 'noir-clickup' }` → look up `tokenEnv` from the discovered\n * declaration (config override honored), then read `process.env[tokenEnv]`.\n * - `{ envVar: 'CLICKUP_API_TOKEN' }` → read `process.env[envVar]` directly.\n */\nexport function resolveToken(\n svc: IntegrationService,\n opts: { integration?: string; envVar?: string },\n env: NodeJS.ProcessEnv = process.env,\n): TokenResolution {\n const integration = opts.integration;\n const envVar = opts.envVar;\n if (integration !== undefined) {\n const binding = findBinding(svc, integration);\n if (!binding) return { ok: false, reason: 'unknown-integration', integration };\n const name = binding.tokenEnv;\n const value = env[name];\n if (typeof value === 'string' && value.length > 0) {\n return { ok: true, token: value, envVar: name };\n }\n return { ok: false, reason: 'no-token', envVar: name };\n }\n if (envVar !== undefined) {\n const value = env[envVar];\n if (typeof value === 'string' && value.length > 0) {\n return { ok: true, token: value, envVar };\n }\n return { ok: false, reason: 'no-token', envVar };\n }\n // Neither integration nor envVar supplied: synthesize a clear refusal rather\n // than a crash. The skill drives the manual-paste fallback from this.\n return { ok: false, reason: 'no-token', envVar: '' };\n}\n\n/** One executed-write audit record. Append-only JSONL — one line\n * per executed write. Dry-runs are NOT audited (no write happened). */\nexport interface IntegrationAuditEntry {\n /** Fixed discriminator so a future `.noir/audit/` reader can filter mixed\n * gate (`kind:'gate'`-ish) + integration rows. */\n kind: 'integration';\n /** Declaration name, e.g. `'noir-clickup'`. */\n integration: string;\n /** Op verb (normalized short form): `status` | `subtask` | `comment` | `batch`. */\n op: string;\n /** Stable target descriptor (e.g. `'task/abc123'`, `'list/90125/task'`). */\n target: string;\n /** HTTP method on the allowlisted endpoint. */\n method: string;\n /** HTTP status returned by ClickUp (last attempt after 429 backoff). */\n httpStatus: number;\n /** True iff `httpStatus` is 2xx. */\n success: boolean;\n /** Epoch-millis when the write was attempted. */\n timestamp: number;\n /** Present when a 429 backoff fired (the spec calls for recording the wait). */\n rateLimitedWaitMs?: number;\n /** Error message when `success === false` (NEVER includes the token). */\n error?: string;\n}\n\n/**\n * Append one integration audit record to `.noir/audit/integration-<short>.jsonl`\n * (the SAME `.noir/audit/` dir as the gate export — X-OQ2 resolved: REUSE).\n * Append-only JSONL so concurrent/sequential writes don't clobber each other and\n * the file grows linearly with executed writes. Creates the dir if missing.\n *\n * Propagates filesystem errors to the caller — the gated proxy's handler is the\n * best-effort boundary: it catches, sets the result envelope's `audited:false`,\n * and continues (the ClickUp write already happened; an audit failure does NOT\n * roll it back). Keeping the throw honest makes `audited:false` reachable instead\n * of a perpetually-true flag.\n */\nexport function writeIntegrationAudit(\n root: string,\n integrationName: string,\n entry: IntegrationAuditEntry,\n): void {\n // X-OQ2 resolved: REUSE the `.noir/audit/` dir (SOT — no second audit\n // location). Integration writes are append-only + cross-task (not keyed by a\n // Noir task id), so a per-integration JSONL file is the natural shape: one\n // executed write ⇒ one line. The `.jsonl` extension keeps it distinct from\n // the per-task `.json` exports.\n const file = join(paths.auditDir(root), `integration-${shortNameOf(integrationName)}.jsonl`);\n mkdirSync(paths.auditDir(root), { recursive: true });\n appendFileSync(file, `${JSON.stringify(entry)}\\n`, 'utf8');\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 * — `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?.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 (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 (§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 (§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 {\n buildRequests,\n type ClickUpOp,\n type ExecResult,\n executeOp,\n normalizeOp,\n previewRows,\n} from './clickup-write.js';\nimport {\n findBinding,\n type IntegrationAuditEntry,\n type IntegrationService,\n resolveToken,\n writeIntegrationAudit,\n} from './integration-seam.js';\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 / §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 * Optional integration service. Built once per serve\n * lifecycle from the discovered `integration.json` declarations\n * (@noir-ai/skills `discoverIntegrations`) merged with the user's\n * `integrations:<name>` config block. When present, the `integrations_auth`\n * tool is ALWAYS registered (resolves a token by env var at call time — kills\n * the non-interactive-shell gotcha), and `noir.clickup_write` is registered\n * when a `noir-clickup` declaration with `runtime:'gated-write-proxy'` is\n * discovered. Both tools degrade gracefully (`no-token`/`no-config`) when the\n * binding/env is absent — never a crash. Token values NEVER enter stderr or\n * audit bodies (only the tool RESULT to the trusted host + the outbound\n * `Authorization` header).\n */\n integrations?: IntegrationService;\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: 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 / §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 // The integration service is optional: stdio/HTTP inject it\n // once per serve lifecycle (mirrors the store/engine/memory/context handles).\n // `integrations_auth` is ALWAYS registered when the service is present — it\n // resolves an integration token VALUE from process.env at CALL TIME (never at\n // load time; never cached) and returns it to the calling agent. This kills the\n // non-interactive-shell gotcha (the skill + its MCP tool never read shell env\n // directly; only the daemon's process env matters). When the env value is\n // absent, the tool reports `no-token` and the skill falls back to manual paste.\n //\n // SECURITY: the token travels ONLY in the tool RESULT to the trusted host +\n // the outbound `Authorization` header. It is never written to stderr, never\n // persisted, never echoed into an audit body. `noir.clickup_write` (below) is\n // registered only when a `noir-clickup` declaration with\n // `runtime:'gated-write-proxy'` is discovered, and its dry-run path never even\n // resolves the token.\n if (ctx.integrations) {\n const integrations = ctx.integrations;\n\n server.registerTool(\n 'integrations_auth',\n {\n description:\n \"Resolve an integration token VALUE server-side at call time (kills the non-interactive-shell gotcha). Pass {integration:'noir-clickup'} to resolve tokenEnv from the discovered declaration, or {envVar:'CLICKUP_API_TOKEN'} to name the env var directly. Returns {ok:true,token,envVar} when present, or {ok:false,reason:'no-token',envVar} when absent (the skill then does manual-paste fallback). The token is returned ONLY in this tool result — never logged, never persisted.\",\n inputSchema: {\n integration: z\n .string()\n .optional()\n .describe(\n \"Integration name (e.g. 'noir-clickup' or 'clickup'); resolves tokenEnv from the discovered declaration (config override honored).\",\n ),\n envVar: z\n .string()\n .optional()\n .describe(\n 'Env-var name to read directly (e.g. CLICKUP_API_TOKEN). Used when no integration binding applies.',\n ),\n },\n },\n async ({ integration, envVar }) => {\n if (integration === undefined && envVar === undefined) {\n return textResult({\n ok: false,\n reason: 'invalid-input',\n error: 'pass one of {integration} or {envVar}',\n });\n }\n const resolution = resolveToken(integrations, { integration, envVar });\n return textResult(resolution);\n },\n );\n\n // `noir.clickup_write` — the gated-write-proxy for ClickUp. Registered ONLY when the shipped\n // `noir-clickup` declaration is discovered AND the EFFECTIVE runtime resolves\n // to `gated-write-proxy`. The effective runtime is the user's\n // `integrations.clickup.runtime` overlay when set, else the declaration's\n // runtime — so a local downgrade to `runtime:'none'` (a read-only run) takes\n // the write tool off without touching the shipped declaration. The tier\n // model: `none` = skill-side reads only; `integrations_auth` still resolves\n // the token regardless (registered above), so the skill's read flows keep\n // working. The proxy constructs URLs ONLY from op + payload + the workspace\n // binding (a caller-supplied `url` is ignored) — the prompt-injection\n // defense. The confirm gate is HARD: unless `confirm === true`, NO `fetch`\n // is made; the tool returns a dry-run preview (method/allowlisted URL/\n // redacted headers/body). Executed writes append to `.noir/audit/`\n // (X-OQ2: REUSE the dir).\n const clickupBinding = findBinding(integrations, 'noir-clickup');\n if (clickupBinding && clickupBinding.effectiveRuntime === 'gated-write-proxy') {\n const binding = clickupBinding;\n const root = integrations.root;\n\n server.registerTool(\n 'noir.clickup_write',\n {\n description:\n 'ClickUp gated-write-proxy: renders a DRY-RUN preview of the exact HTTP request(s) for an op, and ONLY on explicit {confirm:true} executes them server-side with the pk_ token (NO Bearer). Ops: status (PUT /task/{id}), subtask (POST /list/{list_id}/task + optional PUT status), comment (POST /task/{id}/comment), batch (loop POST /list/{list_id}/task, concurrency 4, 429 backoff on X-RateLimit-Reset). URLs are allowlisted — a caller-supplied url is ignored (prompt-injection defense). Executed writes are audited to .noir/audit/.',\n inputSchema: {\n op: z\n .enum([\n 'status',\n 'subtask',\n 'comment',\n 'batch',\n // `task:`-prefixed aliases emitted by the LOCKED noir-clickup\n // SKILL.md; normalized to the short form internally.\n 'task:set-status',\n 'task:create-subtask',\n 'task:comment',\n 'task:batch-create',\n ])\n .describe(\n \"Write op. Short form ('status'|'subtask'|'comment'|'batch') or the task:-prefixed form the skill emits.\",\n ),\n // Flat op-specific fields (matches the LOCKED skill's call shape;\n // the `payload` nesting is honored by also accepting a payload\n // object — the handler reads both).\n taskId: z.string().optional().describe('status/comment: the ClickUp task id.'),\n status: z\n .string()\n .optional()\n .describe('status: the new status value; subtask: optional new-subtask status.'),\n parentTaskId: z\n .string()\n .optional()\n .describe('subtask: the parent task id (same list).'),\n name: z.string().optional().describe('subtask: the new subtask name.'),\n listId: z\n .string()\n .optional()\n .describe('subtask/batch: overrides integrations.clickup.listId.'),\n teamId: z.string().optional().describe('Optional team-id override (custom-id reads).'),\n commentText: z.string().optional().describe('comment: the comment body.'),\n notifyAll: z.boolean().optional().describe('comment: notify_all flag (default false).'),\n assigneeId: z.number().optional().describe('comment: assignee user id (integer).'),\n tasks: z\n .array(z.record(z.string(), z.unknown()))\n .optional()\n .describe(\n 'batch: normalized tasks array ({name,description?,tags?,assignees?,status?}).',\n ),\n markdown: z\n .string()\n .optional()\n .describe('batch: H2-per-task markdown (## Title \\\\n body \\\\n - tag: x).'),\n payload: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Alternative: a single payload object with any of the fields above.'),\n confirm: z\n .boolean()\n .optional()\n .describe(\n 'HARD confirm gate. Unless true, the tool is DRY-RUN (no fetch). Default false.',\n ),\n dryRun: z\n .boolean()\n .optional()\n .describe('Force a dry-run preview even if confirm is true (safe default wins).'),\n },\n },\n async (input) => {\n // Merge the flat input fields with the optional `payload` object so\n // both the `{op,payload}` shape and the LOCKED skill's flat call\n // shape work. Flat fields take precedence (they're the explicit form).\n const payloadRaw: Record<string, unknown> = {\n ...(input.payload ?? {}),\n ...Object.fromEntries(\n Object.entries(input).filter(\n ([k, v]) =>\n k !== 'op' &&\n k !== 'confirm' &&\n k !== 'dryRun' &&\n k !== 'payload' &&\n v !== undefined,\n ),\n ),\n };\n\n // 1. Build the request(s) (dry-run computation; NO network). This\n // validates the op + payload against the allowlist + id charset,\n // throwing InvalidOp on a contract violation.\n let built: ReturnType<typeof buildRequests>;\n try {\n built = buildRequests(input.op, payloadRaw, {\n ...(binding.teamId !== undefined ? { teamId: binding.teamId } : {}),\n ...(binding.listId !== undefined ? { listId: binding.listId } : {}),\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return textResult({ ok: false, reason: 'invalid-op', op: input.op, error: message });\n }\n\n // 2. Resolve the mode. `confirm:true` AND NOT `dryRun:true` ⇒ execute.\n // Everything else ⇒ dry-run (the SAFE default; never execute\n // without explicit confirm).\n const execute = input.confirm === true && input.dryRun !== true;\n\n if (!execute) {\n // Dry-run: NO token resolution, NO fetch. Return the preview.\n return textResult({\n ok: true,\n mode: 'dry-run',\n op: built.op,\n integration: 'noir-clickup',\n preview: previewRows(built.requests),\n note: 'Pass {confirm:true} to execute. No network call was made.',\n });\n }\n\n // 3. Execute path: resolve the token at CALL TIME. No token ⇒ refuse,\n // no fetch (graceful; the skill does manual-paste fallback).\n const resolution = resolveToken(integrations, { integration: 'noir-clickup' });\n if (!resolution.ok) {\n return textResult({\n ok: false,\n reason: resolution.reason,\n // `envVar` is present on the no-token branch; the unknown-integration\n // branch carries `integration` instead. Spread conditionally so the\n // union narrows cleanly.\n ...('envVar' in resolution ? { envVar: resolution.envVar } : {}),\n op: built.op,\n error: 'no token resolved; set the env var or use the manual-paste fallback',\n });\n }\n const token = resolution.token;\n\n // 4. Execute via the daemon's global fetch. The token enters ONLY here\n // (as `Authorization: pk_<token>`, NO Bearer).\n const op: ClickUpOp = built.op;\n let results: ExecResult[];\n try {\n results = await executeOp(op, built.requests, token, (url, init) => fetch(url, init));\n } catch (err) {\n // A throw here is unexpected (executeOne catches its own fetch\n // errors); surface it as a degraded envelope, never a crash.\n const message = err instanceof Error ? err.message : String(err);\n return textResult({ ok: false, reason: 'http-error', op, error: message });\n }\n\n // 5. Audit every EXECUTED request to .noir/audit/integration-clickup.jsonl\n // (X-OQ2: REUSE the dir). Best-effort; the result envelope reports\n // `audited:false` if the write failed.\n let audited = true;\n for (const r of results) {\n const entry: IntegrationAuditEntry = {\n kind: 'integration',\n integration: 'noir-clickup',\n op: normalizeOp(input.op),\n target: r.target,\n method: r.method,\n httpStatus: r.httpStatus,\n success: r.success,\n timestamp: Date.now(),\n ...(r.rateLimitedWaitMs !== undefined\n ? { rateLimitedWaitMs: r.rateLimitedWaitMs }\n : {}),\n ...(r.error !== undefined ? { error: r.error } : {}),\n };\n try {\n writeIntegrationAudit(root, 'noir-clickup', entry);\n } catch {\n audited = false;\n }\n }\n\n // 6. Return the executed result. The token is NEVER included. The\n // overall `ok` reflects whether every request succeeded; per-request\n // detail is in `results[]`.\n const allOk = results.length > 0 && results.every((r) => r.success);\n return textResult({\n ok: allOk,\n mode: 'executed',\n op,\n integration: 'noir-clickup',\n results,\n audited,\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 { buildIntegrationService } from './integration-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 — 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 // Integration service. Built once per serve lifecycle (no\n // store dependency) so `integrations_auth` works even under a read-only store.\n const integrations = buildIntegrationService(ctx.project.root, ctx.project.config.integrations);\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 ...(integrations ? { integrations } : {}),\n });\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":["mkdirSync","join","NOIR_VERSION","buildStatus","NOIR_VERSION","buildStatus","paths","paths","createEmbedFn","resolveEmbedderConfig","resolveMemoryConfig","resolveModelConfig","resolveEmbedderConfig","createEmbedFn","resolveModelConfig","resolveMemoryConfig"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noir-ai/daemon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-beta.1",
|
|
4
4
|
"description": "Noir daemon — the runtime authority: owns the store write handle and exposes the single Noir MCP server (stdio + Streamable HTTP).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "agaaaptr",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"lifecycle"
|
|
24
24
|
],
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">=22"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"@modelcontextprotocol/server": "^2.0.0-beta.5",
|
|
47
47
|
"@modelcontextprotocol/node": "^2.0.0-beta.5",
|
|
48
48
|
"zod": "^4.2.0",
|
|
49
|
-
"@noir-ai/context": "1.
|
|
50
|
-
"@noir-ai/
|
|
51
|
-
"@noir-ai/core": "1.
|
|
52
|
-
"@noir-ai/
|
|
53
|
-
"@noir-ai/skills": "1.
|
|
54
|
-
"@noir-ai/
|
|
55
|
-
"@noir-ai/
|
|
49
|
+
"@noir-ai/context": "1.4.0-beta.1",
|
|
50
|
+
"@noir-ai/memory": "1.4.0-beta.1",
|
|
51
|
+
"@noir-ai/core": "1.4.0-beta.1",
|
|
52
|
+
"@noir-ai/model": "1.4.0-beta.1",
|
|
53
|
+
"@noir-ai/skills": "1.4.0-beta.1",
|
|
54
|
+
"@noir-ai/store": "1.4.0-beta.1",
|
|
55
|
+
"@noir-ai/workflow": "1.4.0-beta.1"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@modelcontextprotocol/client": "^2.0.0-beta.5",
|